Class: Puavo::Authentication

Inherits:
Object
  • Object
show all
Defined in:
lib/puavo/authentication.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeAuthentication

Returns a new instance of Authentication.



40
41
42
# File 'lib/puavo/authentication.rb', line 40

def initialize
  @credentials = {}
end

Instance Attribute Details

#authenticatedObject

Returns the value of attribute authenticated.



34
35
36
# File 'lib/puavo/authentication.rb', line 34

def authenticated
  @authenticated
end

#authorizedObject

Returns the value of attribute authorized.



34
35
36
# File 'lib/puavo/authentication.rb', line 34

def authorized
  @authorized
end

Class Method Details

.dn_cache_key(organisation_key, uid) ⇒ Object



36
37
38
# File 'lib/puavo/authentication.rb', line 36

def self.dn_cache_key(organisation_key, uid)
  "user_dn:#{ organisation_key }:#{ uid }"
end

.remove_connectionObject



62
63
64
65
66
# File 'lib/puavo/authentication.rb', line 62

def self.remove_connection
  ActiveLdap::Base.active_connections.keys.each do |connection_name|
    ActiveLdap::Base.remove_connection(connection_name)
  end
end

Instance Method Details

#authenticateObject

Authenticate configured connection to LDAP.

Raises AuthenticationFailed if connection could not be made. Returns possible admin permissions on successful connect



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
# File 'lib/puavo/authentication.rb', line 155

def authenticate

  # This is the first time when LDAP connection is used with the user's
  # credentials. So this search call will initialize the connection and
  # will raise ActiveLdap::AuthenticationError if user supplied a
  # bad password.
  begin

    @admin_permissions = School.search(
      :filter => "(puavoSchoolAdmin=#{ dn })",
      :scope => :one, :attributes => ["puavoId"],
      :limit => 1 )

    AccessToken.validate @credentials if oauth_access_token?

  rescue ActiveLdap::AuthenticationError
    raise AuthenticationFailed, "Bad dn or password"
  rescue AccessToken::Expired
    raise AuthenticationFailed, "OAuth Access Token expired"
  end


  @authenticated = true

end

#authorizeObject

Authorize that user has permissions to use Puavo



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/puavo/authentication.rb', line 204

def authorize

  raise AuthorizationFailed, "Cannot authorize before authenticating" unless @authenticated

  # Authorize school admins
  if not @admin_permissions.empty?
    logger.info "Authorization ok: Admin #{ dn }"
    return @authorized = true
  end

  # Authorize External Services
  if external_service?
    logger.info "Authorization ok: External Service #{ dn }"
    return @authorized = true
  end

  # Authorize OAuth Access Tokens
  if oauth_access_token?
    return @authorized = true
  end

  # Authorize organisation owners
  organisation = LdapOrganisation.first
  if organisation && organisation.owner && organisation.owner.include?(dn)
    logger.info "Authorization ok: Organisation owner #{ dn }"
    return @authorized = true
  end

  # Authorize servers
  if server?
    logger.info "Authorization ok: Server #{ dn }"
    return @authorized = true
  end

  raise AuthorizationFailed, "Unauthorized access for #{ dn }"
end

#baseObject



54
55
56
# File 'lib/puavo/authentication.rb', line 54

def base
  return current_organisation.ldap_base
end

#configure_ldap_connection(credentials) ⇒ Object



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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/puavo/authentication.rb', line 68

