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