# Load a specific server configuration module Gitlab module Auth module LDAP class Config NET_LDAP_ENCRYPTION_METHOD = { simple_tls: :simple_tls, start_tls: :start_tls, plain: nil }.freeze attr_accessor :provider, :options InvalidProvider = Class.new(StandardError) def self.enabled? Gitlab.config.ldap.enabled end def self.servers Gitlab.config.ldap['servers']&.values || [] end def self.available_servers return [] unless enabled? _available_servers end def self._available_servers Array.wrap(servers.first) end def self.providers servers.map { |server| server['provider_name'] } end def self.valid_provider?(provider) providers.include?(provider) end def self.invalid_provider(provider) raise InvalidProvider.new("Unknown provider (#{provider}). Available providers: #{providers}") end def initialize(provider) if self.class.valid_provider?(provider) @provider = provider else self.class.invalid_provider(provider) end @options = config_for(@provider) # Use @provider, not provider end def enabled? base_config.enabled end def adapter_options opts = base_options.merge( encryption: encryption_options ) opts.merge!(auth_options) if has_auth? opts end def omniauth_options opts = base_options.merge( base: base, encryption: options['encryption'], filter: omniauth_user_filter, name_proc: name_proc, disable_verify_certificates: !options['verify_certificates'] ) if has_auth? opts.merge!( bind_dn: options['bind_dn'], password: options['password'] ) end opts[:ca_file] = options['ca_file'] if options['ca_file'].present? opts[:ssl_version] = options['ssl_version'] if options['ssl_version'].present? opts end def base @base ||= Person.normalize_dn(options['base']) end def uid options['uid'] end def label options['label'] end def sync_ssh_keys? sync_ssh_keys.present? end # The LDAP attribute in which the ssh keys are stored def sync_ssh_keys options['sync_ssh_keys'] end def user_filter options['user_filter'] end def constructed_user_filter @constructed_user_filter ||= Net::LDAP::Filter.construct(user_filter) end def group_base options['group_base'] end def admin_group options['admin_group'] end def active_directory options['active_directory'] end def block_auto_created_users options['block_auto_created_users'] end def attributes default_attributes.merge(options['attributes']) end def timeout options['timeout'].to_i end def external_groups options['external_groups'] || [] end def has_auth? options['password'] || options['bind_dn'] end def allow_username_or_email_login options['allow_username_or_email_login'] end def lowercase_usernames options['lowercase_usernames'] end def name_proc if allow_username_or_email_login proc { |name| name.gsub(/@.*\z/, '') } else proc { |name| name } end end def default_attributes { 'username' => %w(uid sAMAccountName userid), 'email' => %w(mail email userPrincipalName), 'name' => 'cn', 'first_name' => 'givenName', 'last_name' => 'sn' } end protected def base_options { host: options['host'], port: options['port'] } end def base_config Gitlab.config.ldap end def config_for(provider) base_config.servers.values.find { |server| server['provider_name'] == provider } end def encryption_options method = translate_method(options['encryption']) return nil unless method { method: method, tls_options: tls_options(method) } end def translate_method(method_from_config) NET_LDAP_ENCRYPTION_METHOD[method_from_config.to_sym] end def tls_options(method) return { verify_mode: OpenSSL::SSL::VERIFY_NONE } unless method opts = if options['verify_certificates'] OpenSSL::SSL::SSLContext::DEFAULT_PARAMS else # It is important to explicitly set verify_mode for two reasons: # 1. The behavior of OpenSSL is undefined when verify_mode is not set. # 2. The net-ldap gem implementation verifies the certificate hostname # unless verify_mode is set to VERIFY_NONE. { verify_mode: OpenSSL::SSL::VERIFY_NONE } end opts[:ca_file] = options['ca_file'] if options['ca_file'].present? opts[:ssl_version] = options['ssl_version'] if options['ssl_version'].present? opts end def auth_options { auth: { method: :simple, username: options['bind_dn'], password: options['password'] } } end def omniauth_user_filter uid_filter = Net::LDAP::Filter.eq(uid, '%{username}') if user_filter.present? Net::LDAP::Filter.join(uid_filter, constructed_user_filter).to_s else uid_filter.to_s end end end end end end