rubycas-server 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.txt +21 -0
- data/config.example.yml +14 -5
- data/lib/casserver/authenticators/base.rb +3 -0
- data/lib/casserver/authenticators/ldap.rb +62 -17
- data/lib/casserver/cas.rb +4 -4
- data/lib/casserver/controllers.rb +10 -10
- data/lib/casserver/version.rb +1 -1
- metadata +2 -2
data/CHANGELOG.txt
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
=== 0.4.2 :: 2007-07-26
|
2
|
+
|
3
|
+
* The LDAP/AD authenticator has been largely re-written. The code is a bit
|
4
|
+
cleaner now, and should work better with non-Active Directory LDAP servers
|
5
|
+
(although this has yet to be tested since I don't have access to a non-AD
|
6
|
+
LDAP server).
|
7
|
+
* The validate() method in your authenticators now receives a :service element
|
8
|
+
(in addition to :username, and :password). This is simply the service
|
9
|
+
url (if any) specified in the user's CAS request. If you call
|
10
|
+
read_standard_credentials(credentials) at the top of your validator, the value
|
11
|
+
will also be available as @service along with @username and @password.
|
12
|
+
* By request, a :username_prefix option has been added to the ldap
|
13
|
+
configuration. If entered, this string will be automatically prefixed to
|
14
|
+
the username entered by the user.
|
15
|
+
* A bug having to do with handling authenticator errors has been fixed.
|
16
|
+
Any authenticator error messages should now be correctly shown on the
|
17
|
+
login page.
|
18
|
+
* Minor improvements to error messages having to do with login tickets.
|
19
|
+
They're a bit more prescriptive now, explaining to the user what steps
|
20
|
+
they should take to correct the error.
|
21
|
+
|
1
22
|
=== 0.4.1 :: 2007-06-07
|
2
23
|
|
3
24
|
* This release restores compatiblity with older versions of rubygems
|
data/config.example.yml
CHANGED
@@ -104,15 +104,17 @@ database:
|
|
104
104
|
#
|
105
105
|
#
|
106
106
|
# ==> ActiveDirectory Authentication:
|
107
|
-
# This method authenticates against Microsoft's
|
107
|
+
# This method authenticates against Microsoft's Active Directory using LDAP.
|
108
108
|
# You must enter your ActiveDirectory server, and base DN. The port number
|
109
|
-
# and LDAP filter are optional. You must also enter a
|
109
|
+
# and LDAP filter are optional. You must also enter a CN and password
|
110
110
|
# for an "authenticator" user. The authenticator users this account to
|
111
111
|
# log in to the ActiveDirectory server and search LDAP. This does not have
|
112
|
-
# to be an administrative account
|
112
|
+
# to be an administrative account -- it only has to be able to search for other
|
113
113
|
# users.
|
114
|
-
#
|
115
|
-
#
|
114
|
+
#
|
115
|
+
# Note that the auth_user parameter must be the user's CN (Common Name)!
|
116
|
+
# In Active Directory, the CN is genarally the user's full name, which is not
|
117
|
+
# the same as their username (sAMAccountName).
|
116
118
|
#
|
117
119
|
#authenticator:
|
118
120
|
# class: CASServer::Authenticators::ActiveDirectoryLDAP
|
@@ -124,6 +126,13 @@ database:
|
|
124
126
|
# auth_user: authenticator
|
125
127
|
# auth_password: itsasecret
|
126
128
|
#
|
129
|
+
# It is possible to authenticate against Active Directory without the
|
130
|
+
# authenticator user, but this requires that users type in their CN as
|
131
|
+
# the username, rather than typing in their sAMAccountName. In other words
|
132
|
+
# users will likely have to authenticate by typing their full name,
|
133
|
+
# rather than their username. If you prefer to do this, then just
|
134
|
+
# omit the auth_user and auth_password values in the above example.
|
135
|
+
#
|
127
136
|
#
|
128
137
|
# ==> LDAP Authentication:
|
129
138
|
# This is a more general version of the ActiveDirectory authenticator.
|
@@ -2,6 +2,8 @@ module CASServer
|
|
2
2
|
module Authenticators
|
3
3
|
class Base
|
4
4
|
attr_accessor :options
|
5
|
+
attr_reader :username # make this accessible so that we can pick up any
|
6
|
+
# transformations done within the authenticator
|
5
7
|
|
6
8
|
def validate(credentials)
|
7
9
|
raise NotImplementedError, "This method must be implemented by a class extending #{self.class}"
|
@@ -16,6 +18,7 @@ module CASServer
|
|
16
18
|
def read_standard_credentials(credentials)
|
17
19
|
@username = credentials[:username]
|
18
20
|
@password = credentials[:password]
|
21
|
+
@service = credentials[:service]
|
19
22
|
end
|
20
23
|
end
|
21
24
|
end
|
@@ -18,27 +18,72 @@ class CASServer::Authenticators::LDAP < CASServer::Authenticators::Base
|
|
18
18
|
raise CASServer::AuthenticatorError, "Invalid authenticator configuration!" unless @options[:ldap]
|
19
19
|
raise CASServer::AuthenticatorError, "You must specify an ldap server in the configuration!" unless @options[:ldap][:server]
|
20
20
|
|
21
|
-
raise CASServer::AuthenticatorError, "The username '#{@username}' contains invalid characters." if (@username =~ /[*\(\)
|
21
|
+
raise CASServer::AuthenticatorError, "The username '#{@username}' contains invalid characters." if (@username =~ /[*\(\)\0\/]/)
|
22
22
|
|
23
|
-
|
24
|
-
ldap.host = @options[:ldap][:server]
|
25
|
-
ldap.port = @options[:ldap][:port] if @options[:ldap][:port]
|
23
|
+
preprocess_username
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
filter = "(#{@options[:ldap][:username_attribute] || default_username_attribute}=#{@username})"
|
33
|
-
filter += " & (#{@options[:ldap][:filter]})" if @options[:ldap][:filter]
|
34
|
-
|
35
|
-
result = ldap.bind_as(:base => @options[:ldap][:base], :filter => filter, :password => @password)
|
25
|
+
@ldap = Net::LDAP.new
|
26
|
+
@ldap.host = @options[:ldap][:server]
|
27
|
+
@ldap.port = @options[:ldap][:port] if @options[:ldap][:port]
|
36
28
|
|
37
|
-
|
29
|
+
begin
|
30
|
+
if @options[:ldap][:auth_user]
|
31
|
+
bind_with_preauthentication
|
32
|
+
else
|
33
|
+
bind_directly
|
34
|
+
end
|
35
|
+
rescue Net::LDAP::LdapError => e
|
36
|
+
raise CASServer::AuthenticatorError,
|
37
|
+
"LDAP authentication failed with '#{e}'. Check your authenticator configuration."
|
38
|
+
end
|
38
39
|
end
|
39
40
|
|
40
41
|
protected
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
def default_username_attribute
|
43
|
+
"uid"
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
def preprocess_username
|
48
|
+
# add prefix to username, if prefix was specified in the config
|
49
|
+
@username = @options[:ldap][:username_prefix] + @username if @options[:ldap][:username_prefix]
|
50
|
+
end
|
51
|
+
|
52
|
+
def bind_with_preauthentication
|
53
|
+
# If an auth_user is specified, we will connect ("pre-authenticate") to the
|
54
|
+
# LDAP server using the authenticator account, and then attempt to bind as the
|
55
|
+
# user who is actually trying to authenticate. Note that you need to set up
|
56
|
+
# the special authenticator account first. Also, auth_user must be the authenticator
|
57
|
+
# user's full CN, which is probably not the same as their username.
|
58
|
+
#
|
59
|
+
# This pre-authentication process is necessary because binding can only be done
|
60
|
+
# using the CN, so having just the username is not enough. We connect as auth_user,
|
61
|
+
# and then try to find the target user's CN based on the given username. Then we bind
|
62
|
+
# as the target user to validate their credentials.
|
63
|
+
|
64
|
+
raise CASServer::AuthenticatorError, "A password must be specified in the configuration for the authenticator user!" unless
|
65
|
+
@options[:ldap][:auth_password]
|
66
|
+
|
67
|
+
@ldap.authenticate(@options[:ldap][:auth_user], @options[:ldap][:auth_password])
|
68
|
+
|
69
|
+
username_attribute = options[:ldap][:username_attribute] || default_username_attribute
|
70
|
+
|
71
|
+
filter = Net::LDAP::Filter.construct(@options[:ldap][:filter]) &
|
72
|
+
Net::LDAP::Filter.eq(username_attribute, @username)
|
73
|
+
|
74
|
+
@ldap.bind_as(:base => @options[:ldap][:base], :password => @password, :filter => filter)
|
75
|
+
end
|
76
|
+
|
77
|
+
def bind_directly
|
78
|
+
# When no auth_user is specified, we will try to connect directly as the user
|
79
|
+
# who is trying to authenticate. Note that for this to work, the username must
|
80
|
+
# be equivalent to the user's CN, and this is often not the case (for example,
|
81
|
+
# in Active Directory, the username is the 'sAMAccountName' attribute, while the
|
82
|
+
# user's CN is generally their full name.)
|
83
|
+
|
84
|
+
cn = @username
|
85
|
+
|
86
|
+
@ldap.authenticate(cn, @password)
|
87
|
+
@ldap.bind
|
88
|
+
end
|
44
89
|
end
|
data/lib/casserver/cas.rb
CHANGED
@@ -103,20 +103,20 @@ module CASServer::CAS
|
|
103
103
|
|
104
104
|
success = false
|
105
105
|
if ticket.nil?
|
106
|
-
error = "Your login request did not include a login ticket."
|
106
|
+
error = "Your login request did not include a login ticket. There may be a problem with the authentication system."
|
107
107
|
$LOG.warn("Missing login ticket.")
|
108
108
|
elsif lt = LoginTicket.find_by_ticket(ticket)
|
109
109
|
if lt.consumed?
|
110
|
-
error = "The login ticket you provided has already been used up."
|
110
|
+
error = "The login ticket you provided has already been used up. Please try logging in again."
|
111
111
|
$LOG.warn("Login ticket '#{ticket}' previously used up")
|
112
112
|
elsif Time.now - lt.created_on < CASServer::Conf.login_ticket_expiry
|
113
113
|
$LOG.info("Login ticket '#{ticket}' successfully validated")
|
114
114
|
else
|
115
|
-
error = "Your login ticket has expired."
|
115
|
+
error = "Your login ticket has expired. Please try logging in again."
|
116
116
|
$LOG.warn("Expired login ticket '#{ticket}'")
|
117
117
|
end
|
118
118
|
else
|
119
|
-
error = "The login ticket you provided is invalid."
|
119
|
+
error = "The login ticket you provided is invalid. Please try logging in again."
|
120
120
|
$LOG.warn("Invalid login ticket '#{ticket}'")
|
121
121
|
end
|
122
122
|
|
@@ -74,18 +74,18 @@ module CASServer::Controllers
|
|
74
74
|
$LOG.debug("Logging in with username: #{@username}, lt: #{@lt}, service: #{@service}, auth: #{$AUTH}")
|
75
75
|
|
76
76
|
begin
|
77
|
-
credentials_are_valid = $AUTH.validate(:username => @username, :password => @password)
|
78
|
-
rescue AuthenticatorError => e
|
77
|
+
credentials_are_valid = $AUTH.validate(:username => @username, :password => @password, :service => @service)
|
78
|
+
rescue CASServer::AuthenticatorError => e
|
79
79
|
$LOG.error(e)
|
80
80
|
@message = {:type => 'mistake', :message => e.to_s}
|
81
|
-
render
|
81
|
+
return render(:login)
|
82
82
|
end
|
83
83
|
|
84
84
|
if credentials_are_valid
|
85
|
-
$LOG.info("Credentials for username '#{
|
85
|
+
$LOG.info("Credentials for username '#{$AUTH.username}' successfully validated")
|
86
86
|
|
87
87
|
# 3.6 (ticket-granting cookie)
|
88
|
-
tgt = generate_ticket_granting_ticket(
|
88
|
+
tgt = generate_ticket_granting_ticket($AUTH.username)
|
89
89
|
|
90
90
|
if CASServer::Conf.expire_sessions
|
91
91
|
expires = CASServer::Conf.ticket_granting_ticket_expiry.to_i.from_now
|
@@ -98,17 +98,17 @@ module CASServer::Controllers
|
|
98
98
|
# seem to be an easy way to set cookie expire times in Camping :(
|
99
99
|
@cookies[:tgt] = tgt.to_s
|
100
100
|
|
101
|
-
$LOG.debug("Ticket granting cookie '#{@cookies[:tgt]}' granted to '#{
|
101
|
+
$LOG.debug("Ticket granting cookie '#{@cookies[:tgt]}' granted to '#{$AUTH.username}'. #{expiry_info}")
|
102
102
|
|
103
103
|
if @service.blank?
|
104
|
-
$LOG.info("Successfully authenticated user '#{
|
104
|
+
$LOG.info("Successfully authenticated user '#{$AUTH.username}' at '#{tgt.client_hostname}'. No service param was given, so we will not redirect.")
|
105
105
|
@message = {:type => 'confirmation', :message => "You have successfully logged in."}
|
106
106
|
else
|
107
|
-
@st = generate_service_ticket(@service,
|
107
|
+
@st = generate_service_ticket(@service, $AUTH.username)
|
108
108
|
begin
|
109
109
|
service_with_ticket = service_uri_with_ticket(@service, @st)
|
110
110
|
|
111
|
-
$LOG.info("Redirecting authenticated user '#{
|
111
|
+
$LOG.info("Redirecting authenticated user '#{$AUTH.username}' at '#{@st.client_hostname}' to service '#{@service}'")
|
112
112
|
return redirect(service_with_ticket, :status => 303) # response code 303 means "See Other" (see Appendix B in CAS Protocol spec)
|
113
113
|
rescue URI::InvalidURIError
|
114
114
|
$LOG.error("The service '#{@service}' is not a valid URI!")
|
@@ -116,7 +116,7 @@ module CASServer::Controllers
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
else
|
119
|
-
$LOG.warn("Invalid credentials given for user '#{
|
119
|
+
$LOG.warn("Invalid credentials given for user '#{$AUTH.username}'")
|
120
120
|
@message = {:type => 'mistake', :message => "Incorrect username or password."}
|
121
121
|
end
|
122
122
|
|
data/lib/casserver/version.rb
CHANGED
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rubycas-server
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.4.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.4.2
|
7
|
+
date: 2007-07-26 00:00:00 -04:00
|
8
8
|
summary: Provides single sign on for web applications using the CAS protocol.
|
9
9
|
require_paths:
|
10
10
|
- lib
|