BigW Consortium Gitlab

20160302152808_remove_wrong_import_url_from_projects.rb 5.11 KB
Newer Older
1
# rubocop:disable all
James Lopez committed
2 3 4
# Loops through old importer projects that kept a token/password in the import URL
# and encrypts the credentials into a separate field in project#import_data
# #down method not supported
James Lopez committed
5
class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration
6

7
  class ProjectImportDataFake
8 9
    extend AttrEncrypted
    attr_accessor :credentials
10 11 12 13 14 15 16
    attr_encrypted :credentials,
                   key: Gitlab::Application.secrets.db_key_base,
                   marshal: true,
                   encode: true,
                   :mode => :per_attribute_iv_and_salt,
                   insecure_mode: true,
                   algorithm: 'aes-256-cbc'
17 18
  end

James Lopez committed
19
  def up
20 21
    say("Encrypting and migrating project import credentials...")

James Lopez committed
22
    # This should cover GitHub, GitLab, Bitbucket user:password, token@domain, and other similar URLs.
23
    in_transaction(message: "Projects including GitHub and GitLab projects with an unsecured URL.") { process_projects_with_wrong_url }
24

25
    in_transaction(message: "Migrating Bitbucket credentials...") { process_project(import_type: 'bitbucket', credentials_keys: ['bb_session']) }
26

27
    in_transaction(message: "Migrating FogBugz credentials...") { process_project(import_type: 'fogbugz', credentials_keys: ['fb_session']) }
28

29 30 31
  end

  def process_projects_with_wrong_url
32
    projects_with_wrong_import_url.each do |project|
33
      begin
34
        import_url = Gitlab::UrlSanitizer.new(project["import_url"])
35

36 37
        update_import_url(import_url, project)
        update_import_data(import_url, project)
38
      rescue Addressable::URI::InvalidURIError
39 40
        nullify_import_url(project)
      end
41 42 43
    end
  end

44
  def process_project(import_type:, credentials_keys: [])
45
    unencrypted_import_data(import_type: import_type).each do |data|
James Lopez committed
46
      replace_data_credentials(data, credentials_keys)
47 48 49
    end
  end

James Lopez committed
50
  def replace_data_credentials(data, credentials_keys)
51
    data_hash = JSON.load(data['data']) if data['data']
James Lopez committed
52 53 54 55
    unless data_hash.blank?
      encrypted_data_hash = encrypt_data(data_hash, credentials_keys)
      unencrypted_data = data_hash.empty? ? ' NULL ' :  quote(data_hash.to_json)
      update_with_encrypted_data(encrypted_data_hash, data['id'], unencrypted_data)
James Lopez committed
56 57 58
    end
  end

James Lopez committed
59
  def encrypt_data(data_hash, credentials_keys)
60
    new_data_hash = {}
James Lopez committed
61
    credentials_keys.each do |key|
62
      new_data_hash[key.to_sym] = data_hash.delete(key) if data_hash[key]
63
    end
64
    new_data_hash.deep_symbolize_keys
65 66
  end

67 68
  def in_transaction(message:)
    say_with_time(message) do
69 70 71 72 73 74
      ActiveRecord::Base.transaction do
        yield
      end
    end
  end

75
  def update_import_data(import_url, project)
76
    fake_import_data = ProjectImportDataFake.new
77
    fake_import_data.credentials = import_url.credentials
78 79 80
    import_data_id = project['import_data_id']
    if import_data_id
      execute(update_import_data_sql(import_data_id, fake_import_data))
81 82 83 84 85
    else
      execute(insert_import_data_sql(project['id'], fake_import_data))
    end
  end

James Lopez committed
86
  def update_with_encrypted_data(data_hash, import_data_id, unencrypted_data = ' NULL ')
87
    fake_import_data = ProjectImportDataFake.new
88
    fake_import_data.credentials = data_hash
James Lopez committed
89
    execute(update_import_data_sql(import_data_id, fake_import_data, unencrypted_data))
90 91 92 93 94 95
  end

  def update_import_url(import_url, project)
    execute("UPDATE projects SET import_url = #{quote(import_url.sanitized_url)} WHERE id = #{project['id']}")
  end

96 97 98 99
  def nullify_import_url(project)
    execute("UPDATE projects SET import_url = NULL WHERE id = #{project['id']}")
  end

100
  def insert_import_data_sql(project_id, fake_import_data)
101 102 103 104 105 106 107 108 109 110 111
    %(
      INSERT INTO project_import_data
                  (encrypted_credentials,
                   project_id,
                   encrypted_credentials_iv,
                   encrypted_credentials_salt)
      VALUES      ( #{quote(fake_import_data.encrypted_credentials)},
                    '#{project_id}',
                    #{quote(fake_import_data.encrypted_credentials_iv)},
                    #{quote(fake_import_data.encrypted_credentials_salt)})
    ).squish
112 113
  end

114
  def update_import_data_sql(id, fake_import_data, data = 'NULL')
115 116 117 118 119 120 121 122
    %(
      UPDATE project_import_data
      SET    encrypted_credentials = #{quote(fake_import_data.encrypted_credentials)},
             encrypted_credentials_iv = #{quote(fake_import_data.encrypted_credentials_iv)},
             encrypted_credentials_salt = #{quote(fake_import_data.encrypted_credentials_salt)},
             data = #{data}
      WHERE  id = '#{id}'
    ).squish
123 124
  end

James Lopez committed
125
  #GitHub projects with token, and any user:password@ based URL
126
  def projects_with_wrong_import_url
127
    select_all("SELECT p.id, p.import_url, i.id as import_data_id FROM projects p LEFT JOIN project_import_data i on p.id = i.project_id WHERE p.import_url <> '' AND p.import_url LIKE '%//%@%'")
128 129
  end

130 131
  # All imports with data for import_type
  def unencrypted_import_data(import_type: )
132
    select_all("SELECT i.id, p.import_url, i.data FROM projects p INNER JOIN project_import_data i ON p.id = i.project_id WHERE p.import_url <> '' AND p.import_type = '#{import_type}' ")
133 134
  end

135 136
  def quote(value)
    ActiveRecord::Base.connection.quote(value)
James Lopez committed
137 138
  end
end