diff --git a/Gemfile b/Gemfile index cea408700..a6bb3f7f0 100644 --- a/Gemfile +++ b/Gemfile @@ -56,6 +56,8 @@ gem 'rails-backbone', '~> 0.7.2' gem 'rack-throttle' gem 'rest-client', '~> 1.6.6' +gem 'attr_encrypted', '1.2.1' + group :assets do gem 'sass-rails', '~> 3.2.5' gem 'coffee-rails', '~> 3.2.2' diff --git a/Gemfile.lock b/Gemfile.lock index c4c90b960..c5579510d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -58,6 +58,8 @@ GEM ancestry (1.3.0) activerecord (>= 2.3.14) arel (3.0.2) + attr_encrypted (1.2.1) + encryptor (>= 1.1.1) bcrypt-ruby (3.0.1) blankslate (2.1.2.4) bluepill (0.0.60) @@ -102,6 +104,7 @@ GEM diff-display (0.0.1) diff-lcs (1.1.3) ejs (1.0.0) + encryptor (1.1.3) erubis (2.7.0) escape_utils (0.2.4) eventmachine (0.12.10) @@ -364,6 +367,7 @@ DEPENDENCIES RedCloth airbrake (~> 3.1.2) ancestry (~> 1.3.0) + attr_encrypted (= 1.2.1) bluepill (~> 0.0.60) cancan (= 1.6.7) cape diff --git a/app/models/key_pair.rb b/app/models/key_pair.rb index 25c5c50d5..654033f3e 100644 --- a/app/models/key_pair.rb +++ b/app/models/key_pair.rb @@ -2,34 +2,59 @@ class KeyPair < ActiveRecord::Base belongs_to :repository belongs_to :user - attr_accessor :secret + attr_accessor :fingerprint attr_accessible :public, :secret, :repository_id + attr_encrypted :secret, :key => APP_CONFIG['secret_key'] validates :repository_id, :public, :user_id, :presence => true validates :secret, :presence => true, :on => :create validates :repository_id, :uniqueness => {:message => I18n.t("activerecord.errors.key_pair.repo_key_exists")} + validate :check_keys - before_create :key_create_call - before_destroy :rm_key_call + before_create :set_key_id protected - def key_create_call - result, self.key_id = BuildServer.import_gpg_key_pair(public, secret) - raise "Failed to create key_pairs for repository #{repository_id} with code #{result}." if result == 4 - if result != 0 || self.key_id.nil? - errors.add(:public, I18n.t("activerecord.errors.key_pair.rpc_error_#{result}")) - return false - end - result = BuildServer.set_repository_key(repository.platform.name, repository.name, self.key_id) - raise "Failed to sign repository #{repository.name} in platform #{repository.platform.name} - using key_id #{self.key_id} with code #{result}." unless result.zero? + def set_key_id + self.key_id = @fingerprint end - def rm_key_call - result = BuildServer.rm_repository_key(repository.platform.name, repository.name) - raise "Failed to desroy repository key #{repository.name} in platform - #{repository.platform.name} with code #{result}." unless result.zero? + def check_keys + p_key_fingerprint = fingerprint_of_key(:public) + s_key_fingerprint = fingerprint_of_key(:secret) + if p_key_fingerprint && s_key_fingerprint + if p_key_fingerprint != s_key_fingerprint + errors.add :secret, I18n.t('activerecord.errors.key_pair.rpc_error_3') + else + @fingerprint = p_key_fingerprint + end + end end + + def fingerprint_of_key(field) + key = self.send(field) + tmp_file = "key-#{repository_id}-#{user_id}" + file = Tempfile.new tmp_file + file.write key + file.close + str = %x[ gpg --with-fingerprint #{file.path} | sed -n 1,2p] + info = str.strip.split("\n") + if info.size != 2 || (fingerprint = info[1].gsub(/.*\=/, '').strip.gsub(/\s/, ':') && fingerprint.present?) + errors.add field, I18n.t('activerecord.errors.key_pair.wrong_key') + return nil + end + prefix = field == :public ? 'pub' : 'sec' + if info[0] !~ /^#{prefix}/ + errors.add field, I18n.t("activerecord.errors.key_pair.contains_#{field}_key") + return nil + end + return fingerprint + ensure + if file + file.close + file.unlink # deletes the temp file + end + end + end diff --git a/config/locales/models/key_pair.en.yml b/config/locales/models/key_pair.en.yml index 9b685612a..4d78aa08b 100644 --- a/config/locales/models/key_pair.en.yml +++ b/config/locales/models/key_pair.en.yml @@ -21,6 +21,9 @@ en: rpc_error_1: could not import public key rpc_error_2: could not import secret key rpc_error_3: keys are imported, but it is not a key pair (ids differ) + wrong_key: wrong + contains_public_key: contains secret key + contains_secret_key: contains public key models: key_pair: Key Pair attributes: diff --git a/config/locales/models/key_pair.ru.yml b/config/locales/models/key_pair.ru.yml index 465ab0052..43df76d20 100644 --- a/config/locales/models/key_pair.ru.yml +++ b/config/locales/models/key_pair.ru.yml @@ -21,6 +21,9 @@ ru: rpc_error_1: Проблемы с импортром публичного ключа rpc_error_2: Проблемы с импортром секретного ключа rpc_error_3: Ключи импортированы, но не являются парой (идентификаторы не совпадают) + wrong_key: неправильный + contains_public_key: содержит секретный ключ + contains_secret_key: содержит публичный ключ models: key_pair: Подпись attributes: diff --git a/db/migrate/20121219122905_add_encrypted_secret_to_key_pairs.rb b/db/migrate/20121219122905_add_encrypted_secret_to_key_pairs.rb new file mode 100644 index 000000000..0e35c4726 --- /dev/null +++ b/db/migrate/20121219122905_add_encrypted_secret_to_key_pairs.rb @@ -0,0 +1,22 @@ +class AddEncryptedSecretToKeyPairs < ActiveRecord::Migration + def up + rename_table :key_pairs, :key_pairs_backup + rename_index :key_pairs_backup, 'index_key_pairs_on_repository_id', 'index_key_pairs_backup_on_repository_id' + + create_table :key_pairs do |t| + t.text :public, :null => false + t.text :encrypted_secret, :null => false + t.string :key_id, :null => false + t.references :user, :null => false + t.references :repository, :null => false + t.timestamps + end + add_index :key_pairs, :repository_id, :unique => true + end + + def down + drop_table :key_pairs + rename_table :key_pairs_backup, :key_pairs + rename_index :key_pairs, 'index_key_pairs_backup_on_repository_id', 'index_key_pairs_on_repository_id' + end +end diff --git a/db/schema.rb b/db/schema.rb index cf6ffe34e..420d673a8 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20121214145009) do +ActiveRecord::Schema.define(:version => 20121219122905) do create_table "activity_feeds", :force => true do |t| t.integer "user_id", :null => false @@ -207,6 +207,18 @@ ActiveRecord::Schema.define(:version => 20121214145009) do add_index "issues", ["project_id", "serial_id"], :name => "index_issues_on_project_id_and_serial_id", :unique => true create_table "key_pairs", :force => true do |t| + t.text "public", :null => false + t.text "encrypted_secret", :null => false + t.string "key_id", :null => false + t.integer "user_id", :null => false + t.integer "repository_id", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false + end + + add_index "key_pairs", ["repository_id"], :name => "index_key_pairs_on_repository_id", :unique => true + + create_table "key_pairs_backup", :force => true do |t| t.integer "repository_id", :null => false t.integer "user_id", :null => false t.string "key_id", :null => false @@ -215,7 +227,7 @@ ActiveRecord::Schema.define(:version => 20121214145009) do t.datetime "updated_at", :null => false end - add_index "key_pairs", ["repository_id"], :name => "index_key_pairs_on_repository_id", :unique => true + add_index "key_pairs_backup", ["repository_id"], :name => "index_key_pairs_backup_on_repository_id", :unique => true create_table "labelings", :force => true do |t| t.integer "label_id", :null => false