rubycas-server 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_gem 'rubycas-server'
5
+
6
+ $RUN = true
7
+
8
+ load 'casserver.rb'
@@ -0,0 +1,197 @@
1
+ # IMPORTANT NOTE ABOUT YAML CONFIGURATION FILES
2
+ # ---> Be sure to use spaces instead of tabs for indentation. Yaml is white-space sensitive!
3
+
4
+ ##### SERVER ########################################################################
5
+
6
+ # Under what environment are you running the CAS server? The following methods
7
+ # are currently supported:
8
+ #
9
+ # webrick -- run as a stand-alone webrick server; this is the default method
10
+ # mongrel -- run as a stand-alone mongrel server; fast, but you'll need to install
11
+ # mongrel and run it behind an https reverse proxy like Pound or Apache 2.2's mod_proxy)
12
+ # cgi -- slow, but simple to set up if you're already familliar with deploying CGI scripts
13
+ # fastcgi -- see http://www.fastcgi.com (e.g. under Apache you can use this with mod_fastcgi)
14
+ #
15
+ # The cgi and fastcgi methods have not been thoroughly tested!
16
+ # Please report any problems to the authors.
17
+ #
18
+ # IMPORTANT: If you use mongrel, you will need to run the server behind a reverse proxy
19
+ # (Pound, Apache 2.2 with mod_proxy, etc.) since mongrel does not support SSL/HTTPS.
20
+ # See the RubyCAS-Server install docs for more info. Also, mongrel requries
21
+ # Camping 1.5.180 which as of writing is only available via SVN. You can install
22
+ # this by running `gem install camping --source code.whytheluckystiff.net`
23
+
24
+ ### webrick example
25
+
26
+ server: webrick
27
+ port: 443
28
+ ssl_cert: /path/to/your/ssl.pem
29
+ # ssl_key: /path/to/your/private_key.pem <-- if private key is separate from cert
30
+
31
+ ### mongrel example (you will need to run this behind an https reverse proxy,
32
+ ### since mongrel doesn't support SSL on its own)
33
+
34
+ #server: mongrel
35
+ #port: 110011
36
+
37
+ ### cgi example (you'll need to serve this via an SSL-capable server like Apache)
38
+
39
+ #server: cgi
40
+
41
+ ### fastcgi example (you'll need to serve this via an SSL-capable server like Apache)
42
+
43
+ #server: fastcgi
44
+
45
+
46
+ ##### DATABASE #######################################################################
47
+
48
+ # Set up the database connection. Make sure that this database is secure!
49
+ #
50
+ # By default, we use sqlite3 since it works without any extra configuration.
51
+ # You can also use MySQL, PostgreSQL, MSSQL, or anything else supported by ActiveRecord.
52
+ #
53
+ # For example, with MySQL, your config wold be something like:
54
+ #
55
+ #database:
56
+ # adapter: mysql
57
+ # database: casserver
58
+ # user: root
59
+ # password:
60
+ # server: localhost
61
+ #
62
+
63
+ database:
64
+ adapter: sqlite3
65
+ dbfile: /var/lib/casserver.db
66
+
67
+
68
+ ##### AUTHENTICATION #################################################################
69
+
70
+ # Configure how username/passwords are validated.
71
+ #
72
+ # !!! YOU MUST CONFIGURE ONE (AND ONLY ONE) OF THESE AUTHENTICATION METHODS !!!
73
+ #
74
+ # Currently there are three built-in methods for authentication:
75
+ # SQL, ActiveDirectory, and LDAP. If none of these work for you, it is relatively
76
+ # easy to write your own custom Authenticator class.
77
+ #
78
+ # ==> SQL Authentication:
79
+ # The simplest method is to validate against a SQL database. This assumes
80
+ # that all of your users are stored in a table that has a 'username' column
81
+ # and a 'password' column. When the user logs in, CAS conects to this database
82
+ # and look for a matching username/password in the users table. If a matching
83
+ # username and password is found, authentication is successful.
84
+ #
85
+ # Example:
86
+ #
87
+ #authenticator:
88
+ # class: CASServer::Authenticators::SQL
89
+ # database:
90
+ # adapter: mysql
91
+ # database: some_database_with_users_table
92
+ # user: root
93
+ # password:
94
+ # server: localhost
95
+ # user_table: user
96
+ # username_column: username
97
+ # password_column: password
98
+ #
99
+ #
100
+ # ==> ActiveDirectory Authentication:
101
+ # This method authenticates against Microsoft's ActiveDirectory using LDAP.
102
+ # You must enter your ActiveDirectory server, and base DN. The port number
103
+ # and LDAP filter are optional. You must also enter a username and password
104
+ # for an "authenticator" user. The authenticator users this account to
105
+ # log in to the ActiveDirectory server and search LDAP. This does not have
106
+ # to be an administrative account; it only has to be able to search for other
107
+ # users.
108
+ #
109
+ # Example:
110
+ #
111
+ #authenticator:
112
+ # class: CASServer::Authenticators::ActiveDirectoryLDAP
113
+ # ldap:
114
+ # server: ad.example.net
115
+ # port: 389
116
+ # base: dc=example,dc=net
117
+ # filter: (objectClass=person)
118
+ # auth_user: authenticator
119
+ # auth_password: itsasecret
120
+ #
121
+ #
122
+ # ==> LDAP Authentication:
123
+ # This is a more general version of the ActiveDirectory authenticator.
124
+ # The configuration is similar, except you don't need an authenticator
125
+ # username or password. Note that this authenticator hasn't been widely
126
+ # tested, so it is not guaranteed to work.
127
+ #
128
+ #authenticator:
129
+ # class: CASServer::Authenticators::ActiveDirectoryLDAP
130
+ # ldap:
131
+ # server: ad.example.net
132
+ # port: 389
133
+ # base: dc=example,dc=net
134
+ # filter: (objectClass=person)
135
+ #
136
+ #
137
+ # ==> Custom Authentication:
138
+ # It should be relatively easy to write your own Authenticator class. Have a look
139
+ # at the built-in authenticators in the casserver/authenticators directory. Your
140
+ # authenticator should extend the CASServer::Authenticators::Base class and must
141
+ # implement a validate() method that takes a single hash argument. When the user submits
142
+ # the login form, the username and password they entered is passed to validate()
143
+ # as a hash under :username and :password keys. In the future, this hash
144
+ # might also contain other data such as the domain that the user is logging in to.
145
+ #
146
+ # To use your custom authenticator, specify it's class name in the authenticator section
147
+ # of the config. You will also probably have to load the class using using a `require`
148
+ # call at the top of casserver.rb. Any other parameters you specify in the authenticator
149
+ # configuration will be passed on to the authenticator and made availabe in the validate()
150
+ # method as an @options hash.
151
+ #
152
+ # Example:
153
+ #
154
+ #authenticator:
155
+ # class: FooModule::MyCustomAuthenticator
156
+ # option_a: foo
157
+ # another_option: yeeha
158
+
159
+
160
+ ##### LOOK & FEEL ######################################################################
161
+
162
+ # Set the path to the theme directory that determines how your CAS pages look.
163
+ #
164
+ # Custom themes are not well supported yet, but will be in the near future. In the
165
+ # meantime, if you want to create a custom theme, you can create a subdirectory
166
+ # under the CASServer's themes dir (for example '/usr/lib/ruby/1.8/gems/casserver-xxx/lib/themes',
167
+ # if you installed CASServer on Linux as a gem). A theme is basically just a theme.css
168
+ # file that overrides the themes/cas.css styles along with a collection of image files
169
+ # like logo.png and bg.png.
170
+ #
171
+ # By default, we use the 'simple' theme which you can find in themes/simple.
172
+ theme: simple
173
+
174
+ # The name of your company/organization. This will show up on the login page.
175
+ organization: CAS
176
+
177
+ # A short bit of text that shows up on the login page. You can make this blank if you prefer.
178
+ infoline: Powered by <a href="http://code.google.com/p/rubycas-server/">RubyCAS-Server</a>
179
+
180
+
181
+ ##### LOGGING #########################################################################
182
+
183
+ # Configure general logging. This log is where you'll want to look in case of problems.
184
+ #
185
+ # You may want to change the file to something like /var/log/casserver.log
186
+ # Set the level to DEBUG if you want more detailed logging.
187
+
188
+ log:
189
+ file: /var/log/casserver.log
190
+ level: INFO
191
+
192
+
193
+ # If you want full database logging, uncomment this next section.
194
+ # Every SQL query will be logged here. This is useful for debugging database problems.
195
+ #
196
+ #db_log:
197
+ # file: /var/log/casserver_db.log
data/lib/casserver.rb ADDED
@@ -0,0 +1,152 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # change to current directory when invoked on its own
4
+ Dir.chdir(File.dirname(File.expand_path(__FILE__))) if __FILE__ == $0
5
+
6
+ # add current directory to load path
7
+ $: << File.dirname(File.expand_path(__FILE__))
8
+
9
+ require 'rubygems'
10
+ require_gem 'camping', '~> 1.5'
11
+ require 'camping'
12
+
13
+ require 'active_support'
14
+ require 'yaml'
15
+
16
+ # enable xhtml source code indentation for debugging views
17
+ Markaby::Builder.set(:indent, 2)
18
+
19
+ # seed the random number generator (ruby does this by default, but it doesn't hurt to do it here just to be sure)
20
+ srand
21
+
22
+ # Camping.goes must be called after the authenticator class is loaded, otherwise weird things happen
23
+ Camping.goes :CASServer
24
+
25
+ module CASServer
26
+ end
27
+
28
+ require 'casserver/utils'
29
+ require 'casserver/models'
30
+ require 'casserver/cas'
31
+ require 'casserver/conf'
32
+ require 'casserver/views'
33
+ require 'casserver/controllers'
34
+
35
+ # init the logger
36
+ $LOG = CASServer::Utils::Logger.new(CASServer::Conf.log[:file])
37
+ $LOG.level = "CASServer::Utils::Logger::#{CASServer::Conf.log[:level]}".constantize
38
+
39
+ # do initialization stuff
40
+ def CASServer.create
41
+ CASServer::Models.create_schema
42
+
43
+ $LOG.info("RubyCAS-Server initialized.")
44
+
45
+ $LOG.debug("Configuration is:\n#{$CONF.to_yaml}")
46
+ $LOG.debug("Authenticator is: #{$AUTH}")
47
+
48
+ CASServer::Models::ServiceTicket.cleanup_expired(CASServer::Conf.service_ticket_expiry)
49
+ CASServer::Models::LoginTicket.cleanup_expired(CASServer::Conf.login_ticket_expiry)
50
+ CASServer::Models::ProxyGrantingTicket.cleanup_expired(CASServer::Conf.proxy_granting_ticket_expiry)
51
+ CASServer::Models::TicketGrantingTicket.cleanup_expired(CASServer::Conf.ticket_granting_ticket_expiry)
52
+ end
53
+
54
+
55
+ # this gets run if we launch directly (i.e. `ruby casserver.rb` rather than `camping casserver`)
56
+ if __FILE__ == $0 || $RUN
57
+ CASServer::Models::Base.establish_connection(CASServer::Conf.database)
58
+ if CASServer::Conf.db_log
59
+ CASServer::Models::Base.logger = Logger.new(CASServer::Conf.db_log[:file] || 'casserver_db.log')
60
+ CASServer::Models::Base.logger.level = "CASServer::Utils::Logger::#{CASServer::Conf.db_log[:level] || 'DEBUG'}".constantize
61
+ end
62
+
63
+ case CASServer::Conf.server
64
+ when "webrick", :webrick
65
+ require 'webrick/httpserver'
66
+ require 'webrick/https'
67
+ require 'camping/webrick'
68
+
69
+ # TODO: verify the certificate's validity
70
+ # example of how to do this is here: http://pablotron.org/download/ruri-20050331.rb
71
+
72
+ cert_path = CASServer::Conf.ssl_cert
73
+ key_path = CASServer::Conf.ssl_key || CASServer::Conf.ssl_cert
74
+ # look for the key in the ssl_cert if no ssl_key is specified
75
+
76
+ raise "'#{cert_path}' is not a valid ssl certificate. Your 'ssl_cert' configuration" +
77
+ " setting must be a path to a valid ssl certificate file." unless
78
+ File.exists? cert_path
79
+
80
+ raise "'#{key_path}' is not a valid ssl private key. Your 'ssl_key' configuration" +
81
+ " setting must be a path to a valid ssl private key file." unless
82
+ File.exists? key_path
83
+
84
+ cert = OpenSSL::X509::Certificate.new(File.read(cert_path))
85
+ key = OpenSSL::PKey::RSA.new(File.read(key_path))
86
+
87
+ begin
88
+ s = WEBrick::HTTPServer.new(
89
+ :BindAddress => "0.0.0.0",
90
+ :Port => CASServer::Conf.port,
91
+ :SSLEnable => true,
92
+ :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE,
93
+ :SSLCertificate => cert,
94
+ :SSLPrivateKey => key
95
+ )
96
+ rescue Errno::EACCES
97
+ puts "\nThe server could not launch. Are you running on a privileged port? (e.g. port 443) If so, you must run the server as root."
98
+ exit 2
99
+ end
100
+
101
+ CASServer.create
102
+ s.mount "#{CASServer::Conf.uri_path}", WEBrick::CampingHandler, CASServer
103
+
104
+ puts "\n** CASServer is running at http://localhost:#{CASServer::Conf.port}#{CASServer::Conf.uri_path} and logging to '#{CASServer::Conf.log[:file]}'\n\n"
105
+
106
+ # This lets Ctrl+C shut down your server
107
+ trap(:INT) do
108
+ s.shutdown
109
+ end
110
+
111
+ s.start
112
+
113
+ when "mongrel", :mongrel
114
+ require 'rubygems'
115
+ require 'mongrel/camping'
116
+
117
+ # camping has fixes for mongrel currently only availabe in SVN
118
+ # ... you can install camping from svn (1.5.180) by running:
119
+ # gem install camping --source code.whytheluckystiff.net
120
+ require_gem 'camping', '~> 1.5.180'
121
+
122
+ CASServer.create
123
+
124
+ begin
125
+ server = Mongrel::Camping::start("0.0.0.0",CASServer::Conf.port,"#{CASServer::Conf.uri_path}",CASServer)
126
+ rescue Errno::EACCES
127
+ puts "\nThe server could not launch. Are you running on a privileged port? (e.g. port 443) If so, you must run the server as root."
128
+ exit 2
129
+ end
130
+
131
+ puts "\n** CASServer is running at http://localhost:#{CASServer::Conf.port}#{CASServer::Conf.uri_path} and logging to '#{CASServer::Conf.log[:file]}'"
132
+ server.run.join
133
+
134
+ when "fastcgi", :fastcgi
135
+ require 'camping/fastcgi'
136
+ Dir.chdir('/srv/www/camping/casserver/')
137
+
138
+ CASServer.create
139
+ Camping::FastCGI.start(CASServer)
140
+
141
+ when "cgi", :cgi
142
+ CASServer.create
143
+ puts CASServer.run
144
+
145
+ else
146
+ if CASServer::Conf.server
147
+ raise "The server setting '#{CASServer::Conf.server}' in your config.yml file is invalid."
148
+ else
149
+ raise "You must have a 'server' setting in your config.yml file. Please see the RubyCAS-Server documentation."
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,8 @@
1
+ require 'casserver/authenticators/ldap'
2
+
3
+ class CASServer::Authenticators::ActiveDirectoryLDAP < CASServer::Authenticators::LDAP
4
+ protected
5
+ def default_username_attribute
6
+ "sAMAccountName"
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ module CASServer
2
+ module Authenticators
3
+ class Base
4
+ attr_accessor :options
5
+
6
+ def validate(credentials)
7
+ raise NotImplementedError, "This method must be implemented by a class extending #{self.class}"
8
+ end
9
+
10
+ def configure(options)
11
+ raise ArgumentError, "options must be a HashWithIndifferentAccess" unless options.kind_of? HashWithIndifferentAccess
12
+ @options = options.dup
13
+ end
14
+
15
+ protected
16
+ def read_standard_credentials(credentials)
17
+ @username = credentials[:username]
18
+ @password = credentials[:password]
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ require 'casserver/authenticators/base'
2
+
3
+ begin
4
+ require 'net/ldap'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require_gem 'ruby-net-ldap', '~> 0.0.4'
8
+ require 'net/ldap'
9
+ end
10
+
11
+ class CASServer::Authenticators::LDAP < CASServer::Authenticators::Base
12
+ def validate(credentials)
13
+ read_standard_credentials(credentials)
14
+
15
+ raise "Cannot validate credentials because the authenticator hasn't yet been configured" unless @options
16
+ raise "Invalid authenticator configuration!" unless @options[:ldap]
17
+ raise "You must specify an ldap server in the configuration!" unless @options[:ldap][:server]
18
+
19
+ ldap = Net::LDAP.new
20
+ ldap.host = @options[:ldap][:server]
21
+ ldap.port = @options[:ldap][:port] if @options[:ldap][:port]
22
+
23
+ if @options[:ldap][:auth_user]
24
+ raise "A password must be specified in the configuration for the authenticator user!" unless @options[:ldap][:auth_password]
25
+ ldap.authenticate(@options[:ldap][:auth_user], @options[:ldap][:auth_password])
26
+ end
27
+
28
+ filter = "(#{@options[:ldap][:username_attribute] || default_username_attribute}=#{@username})"
29
+ filter += " & (#{@options[:ldap][:filter]})" if @options[:ldap][:filter]
30
+
31
+ result = ldap.bind_as(:base => @options[:ldap][:base], :filter => filter, :password => @password)
32
+
33
+ return result
34
+ end
35
+
36
+ protected
37
+ def default_username_attribute
38
+ "uid"
39
+ end
40
+ end
@@ -0,0 +1,37 @@
1
+ require 'casserver/authenticators/base'
2
+
3
+ begin
4
+ require 'active_record'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'active_record'
8
+ end
9
+
10
+ class CASServer::Authenticators::SQL < CASServer::Authenticators::Base
11
+
12
+ def validate(credentials)
13
+ read_standard_credentials(credentials)
14
+
15
+ raise "Cannot validate credentials because the authenticator hasn't yet been configured" unless @options
16
+ raise "Invalid authenticator configuration!" unless @options[:database]
17
+
18
+ CASUser.establish_connection @options[:database]
19
+ CASUser.set_table_name @options[:user_table] || "users"
20
+
21
+ username_column = @options[:username_column] || 'username'
22
+ password_column = @options[:password_column] || 'password'
23
+
24
+ results = CASUser.find(:all, :conditions => ["#{username_column} = ? AND #{password_column} = ?", @username, @password])
25
+
26
+ if results.size > 0
27
+ $LOG.warn("Multiple matches found for user '#{@username}'") if results.size > 1
28
+ return true
29
+ else
30
+ return false
31
+ end
32
+ end
33
+
34
+ class CASUser < ActiveRecord::Base
35
+ end
36
+
37
+ end