lib/puavo/authentication.rb in puavo_authentication-0.1.0 vs lib/puavo/authentication.rb in puavo_authentication-0.2.0
- old
+ new
@@ -2,38 +2,92 @@
module Authentication
def self.included(base)
base.send :extend, ClassMethods
end
+
module ClassMethods
+
+ def dn_cache_key(login_uid)
+ "user_dn:#{ login_uid }"
+ end
+
+ def delete_caches(login_uid)
+ Rails.cache.delete dn_cache_key login_uid
+ end
+
+ # Authenticate user with login username and password.
+ # Returns user dn string on successful login or false on invalid login
def authenticate(login, password)
- logger.debug "Find user by uid from ldap"
- logger.debug "uid: #{login}"
- begin
+ # To authenticate an user we need to make a LDAP bind with user's dn
+ # and password. Lets look it up from cache:
+ user_dn = Rails.cache.fetch dn_cache_key(login) do
+ # On cache miss we need to use the Puavo credentials from config/ldap.yml
+ # to fetch the user object which contains the user dn.
+
+ # This find call actually initializes the LDAP connection under the
+ # hood with Puavo credentials.
user = self.find(:first, :attribute => "uid", :value => login)
- if user.bind(password)
- host = LdapBase.configuration[:host]
- base = LdapBase.base.to_s
- user.remove_connection
- LdapBase.ldap_setup_connection(host, base, user.dn, password)
+ # Remove connection made with Puavo credentials
+ self.remove_connection
- # Allow authentication always if logged in user is ExteralService object
- if user.class == ExternalService
- return user
- end
-
- # Allow authetication only if user is School Admin in the some School or organisation owner.
- if School.find( :first, :attribute => "puavoSchoolAdmin", :value => user.dn ) ||
- LdapOrganisation.first.owner.include?(user.dn)
- return user
- end
+ if user.nil?
+ return nil
end
- rescue Exception => e
- logger.info "Login failed: login: #{login}, Exception: #{e}"
+
+ user.dn
+ end
+
+ if user_dn.nil?
+ logger.info "Login failed for #{ login }: Unknown username"
return false
end
+
+ # Setup new ActiveLdap connections to use user's credentials
+ LdapBase.ldap_setup_connection(
+ LdapBase.configuration[:host],
+ LdapBase.base.to_s,
+ user_dn,
+ password)
+
+ # Do not never ever allow anonymous connections in Puavo. Should be
+ # false in config/ldap.yml, but we just make sure here.
+ self.connection.instance_variable_set :@allow_anonymous, false
+
+ # 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=#{user_dn})",
+ :scope => :one, :attributes => ["puavoId"],
+ :limit => 1 )
+ rescue ActiveLdap::AuthenticationError
+ logger.info "Login failed for #{ login } (#{ user_dn }): Bad password"
+ return false
+ end
+
+ # Allow authentication if user is a school admin in the some school.
+ if not admin_permissions.empty?
+ return user_dn
+ end
+
+ # Allow authentication if user is an organisation owner
+ organisation = LdapOrganisation.first
+ if organisation && organisation.owner.include?(user_dn)
+ return user_dn
+ end
+
+ # Allow authentication always if logged in user an external service
+ if user_dn.rdns[1]["ou"] == "System Accounts"
+ return user_dn
+ end
+
+ logger.info "Login failed for #{ login } (#{ user_dn }): Not school admin or organisation owner"
+ return false
end
end
end
end