lib/casserver/authenticators/ldap.rb in rubycas-server-0.4.1 vs lib/casserver/authenticators/ldap.rb in rubycas-server-0.4.2

- old
+ new

@@ -16,29 +16,74 @@ raise CASServer::AuthenticatorError, "Cannot validate credentials because the authenticator hasn't yet been configured" unless @options raise CASServer::AuthenticatorError, "Invalid authenticator configuration!" unless @options[:ldap] raise CASServer::AuthenticatorError, "You must specify an ldap server in the configuration!" unless @options[:ldap][:server] - raise CASServer::AuthenticatorError, "The username '#{@username}' contains invalid characters." if (@username =~ /[*\(\)\\\0\/]/) + raise CASServer::AuthenticatorError, "The username '#{@username}' contains invalid characters." if (@username =~ /[*\(\)\0\/]/) - ldap = Net::LDAP.new - ldap.host = @options[:ldap][:server] - ldap.port = @options[:ldap][:port] if @options[:ldap][:port] + preprocess_username - if @options[:ldap][:auth_user] - raise CASServer::AuthenticatorError, "A password must be specified in the configuration for the authenticator user!" unless @options[:ldap][:auth_password] - ldap.authenticate(@options[:ldap][:auth_user], @options[:ldap][:auth_password]) - end + @ldap = Net::LDAP.new + @ldap.host = @options[:ldap][:server] + @ldap.port = @options[:ldap][:port] if @options[:ldap][:port] - filter = "(#{@options[:ldap][:username_attribute] || default_username_attribute}=#{@username})" - filter += " & (#{@options[:ldap][:filter]})" if @options[:ldap][:filter] - - result = ldap.bind_as(:base => @options[:ldap][:base], :filter => filter, :password => @password) - - return result + begin + if @options[:ldap][:auth_user] + bind_with_preauthentication + else + bind_directly + end + rescue Net::LDAP::LdapError => e + raise CASServer::AuthenticatorError, + "LDAP authentication failed with '#{e}'. Check your authenticator configuration." + end end protected - def default_username_attribute - "uid" - end + def default_username_attribute + "uid" + end + + private + def preprocess_username + # add prefix to username, if prefix was specified in the config + @username = @options[:ldap][:username_prefix] + @username if @options[:ldap][:username_prefix] + end + + def bind_with_preauthentication + # If an auth_user is specified, we will connect ("pre-authenticate") to the + # LDAP server using the authenticator account, and then attempt to bind as the + # user who is actually trying to authenticate. Note that you need to set up + # the special authenticator account first. Also, auth_user must be the authenticator + # user's full CN, which is probably not the same as their username. + # + # This pre-authentication process is necessary because binding can only be done + # using the CN, so having just the username is not enough. We connect as auth_user, + # and then try to find the target user's CN based on the given username. Then we bind + # as the target user to validate their credentials. + + raise CASServer::AuthenticatorError, "A password must be specified in the configuration for the authenticator user!" unless + @options[:ldap][:auth_password] + + @ldap.authenticate(@options[:ldap][:auth_user], @options[:ldap][:auth_password]) + + username_attribute = options[:ldap][:username_attribute] || default_username_attribute + + filter = Net::LDAP::Filter.construct(@options[:ldap][:filter]) & + Net::LDAP::Filter.eq(username_attribute, @username) + + @ldap.bind_as(:base => @options[:ldap][:base], :password => @password, :filter => filter) + end + + def bind_directly + # When no auth_user is specified, we will try to connect directly as the user + # who is trying to authenticate. Note that for this to work, the username must + # be equivalent to the user's CN, and this is often not the case (for example, + # in Active Directory, the username is the 'sAMAccountName' attribute, while the + # user's CN is generally their full name.) + + cn = @username + + @ldap.authenticate(cn, @password) + @ldap.bind + end end \ No newline at end of file