summaryrefslogtreecommitdiff
path: root/app/models/concerns/mirror_authentication.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/models/concerns/mirror_authentication.rb')
-rw-r--r--app/models/concerns/mirror_authentication.rb91
1 files changed, 91 insertions, 0 deletions
diff --git a/app/models/concerns/mirror_authentication.rb b/app/models/concerns/mirror_authentication.rb
new file mode 100644
index 00000000000..e3e1a0441f8
--- /dev/null
+++ b/app/models/concerns/mirror_authentication.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+# Mirroring may use password or SSH public-key authentication. This concern
+# implements support for persisting the necessary data in a `credentials`
+# serialized attribute. It also needs an `url` method to be defined
+module MirrorAuthentication
+ SSH_PRIVATE_KEY_OPTS = {
+ type: 'RSA',
+ bits: 4096
+ }.freeze
+
+ extend ActiveSupport::Concern
+
+ included do
+ validates :auth_method, inclusion: { in: %w[password ssh_public_key] }, allow_blank: true
+
+ # We should generate a key even if there's no SSH URL present
+ before_validation :generate_ssh_private_key!, if: -> {
+ regenerate_ssh_private_key || ( auth_method == 'ssh_public_key' && ssh_private_key.blank? )
+ }
+
+ credentials_field :auth_method, reader: false
+ credentials_field :ssh_known_hosts
+ credentials_field :ssh_known_hosts_verified_at
+ credentials_field :ssh_known_hosts_verified_by_id
+ credentials_field :ssh_private_key
+ credentials_field :user
+ credentials_field :password
+ end
+
+ class_methods do
+ def credentials_field(name, reader: true)
+ if reader
+ define_method(name) do
+ credentials[name] if credentials.present?
+ end
+ end
+
+ define_method("#{name}=") do |value|
+ self.credentials ||= {}
+
+ # Removal of the password, username, etc, generally causes an update of
+ # the value to the empty string. Detect and gracefully handle this case.
+ if value.present?
+ self.credentials[name] = value
+ else
+ self.credentials.delete(name)
+ end
+ end
+ end
+ end
+
+ attr_accessor :regenerate_ssh_private_key
+
+ def ssh_key_auth?
+ ssh_mirror_url? && auth_method == 'ssh_public_key'
+ end
+
+ def password_auth?
+ auth_method == 'password'
+ end
+
+ def ssh_mirror_url?
+ url&.start_with?('ssh://')
+ end
+
+ def ssh_known_hosts_verified_by
+ @ssh_known_hosts_verified_by ||= ::User.find_by(id: ssh_known_hosts_verified_by_id)
+ end
+
+ def ssh_known_hosts_fingerprints
+ ::SshHostKey.fingerprint_host_keys(ssh_known_hosts)
+ end
+
+ def auth_method
+ auth_method = credentials.fetch(:auth_method, nil) if credentials.present?
+
+ auth_method.presence || 'password'
+ end
+
+ def ssh_public_key
+ return nil if ssh_private_key.blank?
+
+ comment = "git@#{::Gitlab.config.gitlab.host}"
+ ::SSHKey.new(ssh_private_key, comment: comment).ssh_public_key
+ end
+
+ def generate_ssh_private_key!
+ self.ssh_private_key = ::SSHKey.generate(SSH_PRIVATE_KEY_OPTS).private_key
+ end
+end