Class: InclineLdap::AuthEngine

Inherits:
Incline::AuthEngineBase
  • Object
show all
Defined in:
lib/incline_ldap/auth_engine.rb

Overview

Defines an engine used to authenticate a user against an LDAP provider.

Constant Summary collapse

ConnectionError =

An error raised when attempting to establish a connection to the LDAP provider.

Class.new(StandardError)
InvalidConfiguration =

Raised when a configuration value is invalid.

Class.new(ConnectionError)
BindError =

Raised when the configuration looks good but we are still unable to connect.

Class.new(ConnectionError)

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ AuthEngine

Creates a new LDAP authentication engine.

Valid options:

host

The LDAP host name or IP address. (required)

port

The port to connect to (defaults to 389 for non-ssl and 636 for ssl).

ssl

Should SSL be used for the connection (recommended, default is true).

base_dn

The base DN to search within when looking for user accounts. (required)

browse_user

A user to log in as when looking for user accounts. (required)

browse_password

The password for the browse_user.

email_attribute

The attribute to use when looking for user accounts (default is ‘mail’).

auto_create

If true, users are automatically created when they successfully authenticate against the LDAP provider for the first time.

auto_activate

If this and auto_create are both true, then newly created users will be activated. If auto_create is false, this option has no effect. If this is false and auto_create is true, newly created users will need to activate their accounts as if they had signed up.



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/incline_ldap/auth_engine.rb', line 49

def initialize(options = {})
  @options = {
      ssl: true,
      email_attribute: 'mail'
  }.merge(options || {})

  @options[:port] = @options[:port].to_s.to_i unless @options[:port].is_a?(::Integer)

  if @options[:port] == 0
    @options[:port] = (@options[:ssl] ? 636 : 389)
  end

  raise InvalidConfiguration, "Missing value for 'host' parameter." if @options[:host].blank?
  raise InvalidConfiguration, "The value for 'port' must be between 1 and 65535." unless (1..65535).include?(@options[:port])
  raise InvalidConfiguration, "Missing value for 'base_dn' parameter." if @options[:base_dn].blank?
  raise InvalidConfiguration, "Missing value for 'email_attribute' parameter." if @options[:email_attribute].blank?
  raise InvalidConfiguration, "Missing value for 'browse_user' parameter." if @options[:browse_user].blank?

  ldap_opt = {
      host: @options[:host],
      port: @options[:port],
      base: @options[:base_dn],
      auth: {
          method: :simple,
          username: @options[:browse_user],
          password: @options[:browse_password]
      }
  }

  if @options[:ssl]
    @options[:ssl] = @options[:ssl].to_sym if @options[:ssl].is_a?(::String)

    unless [:simple_tls, :start_tls].include?(@options[:ssl])
      @options[:ssl] =
          if @options[:port] == 389
            :start_tls
          else
            :simple_tls
          end
    end

    ldap_opt[:encryption] = { method: @options[:ssl] }
  end
  
  ::Incline::Log::debug "Creating new LDAP connection to #{@options[:host]}:#{@options[:port]}..."
  @ldap = Net::LDAP.new(ldap_opt)
  
  ::Incline::Log::debug 'Binding to LDAP server...'
  raise BindError, "Failed to connect to #{@options[:host]}:#{@options[:port]}." unless @ldap.bind

  ::Incline::Log::info "Connected to LDAP host #{@options[:host]}:#{@options[:port]}."
end

Instance Method Details

#authenticate(email, password, client_ip) ⇒ Object

Authenticates a user against an LDAP provider.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/incline_ldap/auth_engine.rb', line 134

def authenticate(email, password, client_ip)
  ldap_filter = "(&(objectClass=person)(#{@options[:email_attribute]}=#{email}))"

  reset_ldap!

  search_result = @ldap.search(filter: ldap_filter)

  if search_result && search_result.count == 1
    search_result = search_result.first
    
    user = ::Incline::User.find_by(email: email)
    
    if @options[:auto_create] && user.nil?
      rpwd = ::SecureRandom.urlsafe_base64(48)
      ::Incline::Recaptcha::pause_for do
        user =
            ::Incline::User.create(
                email: email,
                password: rpwd,
                password_confirmation: rpwd,
                name: search_result[:displayName]&.first || search_result[:name]&.first || search_result[:cn]&.first,
                recaptcha: 'none',
                enabled: true,
                activated: !!@options[:auto_activate],
                activated_at: (@options[:auto_activate] ? Time.now : nil)
            )
        unless @options[:auto_activate]
          user.send_activation_email client_ip
        end
      end
    end
    
    if user
      unless user.enabled?
        add_failure_to user, '(LDAP) account disabled', client_ip
        return nil
      end
      # blank passwords are always invalid.
      entry = password.to_s.strip == '' ? nil : @ldap.bind_as(filter: ldap_filter, password: password)
      if entry && entry.count == 1
        add_success_to user, '(LDAP)', client_ip
        return user
      else
        add_failure_to user, '(LDAP) invalid password', client_ip
        return nil
      end
    end
  end
  add_failure_to email, '(LDAP) invalid email', client_ip
  nil
end

#base_dnObject

Gets the Base DN for this LDAP authenticator.



122
123
124
# File 'lib/incline_ldap/auth_engine.rb', line 122

def base_dn
  @options[:base_dn]
end

#email_attributeObject

Gets the email attribute name for this LDAP authenticator.



128
129
130
# File 'lib/incline_ldap/auth_engine.rb', line 128

def email_attribute
  @options[:email_attribute]
end

#hostObject

Gets the host name or IP address for this LDAP authenticator.



104
105
106
# File 'lib/incline_ldap/auth_engine.rb', line 104

def host
  @options[:host]
end

#portObject

Gets the host port for this LDAP authenticator.



110
111
112
# File 'lib/incline_ldap/auth_engine.rb', line 110

def port
  @options[:port]
end

#sslObject

Gets the SSL method for this LDAP authenticator.



116
117
118
# File 'lib/incline_ldap/auth_engine.rb', line 116

def ssl
  @options[:ssl]
end