def configure_ldap_connection(credentials)

  @credentials = credentials

  if current_organisation.nil?
    raise Puavo::AuthenticationError, "Bad organisation"
  end

  if uid = @credentials[:uid]
    if uid.nil? || uid.empty?
      raise AuthenticationFailed, "Cannot get dn from empty or nil uid"
    end

    if uid.match(/^service\//)
      uid = uid.match(/^service\/(.*)/)[1]
      user_class = ExternalService
    else
      user_class = User
    end

    user_dn = Rails.cache.fetch self.class.dn_cache_key(organisation_key, uid) do
      # Remove previous connection
      self.class.remove_connection
      LdapBase.ldap_setup_connection( ldap_host,
                                      base.to_s,
                                      puavo_configuration["bind_dn"],
                                      puavo_configuration["password"] )
      
      user = user_class.find(:first, :attribute => "uid", :value => uid)
      
      if user
        user.dn.to_s
      else
        nil
      end
    end
    
    raise AuthenticationFailed, "Cannot get dn for UID '#{ uid }'" if not user_dn
    logger.debug "Found #{ dn } for #{ uid }"
    @credentials[:dn] = ActiveLdap::DistinguishedName.parse user_dn
  end

  # Reset attributes on new configuration
  @current_user = nil
  @authenticated = false
  @authorized = false

  # Remove previous connection
  self.class.remove_connection



  logger.info "Configuring ActiveLdap to use #{ @credentials.select{ |a,b| a != :password }.map { |k,v| "#{ k }: #{ v }" }.join ", " }"
  logger.debug "PW: #{ @credentials[:password] }" if ENV["LOG_LDAP_PASSWORD"]
  # Setup new ActiveLdap connections to use user's credentials
  LdapBase.ldap_setup_connection ldap_host, base.to_s, @credentials[:dn], @credentials[:password]

  # Do not never ever allow anonymous connections in Puavo. Should be
  # false in config/ldap.yml, but we just make sure here.
  LdapBase.connection.instance_variable_set :@allow_anonymous, false

end

#current_organisationObject



261
262
263
# File 'lib/puavo/authentication.rb', line 261

def current_organisation
  Puavo::Organisation.find organisation_key
end

#current_userObject



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/puavo/authentication.rb', line 241

def current_user

  raise "Cannot get current user before authentication" if not @authenticated

  return @current_user if @current_user


  if external_service?
    @current_user = ExternalService.find dn
  elsif oauth_access_token?
    access_token = AccessToken.find dn
    @current_user = User.find access_token.puavoOAuthEduPerson
  else
    @current_user = User.find dn
  end

  raise "Failed get User object for #{ dn }" if @current_user.nil?
  return @current_user
end

#external_service?Boolean

Returns:

  • (Boolean)


181
182
183
# File 'lib/puavo/authentication.rb', line 181

def external_service?
  dn.rdns[1]["ou"] == "System Accounts"
end

#ldap_hostObject



58
59
60
# File 'lib/puavo/authentication.rb', line 58

def ldap_host
  @credentials[:ldap_host] || puavo_configuration["host"]
end

#loggerObject



265
266
267
# File 'lib/puavo/authentication.rb', line 265

def logger
  RAILS_DEFAULT_LOGGER
end

#oauth_access_token?Boolean

Returns:

  • (Boolean)


193
194
195
# File 'lib/puavo/authentication.rb', line 193

def oauth_access_token?
  dn.rdns.first.keys.first == "puavoOAuthTokenId"
end

#oauth_client_server?Boolean

Returns:

  • (Boolean)


189
190
191
# File 'lib/puavo/authentication.rb', line 189

def oauth_client_server?
  dn.rdns.first.keys.first == "puavoOAuthClientId"
end

#puavo_configurationObject



50
51
52
# File 'lib/puavo/authentication.rb', line 50

def puavo_configuration
  ActiveLdap::Base.ensure_configuration
end

#server?Boolean

Returns:

  • (Boolean)


185
186
187
# File 'lib/puavo/authentication.rb', line 185

def server?
  dn.rdns[1]["ou"] == "Servers"
end

#test_bind(dn, password) ⇒ Object

Test dn&password bind to LDAP without actually configuring ActiveLdap to use them



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/puavo/authentication.rb', line 133

def test_bind(dn, password)
  ldap = Net::LDAP.new(
    :host => ldap_host,
    :port => 389,
    :encryption => {
      :method => :start_tls
    },
    :auth => {
      :method => :simple,
      :username => dn.to_s,
      :password => password
  })

  if not ldap.bind
    raise AuthenticationFailed, "Test bind failed: Bad dn or password"
  end
end

#user_password?Boolean

User is authenticated with real password

Returns:

  • (Boolean)


198
199
200
201
# File 'lib/puavo/authentication.rb', line 198

def user_password?
  return false if oauth_access_token?
  current_user.classes.include? "puavoEduPerson"
end