rubycas-server 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|