31 |
--------------------------------------------------------------------------------
/lib/casserver/views/layout.erb:
--------------------------------------------------------------------------------
1 | <%# coding: UTF-8 -%>
2 |
3 |
5 |
7 |
8 |
9 | <%= escape_html @organization %><%= _(" Central Login") %>
10 |
11 |
12 |
13 |
14 |
15 |
16 | <%= yield %>
17 |
18 |
19 |
--------------------------------------------------------------------------------
/lib/casserver/views/proxy_validate.builder:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | if @success
3 | xml.tag!("cas:serviceResponse", 'xmlns:cas' => "http://www.yale.edu/tp/cas") do
4 | xml.tag!("cas:authenticationSuccess") do
5 | xml.tag!("cas:user", @username.to_s)
6 | @extra_attributes.each do |key, value|
7 | serialize_extra_attribute(xml, key, value)
8 | end
9 | if @pgtiou
10 | xml.tag!("cas:proxyGrantingTicket", @pgtiou.to_s)
11 | end
12 | if @proxies && !@proxies.empty?
13 | xml.tag!("cas:proxies") do
14 | @proxies.each do |proxy_url|
15 | xml.tag!("cas:proxy", proxy_url.to_s)
16 | end
17 | end
18 | end
19 | end
20 | end
21 | else
22 | xml.tag!("cas:serviceResponse", 'xmlns:cas' => "http://www.yale.edu/tp/cas") do
23 | xml.tag!("cas:authenticationFailure", {:code => @error.code}, @error.to_s)
24 | end
25 | end
--------------------------------------------------------------------------------
/lib/casserver/authenticators/test.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require 'casserver/authenticators/base'
3 |
4 | # Dummy authenticator used for testing.
5 | # Accepts any username as valid as long as the password is "testpassword"; otherwise authentication fails.
6 | # Raises an AuthenticationError when username is "do_error" (this is useful to test the Exception
7 | # handling functionality).
8 | class CASServer::Authenticators::Test < CASServer::Authenticators::Base
9 | def validate(credentials)
10 | read_standard_credentials(credentials)
11 |
12 | raise CASServer::AuthenticatorError, "Username is 'do_error'!" if @username == 'do_error'
13 |
14 | @extra_attributes[:test_utf_string] = "Ютф"
15 | @extra_attributes[:test_numeric] = 123.45
16 | @extra_attributes[:test_serialized] = {:foo => 'bar', :alpha => [1,2,3]}
17 |
18 | valid_password = options[:password] || "testpassword"
19 |
20 | return @password == valid_password
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/lib/casserver/utils.rb:
--------------------------------------------------------------------------------
1 | require 'crypt-isaac'
2 |
3 | # Misc utility function used throughout by the RubyCAS-Server.
4 | module CASServer
5 | module Utils
6 | def random_string(max_length = 29)
7 | rg = Crypt::ISAAC.new
8 | max = 4294619050
9 | r = "#{Time.now.to_i}r%X%X%X%X%X%X%X%X" %
10 | [rg.rand(max), rg.rand(max), rg.rand(max), rg.rand(max),
11 | rg.rand(max), rg.rand(max), rg.rand(max), rg.rand(max)]
12 | r[0..max_length-1]
13 | end
14 | module_function :random_string
15 |
16 | def log_controller_action(controller, params)
17 | $LOG << "\n"
18 |
19 | /`(.*)'/.match(caller[1])
20 | method = $~[1]
21 |
22 | if params.respond_to? :dup
23 | params2 = params.dup
24 | params2['password'] = '******' if params2['password']
25 | else
26 | params2 = params
27 | end
28 | $LOG.debug("Processing #{controller}::#{method} #{params2.inspect}")
29 | end
30 | module_function :log_controller_action
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/spec/alt_config.yml:
--------------------------------------------------------------------------------
1 | server: webrick
2 | port: 6543
3 | #ssl_cert: test.pem
4 | uri_path: /test
5 | #bind_address: 0.0.0.0
6 |
7 | # database:
8 | # adapter: mysql
9 | # database: casserver
10 | # username: root
11 | # password:
12 | # host: localhost
13 | # reconnect: true
14 | database:
15 | adapter: sqlite3
16 | database: spec/casserver_spec.db
17 |
18 | disable_auto_migrations: true
19 |
20 | quiet: true
21 |
22 | authenticator:
23 | class: CASServer::Authenticators::Test
24 | password: spec_password
25 |
26 | theme: simple
27 |
28 | organization: "RSPEC-TEST"
29 |
30 | infoline: "This is an rspec test."
31 |
32 | #custom_views: /path/to/custom/views
33 |
34 | default_locale: en
35 |
36 | log:
37 | file: casserver_spec.log
38 | level: DEBUG
39 |
40 | #db_log:
41 | # file: casserver_spec_db.log
42 |
43 | enable_single_sign_out: true
44 |
45 | #maximum_unused_login_ticket_lifetime: 300
46 | #maximum_unused_service_ticket_lifetime: 300
47 |
48 | #maximum_session_lifetime: 172800
49 |
50 | #downcase_username: true
51 |
--------------------------------------------------------------------------------
/spec/default_config.yml:
--------------------------------------------------------------------------------
1 | server: webrick
2 | port: 6543
3 | #ssl_cert: test.pem
4 | #uri_path: /cas
5 | #bind_address: 0.0.0.0
6 |
7 | # database:
8 | # adapter: mysql
9 | # database: casserver
10 | # username: root
11 | # password:
12 | # host: localhost
13 | # reconnect: true
14 | database:
15 | adapter: sqlite3
16 | database: spec/casserver_spec.db
17 |
18 | disable_auto_migrations: true
19 |
20 | quiet: true
21 |
22 | authenticator:
23 | class: CASServer::Authenticators::Test
24 | password: spec_password
25 |
26 | theme: simple
27 |
28 | organization: "RSPEC-TEST"
29 |
30 | infoline: "This is an rspec test."
31 |
32 | #custom_views: /path/to/custom/views
33 |
34 | default_locale: en
35 |
36 | log:
37 | file: casserver_spec.log
38 | level: DEBUG
39 |
40 | #db_log:
41 | # file: casserver_spec_db.log
42 |
43 | enable_single_sign_out: true
44 |
45 | #maximum_unused_login_ticket_lifetime: 300
46 | #maximum_unused_service_ticket_lifetime: 300
47 |
48 | #maximum_session_lifetime: 172800
49 |
50 | #downcase_username: true
51 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/authlogic_crypto_providers/md5.rb:
--------------------------------------------------------------------------------
1 | require "digest/md5"
2 |
3 | module Authlogic
4 | module CryptoProviders
5 | # This class was made for the users transitioning from md5 based systems.
6 | # I highly discourage using this crypto provider as it superbly inferior
7 | # to your other options.
8 | #
9 | # Please use any other provider offered by Authlogic.
10 | class MD5
11 | class << self
12 | attr_accessor :join_token
13 |
14 | # The number of times to loop through the encryption.
15 | def stretches
16 | @stretches ||= 1
17 | end
18 | attr_writer :stretches
19 |
20 | # Turns your raw password into a MD5 hash.
21 | def encrypt(*tokens)
22 | digest = tokens.flatten.join(join_token)
23 | stretches.times { digest = Digest::MD5.hexdigest(digest) }
24 | digest
25 | end
26 |
27 | # Does the crypted password match the tokens? Uses the same tokens that were used to encrypt.
28 | def matches?(crypted, *tokens)
29 | encrypt(*tokens) == crypted
30 | end
31 | end
32 | end
33 | end
34 | end
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Portions of RubyCAS-Server contributed by Matt Zukowski are copyright (c) 2009 Urbacon Ltd.
2 | Other portions are copyright of their respective authors.
3 |
4 | The MIT License
5 |
6 | Permission is hereby granted, free of charge, to any person
7 | obtaining a copy of this software and associated documentation
8 | files (the "Software"), to deal in the Software without
9 | restriction, including without limitation the rights to use,
10 | copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the
12 | Software is furnished to do so, subject to the following
13 | conditions:
14 |
15 | The above copyright notice and this permission notice shall be
16 | included in all copies or substantial portions of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 | OTHER DEALINGS IN THE SOFTWARE.
26 |
27 |
--------------------------------------------------------------------------------
/spec/model_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require File.dirname(__FILE__) + '/spec_helper'
3 |
4 | module CASServer
5 | end
6 | require 'casserver/model'
7 |
8 | describe CASServer::Model::LoginTicket, '.cleanup(max_lifetime, max_unconsumed_lifetime)' do
9 | let(:max_lifetime) { -1 }
10 | let(:max_unconsumed_lifetime) { -2 }
11 |
12 | before do
13 | load_server(File.dirname(__FILE__) + "/default_config.yml")
14 | reset_spec_database
15 |
16 | CASServer::Model::LoginTicket.create :ticket => 'test', :client_hostname => 'test.local'
17 | end
18 |
19 | it 'should destroy all tickets created before the max lifetime' do
20 | expect {
21 | CASServer::Model::LoginTicket.cleanup(max_lifetime, max_unconsumed_lifetime)
22 | }.to change(CASServer::Model::LoginTicket, :count).by(-1)
23 | end
24 |
25 | it 'should destroy all unconsumed tickets not exceeding the max lifetime' do
26 | expect {
27 | CASServer::Model::LoginTicket.cleanup(max_lifetime, max_unconsumed_lifetime)
28 | }.to change(CASServer::Model::LoginTicket, :count).by(-1)
29 | end
30 | end
31 |
32 | describe CASServer::Model::LoginTicket, '#to_s' do
33 | let(:ticket) { 'test' }
34 |
35 | before do
36 | @login_ticket = CASServer::Model::LoginTicket.new :ticket => ticket
37 | end
38 |
39 | it 'should delegate #to_s to #ticket' do
40 | @login_ticket.to_s.should == ticket
41 | end
42 | end
43 |
--------------------------------------------------------------------------------
/resources/init.d.sh:
--------------------------------------------------------------------------------
1 | #! /bin/sh
2 | #
3 | # Copyright (c) 2008 Urbacon Ltd.
4 | #
5 | # System startup script for the RubyCAS-Server
6 | #
7 | # Instructions:
8 | # 1. Rename this file to 'rubycas-server'
9 | # 2. Copy it to your '/etc/init.d' directory
10 | # 3. chmod +x /etc/init.d/rubycas-server
11 | #
12 | # chkconfig - 85 15
13 | # description: Provides single-sign-on authentication for web applications.
14 | #
15 | ### BEGIN INIT INFO
16 | # Provides: rubycas-server
17 | # Required-Start: $syslog
18 | # Should-Start:
19 | # Required-Stop: $syslog
20 | # Should-Stop:
21 | # Default-Start: 3 5
22 | # Default-Stop: 0 1 2 6
23 | # Description: Start the RubyCAS-Server
24 | ### END INIT INFO
25 |
26 | CASSERVER_CTL=rubycas-server-ctl
27 |
28 | # Gracefully exit if the controller is missing.
29 | which $CASSERVER_CTL > /dev/null || exit 0
30 |
31 | # Source config
32 | . /etc/rc.status
33 |
34 | rc_reset
35 | case "$1" in
36 | start)
37 | $CASSERVER_CTL start
38 | rc_status -v
39 | ;;
40 | stop)
41 | $CASSERVER_CTL stop
42 | rc_status -v
43 | ;;
44 | restart)
45 | $0 stop
46 | $0 start
47 | rc_status
48 | ;;
49 | status)
50 | $CASSERVER_CTL status
51 | rc_status -v
52 | ;;
53 | *)
54 | echo "Usage: $0 {start|stop|status|restart}"
55 | exit 1
56 | ;;
57 | esac
58 | rc_exit
59 |
--------------------------------------------------------------------------------
/lib/casserver/views/_login_form.erb:
--------------------------------------------------------------------------------
1 | <%# coding: UTF-8 -%>
2 |
43 |
--------------------------------------------------------------------------------
/spec/authenticators/ldap_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require File.dirname(__FILE__) + '/../spec_helper'
3 |
4 | require 'casserver/authenticators/ldap'
5 |
6 | describe CASServer::Authenticators::LDAP do
7 | before do
8 | @ldap_entry = mock(Net::LDAP::Entry.new)
9 | @ldap_entry.stub!(:[]).and_return("Test")
10 |
11 | @ldap = mock(Net::LDAP)
12 | @ldap.stub!(:host=)
13 | @ldap.stub!(:port=)
14 | @ldap.stub!(:encryption)
15 | @ldap.stub!(:bind_as).and_return(true)
16 | @ldap.stub!(:authenticate).and_return(true)
17 | @ldap.stub!(:search).and_return([@ldap_entry])
18 |
19 | Net::LDAP.stub!(:new).and_return(@ldap)
20 | end
21 |
22 | describe '#validate' do
23 |
24 | it 'validate with preauthentication and with extra attributes' do
25 | auth = CASServer::Authenticators::LDAP.new
26 |
27 | auth_config = HashWithIndifferentAccess.new(
28 | :ldap => {
29 | :host => "ad.example.net",
30 | :port => 389,
31 | :base => "dc=example,dc=net",
32 | :filter => "(objectClass=person)",
33 | :auth_user => "authenticator",
34 | :auth_password => "itsasecret"
35 | },
36 | :extra_attributes => [:full_name, :address]
37 | )
38 |
39 | auth.configure(auth_config.merge('auth_index' => 0))
40 | auth.validate(
41 | :username => 'validusername',
42 | :password => 'validpassword',
43 | :service => 'test.service',
44 | :request => {}
45 | ).should == true
46 |
47 | auth.extra_attributes.should == {:full_name => 'Test', :address => 'Test'}
48 | end
49 |
50 | end
51 | end
52 |
53 |
54 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/client_certificate.rb:
--------------------------------------------------------------------------------
1 | require 'casserver/authenticators/base'
2 |
3 | # NOT YET IMPLEMENTED
4 | #
5 | # This authenticator will authenticate the user based on a client SSL certificate.
6 | #
7 | # You will probably want to use this along with another authenticator, chaining
8 | # it so that if the client does not provide a certificate, the server can
9 | # fall back to some other authentication mechanism.
10 | #
11 | # Here's an example of how to use two chained authenticators in the config.yml
12 | # file. The server will first use the ClientCertificate authenticator, and
13 | # only fall back to the SQL authenticator of the first one fails:
14 | #
15 | # authenticator:
16 | # -
17 | # class: CASServer::Authenticators::ClientCertificate
18 | # -
19 | # class: CASServer::Authenticators::SQL
20 | # database:
21 | # adapter: mysql
22 | # database: some_database_with_users_table
23 | # user: root
24 | # password:
25 | # server: localhost
26 | # user_table: user
27 | # username_column: username
28 | # password_column: password
29 | #
30 | class CASServer::Authenticators::ClientCertificate < CASServer::Authenticators::Base
31 | def validate(credentials)
32 | read_standard_credentials(credentials)
33 |
34 | @client_cert = credentials[:request]['SSL_CLIENT_CERT']
35 |
36 | # note that I haven't actually tested to see if SSL_CLIENT_CERT gets
37 | # filled with data when a client cert is provided, but this should be
38 | # the case at least in theory :)
39 |
40 | return false if @client_cert.blank?
41 |
42 | # IMPLEMENT SSL CERTIFICATE VALIDATION CODE HERE
43 | raise NotImplementedError, "#{self.class.name}#validate NOT YET IMPLEMENTED!"
44 |
45 | return true # if SSL certificate is valid, false otherwise
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/spec/utils_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require File.dirname(__FILE__) + '/spec_helper'
3 |
4 | module CASServer
5 | end
6 | require 'casserver/utils'
7 |
8 | describe CASServer::Utils, '#random_string(max_length = 29)' do
9 | before do
10 | load_server(File.dirname(__FILE__) + "/default_config.yml")
11 | reset_spec_database
12 | end
13 |
14 | context 'when max length is not passed in' do
15 | it 'should return a random string of length 29' do
16 | subject.random_string.length.should == 29
17 | end
18 | end
19 |
20 | context 'when max length is passed in' do
21 | it 'should return a random string of the desired length' do
22 | subject.random_string(30).length.should == 30
23 | end
24 | end
25 |
26 | it 'should include the letter r in the random string' do
27 | subject.random_string.should include 'r'
28 | end
29 |
30 | it 'should return a random string' do
31 | random_string = subject.random_string
32 | another_random_string = subject.random_string
33 | random_string.should_not == another_random_string
34 | end
35 | end
36 |
37 | describe CASServer::Utils, '#log_controller_action(controller, params)' do
38 | let(:params) { {} }
39 | let(:params_with_password) { { 'password' => 'test' } }
40 | let(:params_with_password_filtered) { { 'password' => '******' } }
41 |
42 | it 'should log the controller action' do
43 | $LOG.should_receive(:debug).with 'Processing application::instance_eval {}'
44 |
45 | subject.log_controller_action('application', params)
46 | end
47 |
48 | it 'should filter password parameters in the log' do
49 | $LOG.should_receive(:debug).with "Processing application::instance_eval #{params_with_password_filtered.inspect}"
50 |
51 | subject.log_controller_action('application', params_with_password)
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/authlogic_crypto_providers/sha1.rb:
--------------------------------------------------------------------------------
1 | require "digest/sha1"
2 |
3 | module Authlogic
4 | module CryptoProviders
5 | # This class was made for the users transitioning from restful_authentication.
6 | # I highly discourage using this crypto provider as it inferior to your other options.
7 | # Please use any other provider offered by Authlogic.
8 | class Sha1
9 | class << self
10 | def join_token
11 | @join_token ||= "--"
12 | end
13 | attr_writer :join_token
14 |
15 | def digest_format=(format)
16 | @digest_format = format
17 | end
18 |
19 | # This is for "old style" authentication with a custom format of digest
20 | def digest(tokens)
21 | if @digest_format
22 | @digest_format.
23 | gsub('PASSWORD', tokens.first).
24 | gsub('SALT', tokens.last)
25 | else
26 | tokens.join(join_token)
27 | end
28 | end
29 |
30 | # The number of times to loop through the encryption.
31 | # This is ten because that is what restful_authentication defaults to.
32 |
33 | def stretches
34 | @stretches ||= 10
35 | end
36 | attr_writer :stretches
37 |
38 | # Turns your raw password into a Sha1 hash.
39 | def encrypt(*tokens)
40 | tokens = tokens.flatten
41 |
42 | if stretches > 1
43 | hash = tokens.shift
44 | stretches.times { hash = Digest::SHA1.hexdigest([hash, *tokens].join(join_token)) }
45 | else
46 | hash = Digest::SHA1.hexdigest( digest(tokens) )
47 | end
48 |
49 | hash
50 | end
51 |
52 | # Does the crypted password match the tokens? Uses the same tokens that were used to encrypt.
53 | def matches?(crypted, *tokens)
54 | encrypt(*tokens) == crypted
55 | end
56 | end
57 | end
58 | end
59 | end
60 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/authlogic_crypto_providers/aes256.rb:
--------------------------------------------------------------------------------
1 | require "openssl"
2 |
3 | module Authlogic
4 | module CryptoProviders
5 | # This encryption method is reversible if you have the supplied key. So in order to use this encryption method you must supply it with a key first.
6 | # In an initializer, or before your application initializes, you should do the following:
7 | #
8 | # Authlogic::CryptoProviders::AES256.key = "my really long and unique key, preferrably a bunch of random characters"
9 | #
10 | # My final comment is that this is a strong encryption method, but its main weakness is that its reversible. If you do not need to reverse the hash
11 | # then you should consider Sha512 or BCrypt instead.
12 | #
13 | # Keep your key in a safe place, some even say the key should be stored on a separate server.
14 | # This won't hurt performance because the only time it will try and access the key on the separate server is during initialization, which only
15 | # happens once. The reasoning behind this is if someone does compromise your server they won't have the key also. Basically, you don't want to
16 | # store the key with the lock.
17 | class AES256
18 | class << self
19 | attr_writer :key
20 |
21 | def encrypt(*tokens)
22 | aes.encrypt
23 | aes.key = @key
24 | [aes.update(tokens.join) + aes.final].pack("m").chomp
25 | end
26 |
27 | def matches?(crypted, *tokens)
28 | aes.decrypt
29 | aes.key = @key
30 | (aes.update(crypted.unpack("m").first) + aes.final) == tokens.join
31 | rescue OpenSSL::CipherError
32 | false
33 | end
34 |
35 | private
36 | def aes
37 | raise ArgumentError.new("You must provide a key like #{name}.key = my_key before using the #{name}") if @key.blank?
38 | @aes ||= OpenSSL::Cipher::Cipher.new("AES-256-ECB")
39 | end
40 | end
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: .
3 | specs:
4 | rubycas-server (1.0)
5 | activerecord (~> 2.3.12)
6 | activesupport (~> 2.3.12)
7 | crypt-isaac (~> 0.9.1)
8 | gettext (~> 2.1.0)
9 | sinatra (~> 1.0)
10 |
11 | GEM
12 | remote: http://rubygems.org/
13 | specs:
14 | activerecord (2.3.12)
15 | activesupport (= 2.3.12)
16 | activeresource (2.3.12)
17 | activesupport (= 2.3.12)
18 | activesupport (2.3.12)
19 | capybara (0.4.1.2)
20 | celerity (>= 0.7.9)
21 | culerity (>= 0.2.4)
22 | mime-types (>= 1.16)
23 | nokogiri (>= 1.3.3)
24 | rack (>= 1.0.0)
25 | rack-test (>= 0.5.4)
26 | selenium-webdriver (>= 0.0.27)
27 | xpath (~> 0.1.3)
28 | celerity (0.8.8)
29 | childprocess (0.1.7)
30 | ffi (~> 0.6.3)
31 | crypt-isaac (0.9.1)
32 | culerity (0.2.15)
33 | diff-lcs (1.1.2)
34 | ffi (0.6.3)
35 | rake (>= 0.8.7)
36 | gettext (2.1.0)
37 | locale (>= 2.0.5)
38 | json_pure (1.5.1)
39 | locale (2.0.5)
40 | mime-types (1.16)
41 | net-ldap (0.1.1)
42 | nokogiri (1.4.4)
43 | rack (1.2.1)
44 | rack-test (0.5.7)
45 | rack (>= 1.0)
46 | rake (0.8.7)
47 | rspec (2.5.0)
48 | rspec-core (~> 2.5.0)
49 | rspec-expectations (~> 2.5.0)
50 | rspec-mocks (~> 2.5.0)
51 | rspec-core (2.5.1)
52 | rspec-expectations (2.5.0)
53 | diff-lcs (~> 1.1.2)
54 | rspec-mocks (2.5.0)
55 | rubyzip (0.9.4)
56 | selenium-webdriver (0.1.3)
57 | childprocess (~> 0.1.5)
58 | ffi (~> 0.6.3)
59 | json_pure
60 | rubyzip
61 | sinatra (1.1.3)
62 | rack (~> 1.1)
63 | tilt (< 2.0, >= 1.2.2)
64 | sqlite3 (1.3.3)
65 | tilt (1.2.2)
66 | xpath (0.1.3)
67 | nokogiri (~> 1.3)
68 |
69 | PLATFORMS
70 | ruby
71 |
72 | DEPENDENCIES
73 | activeresource (~> 2.3.12)
74 | capybara
75 | net-ldap (~> 0.1.1)
76 | rack-test
77 | rspec
78 | rspec-core
79 | rubycas-server!
80 | sqlite3 (~> 1.3.1)
81 |
--------------------------------------------------------------------------------
/db/migrate/001_create_initial_structure.rb:
--------------------------------------------------------------------------------
1 | class CreateInitialStructure < ActiveRecord::Migration
2 | def self.up
3 | # Oracle table names cannot exceed 30 chars...
4 | # See http://code.google.com/p/rubycas-server/issues/detail?id=15
5 | create_table 'casserver_lt', :force => true do |t|
6 | t.string 'ticket', :null => false
7 | t.timestamp 'created_on', :null => false
8 | t.datetime 'consumed', :null => true
9 | t.string 'client_hostname', :null => false
10 | end
11 |
12 | create_table 'casserver_st', :force => true do |t|
13 | t.string 'ticket', :null => false
14 | t.text 'service', :null => false
15 | t.timestamp 'created_on', :null => false
16 | t.datetime 'consumed', :null => true
17 | t.string 'client_hostname', :null => false
18 | t.string 'username', :null => false
19 | t.string 'type', :null => false
20 | t.integer 'granted_by_pgt_id', :null => true
21 | t.integer 'granted_by_tgt_id', :null => true
22 | end
23 |
24 | create_table 'casserver_tgt', :force => true do |t|
25 | t.string 'ticket', :null => false
26 | t.timestamp 'created_on', :null => false
27 | t.string 'client_hostname', :null => false
28 | t.string 'username', :null => false
29 | t.text 'extra_attributes', :null => true
30 | end
31 |
32 | create_table 'casserver_pgt', :force => true do |t|
33 | t.string 'ticket', :null => false
34 | t.timestamp 'created_on', :null => false
35 | t.string 'client_hostname', :null => false
36 | t.string 'iou', :null => false
37 | t.integer 'service_ticket_id', :null => false
38 | end
39 | end # self.up
40 |
41 | def self.down
42 | drop_table 'casserver_pgt'
43 | drop_table 'casserver_tgt'
44 | drop_table 'casserver_st'
45 | drop_table 'casserver_lt'
46 | end # self.down
47 | end
48 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/authlogic_crypto_providers/sha512.rb:
--------------------------------------------------------------------------------
1 | require "digest/sha2"
2 |
3 | module Authlogic
4 | # The acts_as_authentic method has a crypto_provider option. This allows you to use any type of encryption you like.
5 | # Just create a class with a class level encrypt and matches? method. See example below.
6 | #
7 | # === Example
8 | #
9 | # class MyAwesomeEncryptionMethod
10 | # def self.encrypt(*tokens)
11 | # # the tokens passed will be an array of objects, what type of object is irrelevant,
12 | # # just do what you need to do with them and return a single encrypted string.
13 | # # for example, you will most likely join all of the objects into a single string and then encrypt that string
14 | # end
15 | #
16 | # def self.matches?(crypted, *tokens)
17 | # # return true if the crypted string matches the tokens.
18 | # # depending on your algorithm you might decrypt the string then compare it to the token, or you might
19 | # # encrypt the tokens and make sure it matches the crypted string, its up to you
20 | # end
21 | # end
22 | module CryptoProviders
23 | # = Sha512
24 | #
25 | # Uses the Sha512 hash algorithm to encrypt passwords.
26 | class Sha512
27 | class << self
28 | attr_accessor :join_token
29 |
30 | # The number of times to loop through the encryption. This is ten because that is what restful_authentication defaults to.
31 | def stretches
32 | @stretches ||= 20
33 | end
34 | attr_writer :stretches
35 |
36 | # Turns your raw password into a Sha512 hash.
37 | def encrypt(*tokens)
38 | digest = tokens.flatten.join(join_token)
39 | stretches.times { digest = Digest::SHA512.hexdigest(digest) }
40 | digest
41 | end
42 |
43 | # Does the crypted password match the tokens? Uses the same tokens that were used to encrypt.
44 | def matches?(crypted, *tokens)
45 | encrypt(*tokens) == crypted
46 | end
47 | end
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/google.rb:
--------------------------------------------------------------------------------
1 | require 'casserver/authenticators/base'
2 | require 'uri'
3 | require 'net/http'
4 | require 'net/https'
5 | require 'timeout'
6 |
7 | # Validates Google accounts against Google's authentication service -- in other
8 | # words, this authenticator allows users to log in to CAS using their
9 | # Gmail/Google accounts.
10 | class CASServer::Authenticators::Google < CASServer::Authenticators::Base
11 | def validate(credentials)
12 | read_standard_credentials(credentials)
13 |
14 | return false if @username.blank? || @password.blank?
15 |
16 | auth_data = {
17 | 'Email' => @username,
18 | 'Passwd' => @password,
19 | 'service' => 'xapi',
20 | 'source' => 'RubyCAS-Server',
21 | 'accountType' => 'HOSTED_OR_GOOGLE'
22 | }
23 |
24 | url = URI.parse('https://www.google.com/accounts/ClientLogin')
25 | if @options[:proxy]
26 | http = Net::HTTP.Proxy(@options[:proxy][:host], @options[:proxy][:port], @options[:proxy][:username], @options[:proxy][:password]).new(url.host, url.port)
27 | else
28 | http = Net::HTTP.new(url.host, url.port)
29 | end
30 | http.use_ssl = true
31 |
32 | # TODO: make the timeout configurable
33 | wait_seconds = 10
34 | begin
35 | timeout(wait_seconds) do
36 | res = http.start do |conn|
37 | req = Net::HTTP::Post.new(url.path)
38 | req.set_form_data(auth_data,'&')
39 | conn.request(req)
40 | end
41 |
42 | case res
43 | when Net::HTTPSuccess
44 | true
45 | when Net::HTTPForbidden
46 | false
47 | else
48 | $LOG.error("Unexpected response from Google while validating credentials: #{res.inspect} ==> #{res.body}.")
49 | raise CASServer::AuthenticatorError, "Unexpected response received from Google while validating credentials."
50 | end
51 | end
52 | rescue Timeout::Error
53 | $LOG.error("Google did not respond to the credential validation request. We waited for #{wait_seconds.inspect} seconds before giving up.")
54 | raise CASServer::AuthenticatorError, "Timeout while waiting for Google to validate credentials."
55 | end
56 |
57 | end
58 | end
59 |
--------------------------------------------------------------------------------
/public/themes/cas.css:
--------------------------------------------------------------------------------
1 | * {
2 | font-family: Verdana, sans-serif;
3 | }
4 |
5 | body {
6 | text-align: center; /* hack for IE */
7 | }
8 |
9 | label {
10 | font-weight: bold;
11 | font-size: 9px;
12 | }
13 |
14 | input {
15 | font-weight: normal;
16 | font-size: 12px;
17 | }
18 |
19 | input.button {
20 | /*font-weight: bold;*/
21 | font-size: 10px;
22 | }
23 |
24 | #login-box {
25 | margin: 0 auto;
26 | width: 350px;
27 | top: 130px;
28 | position: relative;
29 | }
30 |
31 | #headline-container {
32 | text-align: right;
33 | border-bottom: 1px solid #899989;
34 | font-family: Tahoma, Verdana, sans-serif;
35 | font-size: 22px;
36 | margin-right: 0px;
37 | padding-right: 7px;
38 | margin-left: 10px;
39 | letter-spacing: -0.25px;
40 | }
41 |
42 | #logo-container {
43 | vertical-align: top;
44 | }
45 |
46 | #logo {
47 | }
48 |
49 | #login-form-container {
50 | vertical-align: top;
51 | }
52 |
53 |
54 | #username,
55 | #password {
56 | width: 10em;
57 | }
58 |
59 | #login-form {
60 | padding: 20px;
61 | }
62 |
63 |
64 | #form-layout {
65 | position: relative;
66 | top: 6px;
67 | width: 100%;
68 | }
69 |
70 | #form-layout td {
71 | text-align: center;
72 | padding-bottom: 8px;
73 | }
74 |
75 | #form-layout td#submit-container {
76 | text-align: right;
77 | padding-right: 10px;
78 | }
79 |
80 | #infoline {
81 | font-size: 9px;
82 | }
83 |
84 | #messagebox-container {
85 | padding-left: 11px;
86 | padding-right: 16px;
87 | }
88 |
89 | div.messagebox {
90 | font-size: 12px;
91 | padding: 5px;
92 | padding-left: 55px;
93 | text-align: center;
94 | width: 70%;
95 | min-height: 34px;
96 | vertical-align: middle;
97 | }
98 |
99 | div.mistake {
100 | color: #d00;
101 | background-image: url(warning.png);
102 | background-repeat: no-repeat;
103 | background-position: 10px 5px;
104 | font-weight: bold;
105 | }
106 |
107 | div.confirmation {
108 | color: #280;
109 | background-image: url(ok.png);
110 | background-repeat: no-repeat;
111 | background-position: 10px 5px;
112 | font-weight: bold;
113 | }
114 |
115 | div.notice {
116 | color: #04c;
117 | background-image: url(notice.png);
118 | background-repeat: no-repeat;
119 | background-position: 10px 5px;
120 | font-weight: bold;
121 | }
122 |
--------------------------------------------------------------------------------
/rubycas-server.gemspec:
--------------------------------------------------------------------------------
1 |
2 | $gemspec = Gem::Specification.new do |s|
3 | s.name = 'rubycas-server'
4 | s.version = '1.0'
5 | s.authors = ["Matt Zukowski"]
6 | s.email = ["matt@zukowski.ca"]
7 | s.homepage = 'http://code.google.com/p/rubycas-server/'
8 | s.platform = Gem::Platform::RUBY
9 | s.summary = %q{Provides single sign-on authentication for web applications using the CAS protocol.}
10 | s.description = %q{Provides single sign-on authentication for web applications using the CAS protocol.}
11 |
12 | s.files = [
13 | "CHANGELOG", "LICENSE", "README.md", "Rakefile", "setup.rb",
14 | "bin/*", "db/*", "lib/**/*.rb", "public/**/*", "po/**/*", "mo/**/*", "resources/*.*",
15 | "config.ru", "config/**/*", "tasks/**/*.rake", "vendor/**/*", "script/*", "lib/**/*.erb", "lib/**/*.builder",
16 | "Gemfile", "rubycas-server.gemspec"
17 | ].map{|p| Dir[p]}.flatten
18 |
19 | s.test_files = `git ls-files -- spec`.split("\n")
20 |
21 | s.executables = ["rubycas-server"]
22 | s.bindir = "bin"
23 | s.require_path = "lib"
24 |
25 | s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.md"]
26 |
27 | s.has_rdoc = true
28 | s.post_install_message = %q{
29 | For more information on RubyCAS-Server, see http://code.google.com/p/rubycas-server
30 |
31 | If you plan on using RubyCAS-Server with languages other than English, please cd into the
32 | RubyCAS-Server installation directory (where the gem is installed) and type `rake localization:mo`
33 | to build the LOCALE_LC files.
34 |
35 | }
36 |
37 | s.add_dependency("activerecord", "~> 2.3.12")
38 | s.add_dependency("activesupport", "~> 2.3.12")
39 | s.add_dependency("sinatra", "~> 1.0")
40 | s.add_dependency("gettext", "~> 2.1.0")
41 | s.add_dependency("crypt-isaac", "~> 0.9.1")
42 |
43 | s.add_development_dependency("rack-test")
44 | s.add_development_dependency("capybara")
45 | s.add_development_dependency("rspec")
46 | s.add_development_dependency("rspec-core")
47 | s.add_development_dependency("sqlite3", "~> 1.3.1")
48 |
49 | # for authenticator specs
50 | s.add_development_dependency("net-ldap", "~> 0.1.1")
51 | s.add_development_dependency("activeresource", "~> 2.3.12")
52 |
53 | s.rdoc_options = [
54 | '--quiet', '--title', 'RubyCAS-Server Documentation', '--opname',
55 | 'index.html', '--line-numbers', '--main', 'README.md', '--inline-source'
56 | ]
57 | end
58 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'sinatra'
3 | require 'rack/test'
4 | require 'rspec'
5 | #require 'spec/autorun'
6 | #require 'spec/interop/test'
7 | require 'logger'
8 | require 'ostruct'
9 |
10 | require 'capybara'
11 | require 'capybara/dsl'
12 |
13 | # set test environment
14 | set :environment, :test
15 | set :run, false
16 | set :raise_errors, true
17 | set :logging, false
18 |
19 |
20 | if Dir.getwd =~ /\/spec$/
21 | # Avoid potential weirdness by changing the working directory to the CASServer root
22 | FileUtils.cd('..')
23 | end
24 |
25 | def silence_warnings
26 | old_verbose, $VERBOSE = $VERBOSE, nil
27 | yield
28 | ensure
29 | $VERBOSE = old_verbose
30 | end
31 |
32 | # Ugly monkeypatch to allow us to test for correct redirection to
33 | # external services.
34 | #
35 | # This will likely break in the future when Capybara or RackTest are upgraded.
36 | class Capybara::Driver::RackTest
37 | def current_url
38 | if @redirected_to_external_url
39 | @redirected_to_external_url
40 | else
41 | request.url rescue ""
42 | end
43 | end
44 |
45 | def follow_redirects!
46 | if response.redirect? && response['Location'] =~ /^http[s]?:/
47 | @redirected_to_external_url = response['Location']
48 | else
49 | 5.times do
50 | follow_redirect! if response.redirect?
51 | end
52 | raise Capybara::InfiniteRedirectError, "redirected more than 5 times, check for infinite redirects." if response.redirect?
53 | end
54 | end
55 | end
56 |
57 | # This called in specs' `before` block.
58 | # Due to the way Sinatra applications are loaded,
59 | # we're forced to delay loading of the server code
60 | # until the start of each test so that certain
61 | # configuraiton options can be changed (e.g. `uri_path`)
62 | def load_server(config_file)
63 | ENV['CONFIG_FILE'] = config_file
64 |
65 | silence_warnings do
66 | load File.dirname(__FILE__) + '/../lib/casserver/server.rb'
67 | end
68 |
69 | CASServer::Server.enable(:raise_errors)
70 | CASServer::Server.disable(:show_exceptions)
71 |
72 | #Capybara.current_driver = :selenium
73 | Capybara.app = CASServer::Server
74 | end
75 |
76 | # Deletes the sqlite3 database specified in the app's config
77 | # and runs the db:migrate rake tasks to rebuild the database schema.
78 | def reset_spec_database
79 | raise "Cannot reset the spec database because config[:database][:database] is not defined." unless
80 | CASServer::Server.config[:database] && CASServer::Server.config[:database][:database]
81 |
82 | FileUtils.rm_f(CASServer::Server.config[:database][:database])
83 |
84 | ActiveRecord::Base.logger = Logger.new(STDOUT)
85 | ActiveRecord::Base.logger.level = Logger::ERROR
86 | ActiveRecord::Migration.verbose = false
87 | ActiveRecord::Migrator.migrate("db/migrate")
88 | end
89 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/base.rb:
--------------------------------------------------------------------------------
1 | module CASServer
2 | module Authenticators
3 | class Base
4 | attr_accessor :options
5 | attr_reader :username # make this accessible so that we can pick up any
6 | # transformations done within the authenticator
7 |
8 | # This is called at server startup.
9 | # Any class-wide initializiation for the authenticator should be done here.
10 | # (e.g. establish database connection).
11 | # You can leave this empty if you don't need to set up anything.
12 | def self.setup(options)
13 | end
14 |
15 | # This is called prior to #validate (i.e. each time the user tries to log in).
16 | # Any per-instance initialization for the authenticator should be done here.
17 | #
18 | # By default this makes the authenticator options hash available for #validate
19 | # under @options and initializes @extra_attributes to an empty hash.
20 | def configure(options)
21 | raise ArgumentError, "options must be a HashWithIndifferentAccess" unless options.kind_of? HashWithIndifferentAccess
22 | @options = options.dup
23 | @extra_attributes = {}
24 | end
25 |
26 | # Override this to implement your authentication credential validation.
27 | # This is called each time the user tries to log in. The credentials hash
28 | # holds the credentials as entered by the user (generally under :username
29 | # and :password keys; :service and :request are also included by default)
30 | #
31 | # Note that the standard credentials can be read in to instance variables
32 | # by calling #read_standard_credentials.
33 | def validate(credentials)
34 | raise NotImplementedError, "This method must be implemented by a class extending #{self.class}"
35 | end
36 |
37 | def extra_attributes
38 | @extra_attributes
39 | end
40 |
41 | protected
42 | def read_standard_credentials(credentials)
43 | @username = credentials[:username]
44 | @password = credentials[:password]
45 | @service = credentials[:service]
46 | @request = credentials[:request]
47 | end
48 |
49 | def extra_attributes_to_extract
50 | if @options[:extra_attributes].kind_of? Array
51 | attrs = @options[:extra_attributes]
52 | elsif @options[:extra_attributes].kind_of? String
53 | attrs = @options[:extra_attributes].split(',').collect{|col| col.strip}
54 | else
55 | $LOG.error("Can't figure out attribute list from #{@options[:extra_attributes].inspect}. This must be an Aarray of column names or a comma-separated list.")
56 | attrs = []
57 | end
58 |
59 | $LOG.debug("#{self.class.name} will try to extract the following extra_attributes: #{attrs.inspect}")
60 | return attrs
61 | end
62 | end
63 | end
64 |
65 | class AuthenticatorError < Exception
66 | end
67 | end
68 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/sql_rest_auth.rb:
--------------------------------------------------------------------------------
1 | require 'casserver/authenticators/sql_encrypted'
2 |
3 | require 'digest/sha1'
4 |
5 | begin
6 | require 'active_record'
7 | rescue LoadError
8 | require 'rubygems'
9 | require 'active_record'
10 | end
11 |
12 | # This is a version of the SQL authenticator that works nicely with RestfulAuthentication.
13 | # Passwords are encrypted the same way as it done in RestfulAuthentication.
14 | # Before use you this, you MUST configure rest_auth_digest_streches and rest_auth_site_key in
15 | # config.
16 | #
17 | # Using this authenticator requires restful authentication plugin on rails (client) side.
18 | #
19 | # * git://github.com/technoweenie/restful-authentication.git
20 | #
21 | class CASServer::Authenticators::SQLRestAuth < CASServer::Authenticators::SQLEncrypted
22 |
23 | def validate(credentials)
24 | read_standard_credentials(credentials)
25 | raise_if_not_configured
26 |
27 | raise CASServer::AuthenticatorError, "You must specify a 'site_key' in the SQLRestAuth authenticator's configuration!" unless @options[:site_key]
28 | raise CASServer::AuthenticatorError, "You must specify 'digest_streches' in the SQLRestAuth authenticator's configuration!" unless @options[:digest_streches]
29 |
30 | user_model = self.class.user_model
31 |
32 | username_column = @options[:username_column] || "email"
33 |
34 | $LOG.debug "#{self.class}: [#{user_model}] " + "Connection pool size: #{user_model.connection_pool.instance_variable_get(:@checked_out).length}/#{user_model.connection_pool.instance_variable_get(:@connections).length}"
35 | results = user_model.find(:all, :conditions => ["#{username_column} = ?", @username])
36 | user_model.connection_pool.checkin(user_model.connection)
37 |
38 | if results.size > 0
39 | $LOG.warn("Multiple matches found for user '#{@username}'") if results.size > 1
40 | user = results.first
41 | if user.crypted_password == user.encrypt(@password)
42 | unless @options[:extra_attributes].blank?
43 | extract_extra(user)
44 | log_extra
45 | end
46 | return true
47 | else
48 | return false
49 | end
50 | else
51 | return false
52 | end
53 | end
54 |
55 | def self.setup(options)
56 | super(options)
57 | user_model.__send__(:include, EncryptedPassword)
58 | end
59 |
60 | module EncryptedPassword
61 |
62 | def self.included(mod)
63 | raise "#{self} should be inclued in an ActiveRecord class!" unless mod.respond_to?(:before_save)
64 | end
65 |
66 | def encrypt(password)
67 | password_digest(password, self.salt)
68 | end
69 |
70 | def secure_digest(*args)
71 | Digest::SHA1.hexdigest(args.flatten.join('--'))
72 | end
73 |
74 | def password_digest(password, salt)
75 | digest = @options[:site_key]
76 | @options[:digest_streches].times do
77 | digest = secure_digest(digest, salt, password, @options[:site_key])
78 | end
79 | digest
80 | end
81 | end
82 | end
83 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/sql_encrypted.rb:
--------------------------------------------------------------------------------
1 | require 'casserver/authenticators/sql'
2 |
3 | require 'digest/sha1'
4 | require 'digest/sha2'
5 | require 'crypt-isaac'
6 |
7 | # This is a more secure version of the SQL authenticator. Passwords are encrypted
8 | # rather than being stored in plain text.
9 | #
10 | # Based on code contributed by Ben Mabey.
11 | #
12 | # Using this authenticator requires some configuration on the client side. Please see
13 | # http://code.google.com/p/rubycas-server/wiki/UsingTheSQLEncryptedAuthenticator
14 | class CASServer::Authenticators::SQLEncrypted < CASServer::Authenticators::SQL
15 | # Include this module into your application's user model.
16 | #
17 | # Your model must have an 'encrypted_password' column where the password will be stored,
18 | # and an 'encryption_salt' column that will be populated with a random string before
19 | # the user record is first created.
20 | module EncryptedPassword
21 | def self.included(mod)
22 | raise "#{self} should be inclued in an ActiveRecord class!" unless mod.respond_to?(:before_save)
23 | mod.before_save :generate_encryption_salt
24 | end
25 |
26 | def encrypt(str)
27 | generate_encryption_salt unless encryption_salt
28 | Digest::SHA256.hexdigest("#{encryption_salt}::#{str}")
29 | end
30 |
31 | def password=(password)
32 | self[:encrypted_password] = encrypt(password)
33 | end
34 |
35 | def generate_encryption_salt
36 | self.encryption_salt = Digest::SHA1.hexdigest(Crypt::ISAAC.new.rand(2**31).to_s) unless
37 | encryption_salt
38 | end
39 | end
40 |
41 | def self.setup(options)
42 | super(options)
43 | user_model.__send__(:include, EncryptedPassword)
44 | end
45 |
46 | def validate(credentials)
47 | read_standard_credentials(credentials)
48 | raise_if_not_configured
49 |
50 | user_model = self.class.user_model
51 |
52 | username_column = @options[:username_column] || "username"
53 | encrypt_function = @options[:encrypt_function] || 'user.encrypted_password == Digest::SHA256.hexdigest("#{user.encryption_salt}::#{@password}")'
54 |
55 | $LOG.debug "#{self.class}: [#{user_model}] " + "Connection pool size: #{user_model.connection_pool.instance_variable_get(:@checked_out).length}/#{user_model.connection_pool.instance_variable_get(:@connections).length}"
56 | results = user_model.find(:all, :conditions => ["#{username_column} = ?", @username])
57 | user_model.connection_pool.checkin(user_model.connection)
58 |
59 | if results.size > 0
60 | $LOG.warn("Multiple matches found for user '#{@username}'") if results.size > 1
61 | user = results.first
62 | unless @options[:extra_attributes].blank?
63 | if results.size > 1
64 | $LOG.warn("#{self.class}: Unable to extract extra_attributes because multiple matches were found for #{@username.inspect}")
65 | else
66 | extract_extra(user)
67 | log_extra
68 | end
69 | end
70 | return eval(encrypt_function)
71 | else
72 | return false
73 | end
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/po/rubycas-server.pot:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) 2009 of The Contributors (see project history on github.com)
3 | # This file is distributed under the same license as the RubyCAS-Server package.
4 | # Matt Zukowski, 2009.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: rubycas-server \n"
10 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
11 | "PO-Revision-Date: 2008-10-29 20:55+0200\n"
12 | "Last-Translator: FULL NAME \n"
13 | "Language-Team: LANGUAGE \n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
18 |
19 | #: lib/casserver/cas.rb:117
20 | msgid ""
21 | "Your login request did not include a login ticket. There may be a problem "
22 | "with the authentication system."
23 | msgstr ""
24 |
25 | #: lib/casserver/cas.rb:121
26 | msgid ""
27 | "The login ticket you provided has already been used up. Please try logging "
28 | "in again."
29 | msgstr ""
30 |
31 | #: lib/casserver/cas.rb:126
32 | msgid "You took too long to enter your credentials. Please try again."
33 | msgstr ""
34 |
35 | #: lib/casserver/cas.rb:130
36 | msgid ""
37 | "The login ticket you provided is invalid. There may be a problem with the "
38 | "authentication system."
39 | msgstr ""
40 |
41 | #: lib/casserver/controllers.rb:32
42 | msgid ""
43 | "You are currently logged in as '%s'. If this is not you, please log in below."
44 | msgstr ""
45 |
46 | #: lib/casserver/controllers.rb:37
47 | msgid ""
48 | "The client and server are unable to negotiate authentication. Please try "
49 | "logging in again later."
50 | msgstr ""
51 |
52 | #: lib/casserver/controllers.rb:54
53 | msgid ""
54 | "The server cannot fulfill this gateway request because no service parameter "
55 | "was given."
56 | msgstr ""
57 |
58 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
59 | msgid ""
60 | "The target service your browser supplied appears to be invalid. Please "
61 | "contact your system administrator for help."
62 | msgstr ""
63 |
64 | #: lib/casserver/controllers.rb:88
65 | msgid ""
66 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
67 | "with your request."
68 | msgstr ""
69 |
70 | #: lib/casserver/controllers.rb:168
71 | msgid "You have successfully logged in."
72 | msgstr ""
73 |
74 | #: lib/casserver/controllers.rb:184
75 | msgid "Incorrect username or password."
76 | msgstr ""
77 |
78 | #: lib/casserver/controllers.rb:267
79 | msgid "You have successfully logged out."
80 | msgstr ""
81 |
82 | #: lib/casserver/controllers.rb:269
83 | msgid " Please click on the following link to continue:"
84 | msgstr ""
85 |
86 | #: lib/casserver/controllers.rb:419
87 | msgid "To generate a login ticket, you must make a POST request."
88 | msgstr ""
89 |
90 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
91 | msgid " Central Login"
92 | msgstr ""
93 |
94 | #: lib/casserver/views.rb:68
95 | msgid "Please wait..."
96 | msgstr ""
97 |
98 | #: lib/casserver/views.rb:74
99 | msgid "Username"
100 | msgstr ""
101 |
102 | #: lib/casserver/views.rb:83
103 | msgid "Password"
104 | msgstr ""
105 |
106 | #: lib/casserver/views.rb:92
107 | msgid "Remeber me on this computer"
108 | msgstr ""
109 |
110 | #: lib/casserver/views.rb:101
111 | msgid "LOGIN"
112 | msgstr ""
113 |
--------------------------------------------------------------------------------
/lib/casserver/localization.rb:
--------------------------------------------------------------------------------
1 | require "gettext"
2 | require "gettext/cgi"
3 | require 'active_support'
4 |
5 | module CASServer
6 | module Localization
7 | def self.included(mod)
8 | mod.module_eval do
9 | include GetText
10 | end
11 | end
12 |
13 | include GetText
14 | bindtextdomain("rubycas-server", :path => File.join(File.dirname(File.expand_path(__FILE__)), "../../locale"))
15 |
16 | def determine_locale(request)
17 | source = nil
18 | lang = case
19 | when !request.params['lang'].blank?
20 | source = "'lang' request variable"
21 | request.cookies['lang'] = request.params['lang']
22 | request.params['lang']
23 | when !request.cookies['lang'].blank?
24 | source = "'lang' cookie"
25 | request.cookies['lang']
26 | when !request.env['HTTP_ACCEPT_LANGUAGE'].blank?
27 | source = "'HTTP_ACCEPT_LANGUAGE' header"
28 | lang = request.env['HTTP_ACCEPT_LANGUAGE']
29 | when !request.env['HTTP_USER_AGENT'].blank? && request.env['HTTP_USER_AGENT'] =~ /[^a-z]([a-z]{2}(-[a-z]{2})?)[^a-z]/i
30 | source = "'HTTP_USER_AGENT' header"
31 | $~[1]
32 | # when !$CONF['default_locale'].blank?
33 | # source = "'default_locale' config option"
34 | # $CONF[:default_locale]
35 | else
36 | source = "default"
37 | "en"
38 | end
39 |
40 | $LOG.debug "Detected locale is #{lang.inspect} (from #{source})"
41 |
42 | lang.gsub!('_','-')
43 |
44 | # TODO: Need to confirm that this method of splitting the accepted
45 | # language string is correct.
46 | if lang =~ /[,;\|]/
47 | langs = lang.split(/[,;\|]/)
48 | else
49 | langs = [lang]
50 | end
51 |
52 | # TODO: This method of selecting the desired language might not be
53 | # standards-compliant. For example, http://www.w3.org/TR/ltli/
54 | # suggests that de-de and de-*-DE might be acceptable identifiers
55 | # for selecting various wildcards. The algorithm below does not
56 | # currently support anything like this.
57 |
58 | available = available_locales
59 |
60 | if available.length == 1
61 | $LOG.warn "Only the #{available.first.inspect} localization is available. You should run `rake localization:mo` to compile support for additional languages!"
62 | elsif available.length == 0 # this should never actually happen
63 | $LOG.error "No localizations available! Run `rake localization:mo` to compile support for additional languages."
64 | end
65 |
66 | # Try to pick a locale exactly matching the desired identifier, otherwise
67 | # fall back to locale without region (i.e. given "en-US; de-DE", we would
68 | # first look for "en-US", then "en", then "de-DE", then "de").
69 |
70 | chosen_lang = nil
71 | langs.each do |l|
72 | a = available.find{ |a| a =~ Regexp.new("\\A#{l}\\Z", 'i') ||
73 | a =~ Regexp.new("#{l}-\w*", 'i') }
74 | if a
75 | chosen_lang = a
76 | break
77 | end
78 | end
79 |
80 | chosen_lang = "en" if chosen_lang.blank?
81 |
82 | $LOG.debug "Chosen locale is #{chosen_lang.inspect}"
83 |
84 | return chosen_lang
85 | end
86 |
87 | def available_locales
88 | (Dir.glob(File.join(File.dirname(File.expand_path(__FILE__)), "../../locale/[a-z]*")).map{|path| File.basename(path)} << "en").uniq.collect{|l| l.gsub('_','-')}
89 | end
90 | end
91 | end
92 |
--------------------------------------------------------------------------------
/config/unicorn.rb:
--------------------------------------------------------------------------------
1 | # Sample configuration file for Unicorn (not Rack)
2 | #
3 | # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
4 | # documentation.
5 | SINATRA_ROOT = `pwd`.strip
6 |
7 | # Use at least one worker per core if you're on a dedicated server,
8 | # more will usually help for _short_ waits on databases/caches.
9 | worker_processes 3
10 |
11 | # Help ensure your application will always spawn in the symlinked
12 | # "current" directory that Capistrano sets up.
13 | working_directory SINATRA_ROOT # available in 0.94.0+
14 |
15 | # listen on both a Unix domain socket and a TCP port,
16 | # we use a shorter backlog for quicker failover when busy
17 | # listen "/tmp/.sock", :backlog => 64
18 | listen 18889, :tcp_nopush => true
19 |
20 | # nuke workers after 30 seconds instead of 60 seconds (the default)
21 | timeout 30
22 |
23 | # feel free to point this anywhere accessible on the filesystem
24 |
25 | pid "#{SINATRA_ROOT}/tmp/pids/unicorn.pid"
26 |
27 | # relative_path "/test_platform"
28 | # some applications/frameworks log to stderr or stdout, so prevent
29 | # them from going to /dev/null when daemonized here:
30 | stderr_path "#{SINATRA_ROOT}/log/unicorn.stderr.log"
31 | stdout_path "#{SINATRA_ROOT}/log/unicorn.stdout.log"
32 |
33 | # combine REE with "preload_app true" for memory savings
34 | # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
35 | preload_app false
36 | GC.respond_to?(:copy_on_write_friendly=) and
37 | GC.copy_on_write_friendly = true
38 |
39 | before_fork do |server, worker|
40 | # the following is highly recomended for Rails + "preload_app true"
41 | # as there's no need for the master process to hold a connection
42 | # defined?(ActiveRecord::Base) and
43 | # ActiveRecord::Base.connection.disconnect!
44 |
45 | # The following is only recommended for memory/DB-constrained
46 | # installations. It is not needed if your system can house
47 | # twice as many worker_processes as you have configured.
48 | #
49 | # # This allows a new master process to incrementally
50 | # # phase out the old master process with SIGTTOU to avoid a
51 | # # thundering herd (especially in the "preload_app false" case)
52 | # # when doing a transparent upgrade. The last worker spawned
53 | # # will then kill off the old master process with a SIGQUIT.
54 | old_pid = "#{server.config[:pid]}.oldbin"
55 |
56 | puts 'pid:'
57 | puts '-------------------'
58 | puts server.pid
59 | puts old_pid
60 | puts '---------------------'
61 |
62 | if old_pid != server.pid
63 | begin
64 | sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
65 | Process.kill(sig, File.read(old_pid).to_i)
66 | rescue Errno::ENOENT, Errno::ESRCH
67 | end
68 | end
69 | #
70 | # # *optionally* throttle the master from forking too quickly by sleeping
71 | sleep 1
72 | end
73 |
74 | after_fork do |server, worker|
75 | # per-process listener ports for debugging/admin/migrations
76 | # addr = "127.0.0.1:#{9293 + worker.nr}"
77 | # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
78 |
79 | # the following is *required* for Rails + "preload_app true",
80 | # defined?(ActiveRecord::Base) and
81 | # ActiveRecord::Base.establish_connection
82 |
83 | # if preload_app is true, then you may also want to check and
84 | # restart any other shared sockets/descriptors such as Memcached,
85 | # and Redis. TokyoCabinet file handles are safe to reuse
86 | # between any number of forked children (assuming your kernel
87 | # correctly implements pread()/pwrite() system calls)
88 | end
--------------------------------------------------------------------------------
/lib/casserver/authenticators/sql_authlogic.rb:
--------------------------------------------------------------------------------
1 | require 'casserver/authenticators/sql'
2 |
3 | # These were pulled directly from Authlogic, and new ones can be added
4 | # just by including new Crypto Providers
5 | require File.dirname(__FILE__) + '/authlogic_crypto_providers/aes256'
6 | require File.dirname(__FILE__) + '/authlogic_crypto_providers/bcrypt'
7 | require File.dirname(__FILE__) + '/authlogic_crypto_providers/md5'
8 | require File.dirname(__FILE__) + '/authlogic_crypto_providers/sha1'
9 | require File.dirname(__FILE__) + '/authlogic_crypto_providers/sha512'
10 |
11 | begin
12 | require 'active_record'
13 | rescue LoadError
14 | require 'rubygems'
15 | require 'active_record'
16 | end
17 |
18 | # This is a version of the SQL authenticator that works nicely with Authlogic.
19 | # Passwords are encrypted the same way as it done in Authlogic.
20 | # Before use you this, you MUST configure rest_auth_digest_streches and rest_auth_site_key in
21 | # config.
22 | #
23 | # Using this authenticator requires restful authentication plugin on rails (client) side.
24 | #
25 | # * git://github.com/binarylogic/authlogic.git
26 | #
27 | # Usage:
28 |
29 | # authenticator:
30 | # class: CASServer::Authenticators::SQLAuthlogic
31 | # database:
32 | # adapter: mysql
33 | # database: some_database_with_users_table
34 | # user: root
35 | # password:
36 | # server: localhost
37 | # user_table: user
38 | # username_column: login
39 | # password_column: crypted_password
40 | # salt_column: password_salt
41 | # encryptor: Sha1
42 | # encryptor_options:
43 | # digest_format: --SALT--PASSWORD--
44 | # stretches: 1
45 | #
46 | class CASServer::Authenticators::SQLAuthlogic < CASServer::Authenticators::SQL
47 |
48 | def validate(credentials)
49 | read_standard_credentials(credentials)
50 | raise_if_not_configured
51 |
52 | user_model = self.class.user_model
53 |
54 | username_column = @options[:username_column] || "login"
55 | password_column = @options[:password_column] || "crypted_password"
56 | salt_column = @options[:salt_column]
57 |
58 | $LOG.debug "#{self.class}: [#{user_model}] " + "Connection pool size: #{user_model.connection_pool.instance_variable_get(:@checked_out).length}/#{user_model.connection_pool.instance_variable_get(:@connections).length}"
59 | results = user_model.find(:all, :conditions => ["#{username_column} = ?", @username])
60 | user_model.connection_pool.checkin(user_model.connection)
61 |
62 | begin
63 | encryptor = eval("Authlogic::CryptoProviders::" + @options[:encryptor] || "Sha512")
64 | rescue
65 | $LOG.warn("Could not initialize Authlogic crypto class for '#{@options[:encryptor]}'")
66 | encryptor = Authlogic::CryptoProviders::Sha512
67 | end
68 |
69 | @options[:encryptor_options].each do |name, value|
70 | encryptor.send("#{name}=", value) if encryptor.respond_to?("#{name}=")
71 | end
72 |
73 | if results.size > 0
74 | $LOG.warn("Multiple matches found for user '#{@username}'") if results.size > 1
75 | user = results.first
76 | tokens = [@password, (not salt_column.nil?) && user.send(salt_column) || nil].compact
77 | crypted = user.send(password_column)
78 |
79 | unless @options[:extra_attributes].blank?
80 | if results.size > 1
81 | $LOG.warn("#{self.class}: Unable to extract extra_attributes because multiple matches were found for #{@username.inspect}")
82 | else
83 | extract_extra(user)
84 | log_extra
85 | end
86 | end
87 |
88 | return encryptor.matches?(crypted, tokens)
89 | else
90 | return false
91 | end
92 | end
93 | end
94 |
--------------------------------------------------------------------------------
/po/zh_CN/rubycas-server.po:
--------------------------------------------------------------------------------
1 | # Simplified Chinese translations for RubyCAS-Server package
2 | # RubyCAS-Server 的简体中文翻译。
3 | # Copyright (C) 2008 THE RubyCAS-Server'S COPYRIGHT HOLDER
4 | # This file is distributed under the same license as the RubyCAS-Server package.
5 | # hgf , 2010.
6 | #
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: rubycas-server \n"
10 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
11 | "PO-Revision-Date: 2009-03-17 20:55+0200\n"
12 | "Last-Translator: hgf \n"
13 | "Language-Team: Simplified Chinese\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n"
18 |
19 | #: lib/casserver/cas.rb:117
20 | msgid ""
21 | "Your login request did not include a login ticket. There may be a problem "
22 | "with the authentication system."
23 | msgstr "你的登录请求没包含登录凭证,这可能是认证系统的问题"
24 |
25 | #: lib/casserver/cas.rb:121
26 | msgid ""
27 | "The login ticket you provided has already been used up. Please try logging "
28 | "in again."
29 | msgstr "你提供的登录凭证已经使用过了,请重新登录"
30 |
31 | #: lib/casserver/cas.rb:126
32 | msgid "You took too long to enter your credentials. Please try again."
33 | msgstr "你的认证花了太长时间,请重新认证"
34 |
35 | #: lib/casserver/cas.rb:130
36 | msgid ""
37 | "The login ticket you provided is invalid. There may be a problem with the "
38 | "authentication system."
39 | msgstr "你所提供的登录凭证是无效的,这可能是认证系统的问题"
40 |
41 | #: lib/casserver/controllers.rb:32
42 | msgid ""
43 | "You are currently logged in as '%s'. If this is not you, please log in below."
44 | msgstr "你正以 '%s' 的身份登入。如果这不是你,请重新登录"
45 |
46 | #: lib/casserver/controllers.rb:37
47 | msgid ""
48 | "The client and server are unable to negotiate authentication. Please try "
49 | "logging in again later."
50 | msgstr "现在无法认证,请稍后重试"
51 |
52 | #: lib/casserver/controllers.rb:54
53 | msgid ""
54 | "The server cannot fulfill this gateway request because no service parameter "
55 | "was given."
56 | msgstr "无法完成网关请求,因为没有提供服务的参数"
57 |
58 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
59 | msgid ""
60 | "The target service your browser supplied appears to be invalid. Please "
61 | "contact your system administrator for help."
62 | msgstr "你的浏览器提供的网址是无效的,请联系你的系统管理员"
63 |
64 | #: lib/casserver/controllers.rb:88
65 | msgid ""
66 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
67 | "with your request."
68 | msgstr "无法判断 CAS 的登录地址。请提供 submitToURI 的参数"
69 |
70 | #: lib/casserver/controllers.rb:168
71 | msgid "You have successfully logged in."
72 | msgstr "你成功登录了"
73 |
74 | #: lib/casserver/controllers.rb:184
75 | msgid "Incorrect username or password."
76 | msgstr "错误的帐号或密码"
77 |
78 | #: lib/casserver/controllers.rb:267
79 | msgid "You have successfully logged out."
80 | msgstr "你成功登出了"
81 |
82 | #: lib/casserver/controllers.rb:269
83 | msgid " Please click on the following link to continue:"
84 | msgstr " 请点击下列连接继续"
85 |
86 | #: lib/casserver/controllers.rb:419
87 | msgid "To generate a login ticket, you must make a POST request."
88 | msgstr "你必须使用 POST 来产生登录凭证"
89 |
90 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
91 | msgid " Central Login"
92 | msgstr " 整合登录"
93 |
94 | #: lib/casserver/views.rb:68
95 | msgid "Please wait..."
96 | msgstr "请稍候..."
97 |
98 | #: lib/casserver/views.rb:74
99 | msgid "Username"
100 | msgstr "用户名"
101 |
102 | #: lib/casserver/views.rb:83
103 | msgid "Password"
104 | msgstr "密码"
105 |
106 | #: lib/casserver/views.rb:92
107 | msgid "Remeber me on this computer"
108 | msgstr "记住我"
109 |
110 | #: lib/casserver/views.rb:101
111 | msgid "LOGIN"
112 | msgstr "登录"
113 |
114 |
--------------------------------------------------------------------------------
/po/zh_TW/rubycas-server.po:
--------------------------------------------------------------------------------
1 | # Traditional Chinese translations for RubyCAS-Server package
2 | # RubyCAS-Server 的正體中文翻譯。
3 | # Copyright (C) 2008 THE RubyCAS-Server'S COPYRIGHT HOLDER
4 | # This file is distributed under the same license as the RubyCAS-Server package.
5 | # Lin Jen-Shin , 2009.
6 | #
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: rubycas-server \n"
10 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
11 | "PO-Revision-Date: 2009-03-17 20:55+0200\n"
12 | "Last-Translator: Lin Jen-Shin \n"
13 | "Language-Team: Traditional Chinese\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n"
18 |
19 | #: lib/casserver/cas.rb:117
20 | msgid ""
21 | "Your login request did not include a login ticket. There may be a problem "
22 | "with the authentication system."
23 | msgstr "你沒有傳送登入憑証,這可能是認證系統的問題"
24 |
25 | #: lib/casserver/cas.rb:121
26 | msgid ""
27 | "The login ticket you provided has already been used up. Please try logging "
28 | "in again."
29 | msgstr "你所提供的登入憑証已經被使用過了,請重新登入"
30 |
31 | #: lib/casserver/cas.rb:126
32 | msgid "You took too long to enter your credentials. Please try again."
33 | msgstr "你的認證花了太多時間,請再試一次"
34 |
35 | #: lib/casserver/cas.rb:130
36 | msgid ""
37 | "The login ticket you provided is invalid. There may be a problem with the "
38 | "authentication system."
39 | msgstr "你所傳送的登入憑証是無效的,這可能是認證系統的問題"
40 |
41 | #: lib/casserver/controllers.rb:32
42 | msgid ""
43 | "You are currently logged in as '%s'. If this is not you, please log in below."
44 | msgstr "你正以 '%s' 的身分登入。如果這不是你,請重新登入"
45 |
46 | #: lib/casserver/controllers.rb:37
47 | msgid ""
48 | "The client and server are unable to negotiate authentication. Please try "
49 | "logging in again later."
50 | msgstr "現在無法認證,請稍候再嘗試登入"
51 |
52 | #: lib/casserver/controllers.rb:54
53 | msgid ""
54 | "The server cannot fulfill this gateway request because no service parameter "
55 | "was given."
56 | msgstr "無法完成 gateway request 因為沒有提供 service 的參數"
57 |
58 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
59 | msgid ""
60 | "The target service your browser supplied appears to be invalid. Please "
61 | "contact your system administrator for help."
62 | msgstr "你的瀏覽器傳送的服務網址是無效的,請向你的系統管理員尋求協助"
63 |
64 | #: lib/casserver/controllers.rb:88
65 | msgid ""
66 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
67 | "with your request."
68 | msgstr "無法判斷 CAS 的登入網址。請提供 submitToURI 的參數"
69 |
70 | #: lib/casserver/controllers.rb:168
71 | msgid "You have successfully logged in."
72 | msgstr "你成功登入了"
73 |
74 | #: lib/casserver/controllers.rb:184
75 | msgid "Incorrect username or password."
76 | msgstr "你所輸入的帳號或是密碼是錯誤的"
77 |
78 | #: lib/casserver/controllers.rb:267
79 | msgid "You have successfully logged out."
80 | msgstr "你成功登出了"
81 |
82 | #: lib/casserver/controllers.rb:269
83 | msgid " Please click on the following link to continue:"
84 | msgstr " 請點以下的連結繼續"
85 |
86 | #: lib/casserver/controllers.rb:419
87 | msgid "To generate a login ticket, you must make a POST request."
88 | msgstr "你必須使用 POST 來產生登入憑証"
89 |
90 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
91 | msgid " Central Login"
92 | msgstr " 整合登入"
93 |
94 | #: lib/casserver/views.rb:68
95 | msgid "Please wait..."
96 | msgstr "請稍候..."
97 |
98 | #: lib/casserver/views.rb:74
99 | msgid "Username"
100 | msgstr "帳號"
101 |
102 | #: lib/casserver/views.rb:83
103 | msgid "Password"
104 | msgstr "密碼"
105 |
106 | #: lib/casserver/views.rb:92
107 | msgid "Remeber me on this computer"
108 | msgstr "在這台電腦上記住我"
109 |
110 | #: lib/casserver/views.rb:101
111 | msgid "LOGIN"
112 | msgstr "登入"
113 |
114 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/authlogic_crypto_providers/bcrypt.rb:
--------------------------------------------------------------------------------
1 | begin
2 | require "bcrypt"
3 | rescue LoadError
4 | end
5 |
6 | module Authlogic
7 | module CryptoProviders
8 | # For most apps Sha512 is plenty secure, but if you are building an app that stores nuclear launch codes you might want to consier BCrypt. This is an extremely
9 | # secure hashing algorithm, mainly because it is slow. A brute force attack on a BCrypt encrypted password would take much longer than a brute force attack on a
10 | # password encrypted with a Sha algorithm. Keep in mind you are sacrificing performance by using this, generating a password takes exponentially longer than any
11 | # of the Sha algorithms. I did some benchmarking to save you some time with your decision:
12 | #
13 | # require "bcrypt"
14 | # require "digest"
15 | # require "benchmark"
16 | #
17 | # Benchmark.bm(18) do |x|
18 | # x.report("BCrypt (cost = 10:") { 100.times { BCrypt::Password.create("mypass", :cost => 10) } }
19 | # x.report("BCrypt (cost = 2:") { 100.times { BCrypt::Password.create("mypass", :cost => 2) } }
20 | # x.report("Sha512:") { 100.times { Digest::SHA512.hexdigest("mypass") } }
21 | # x.report("Sha1:") { 100.times { Digest::SHA1.hexdigest("mypass") } }
22 | # end
23 | #
24 | # user system total real
25 | # BCrypt (cost = 10): 10.780000 0.060000 10.840000 ( 11.100289)
26 | # BCrypt (cost = 2): 0.180000 0.000000 0.180000 ( 0.181914)
27 | # Sha512: 0.000000 0.000000 0.000000 ( 0.000829)
28 | # Sha1: 0.000000 0.000000 0.000000 ( 0.000395)
29 | #
30 | # You can play around with the cost to get that perfect balance between performance and security.
31 | #
32 | # Decided BCrypt is for you? Just insall the bcrypt gem:
33 | #
34 | # gem install bcrypt-ruby
35 | #
36 | # Tell acts_as_authentic to use it:
37 | #
38 | # acts_as_authentic do |c|
39 | # c.crypto_provider = Authlogic::CryptoProviders::BCrypt
40 | # end
41 | #
42 | # You are good to go!
43 | class BCrypt
44 | class << self
45 | # This is the :cost option for the BCrpyt library. The higher the cost the more secure it is and the longer is take the generate a hash. By default this is 10.
46 | # Set this to whatever you want, play around with it to get that perfect balance between security and performance.
47 | def cost
48 | @cost ||= 10
49 | end
50 | attr_writer :cost
51 |
52 | # Creates a BCrypt hash for the password passed.
53 | def encrypt(*tokens)
54 | ::BCrypt::Password.create(join_tokens(tokens), :cost => cost)
55 | end
56 |
57 | # Does the hash match the tokens? Uses the same tokens that were used to encrypt.
58 | def matches?(hash, *tokens)
59 | $LOG.debug hash
60 | $LOG.debug tokens.inspect
61 |
62 | hash = new_from_hash(hash)
63 | return false if hash.blank?
64 | hash == join_tokens(tokens)
65 | end
66 |
67 | # This method is used as a flag to tell Authlogic to "resave" the password upon a successful login, using the new cost
68 | def cost_matches?(hash)
69 | hash = new_from_hash(hash)
70 | if hash.blank?
71 | false
72 | else
73 | hash.cost == cost
74 | end
75 | end
76 |
77 | private
78 | def join_tokens(tokens)
79 | tokens.flatten.join
80 | end
81 |
82 | def new_from_hash(hash)
83 | begin
84 | ::BCrypt::Password.new(hash)
85 | rescue ::BCrypt::Errors::InvalidHash
86 | return nil
87 | end
88 | end
89 | end
90 | end
91 | end
92 | end
93 |
--------------------------------------------------------------------------------
/po/ru_RU/rubycas-server.po:
--------------------------------------------------------------------------------
1 | # Russian translation for Rubycas server.
2 | # Copyright (C) Matt Zukowski
3 | # This file is distributed under the same license as the rubycas-server package.
4 | # Antono Vasiljev , 2008.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: rubycas-server \n"
9 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
10 | "PO-Revision-Date: 2008-11-04 12:32+0200\n"
11 | "Last-Translator: Antono Vasiljev \n"
12 | "Language-Team: Russian \n"
13 | "MIME-Version: 1.0\n"
14 | "Content-Type: text/plain; charset=UTF-8\n"
15 | "Content-Transfer-Encoding: 8bit\n"
16 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
17 |
18 | #: lib/casserver/cas.rb:117
19 | msgid ""
20 | "Your login request did not include a login ticket. There may be a problem "
21 | "with the authentication system."
22 | msgstr ""
23 |
24 | #: lib/casserver/cas.rb:121
25 | msgid ""
26 | "The login ticket you provided has already been used up. Please try logging "
27 | "in again."
28 | msgstr ""
29 |
30 | #: lib/casserver/cas.rb:126
31 | msgid "You took too long to enter your credentials. Please try again."
32 | msgstr ""
33 |
34 | #: lib/casserver/cas.rb:130
35 | msgid ""
36 | "The login ticket you provided is invalid. There may be a problem with the "
37 | "authentication system."
38 | msgstr ""
39 |
40 | #: lib/casserver/controllers.rb:32
41 | msgid ""
42 | "You are currently logged in as '%s'. If this is not you, please log in below."
43 | msgstr "Вы авторизированы как '%s'."
44 |
45 | #: lib/casserver/controllers.rb:37
46 | msgid ""
47 | "The client and server are unable to negotiate authentication. Please try "
48 | "logging in again later."
49 | msgstr ""
50 | "Клиент и сервер не могут провести проверку прав. Попробуйте войти позже."
51 |
52 | #: lib/casserver/controllers.rb:54
53 | msgid ""
54 | "The server cannot fulfill this gateway request because no service parameter "
55 | "was given."
56 | msgstr ""
57 | "Этот сервер не может выполнить этот запрос, поскольку не были указаны "
58 | "праметры сервиса."
59 |
60 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
61 | msgid ""
62 | "The target service your browser supplied appears to be invalid. Please "
63 | "contact your system administrator for help."
64 | msgstr ""
65 | "Сервис, указанный вашим браузером, неверен. Свяжитесь с вашим системным "
66 | "администратором."
67 |
68 | #: lib/casserver/controllers.rb:88
69 | msgid ""
70 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
71 | "with your request."
72 | msgstr ""
73 | "Невозможно угадать адрес входа на CAS. Пожалуйста, передайте с запросом "
74 | "параметр submitToURI."
75 |
76 | #: lib/casserver/controllers.rb:168
77 | msgid "You have successfully logged in."
78 | msgstr "Вы успешно вошли."
79 |
80 | #: lib/casserver/controllers.rb:184
81 | msgid "Incorrect username or password."
82 | msgstr "Неверное имя пользователя или пароль."
83 |
84 | #: lib/casserver/controllers.rb:267
85 | msgid "You have successfully logged out."
86 | msgstr "Вы успешно вышли."
87 |
88 | #: lib/casserver/controllers.rb:269
89 | msgid " Please click on the following link to continue:"
90 | msgstr " Перейдите по ссылке чтобы продолжить: "
91 |
92 | #: lib/casserver/controllers.rb:419
93 | msgid "To generate a login ticket, you must make a POST request."
94 | msgstr "Чтобы сгенерировать входной билет вы должны делать POST запрос."
95 |
96 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
97 | msgid " Central Login"
98 | msgstr " Центральный вход"
99 |
100 | #: lib/casserver/views.rb:68
101 | msgid "Please wait..."
102 | msgstr "Подождите..."
103 |
104 | #: lib/casserver/views.rb:74
105 | msgid "Username"
106 | msgstr "Логин"
107 |
108 | #: lib/casserver/views.rb:83
109 | msgid "Password"
110 | msgstr "Пароль"
111 |
112 | #: lib/casserver/views.rb:92
113 | msgid "Remeber me on this computer"
114 | msgstr ""
115 |
116 | #: lib/casserver/views.rb:101
117 | msgid "LOGIN"
118 | msgstr "Войти"
119 |
--------------------------------------------------------------------------------
/po/ja_JP/rubycas-server.po:
--------------------------------------------------------------------------------
1 | # Japanese translations for RubyCAS-Server package
2 | # RubyCAS-Server パッケージに対する英訳.
3 | # Copyright (C) 2008 THE RubyCAS-Server'S COPYRIGHT HOLDER
4 | # This file is distributed under the same license as the RubyCAS-Server package.
5 | # Matt Zukowski , 2008.
6 | #
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: rubycas-server \n"
10 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
11 | "PO-Revision-Date: 2008-11-12 13:04-0500\n"
12 | "Last-Translator: Matt Zukowski \n"
13 | "Language-Team: Japanese\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=1; plural=0;\n"
18 |
19 | #: lib/casserver/cas.rb:117
20 | msgid ""
21 | "Your login request did not include a login ticket. There may be a problem "
22 | "with the authentication system."
23 | msgstr ""
24 | "ログインリクエストにログインチケットが含まれていません。認証システムに問題があるようです。"
25 |
26 | #: lib/casserver/cas.rb:121
27 | msgid ""
28 | "The login ticket you provided has already been used up. Please try logging "
29 | "in again."
30 | msgstr ""
31 | "ログインチケットはすでに使い切られています。もう一度ログインしてください。"
32 |
33 | #: lib/casserver/cas.rb:126
34 | msgid "You took too long to enter your credentials. Please try again."
35 | msgstr ""
36 | "クレデンシャルの入力に時間が掛かりすぎました。もう一度試してください。"
37 |
38 | #: lib/casserver/cas.rb:130
39 | msgid ""
40 | "The login ticket you provided is invalid. There may be a problem with the "
41 | "authentication system."
42 | msgstr ""
43 | "指定されたログインチケットが無効です。認証システムに問題があるようです。"
44 |
45 | #: lib/casserver/controllers.rb:32
46 | msgid ""
47 | "You are currently logged in as '%s'. If this is not you, please log in below."
48 | msgstr ""
49 | "'%s'としてログインしています。違うユーザーでログインするには下に入力してくだ"
50 | "さい。"
51 |
52 | #: lib/casserver/controllers.rb:37
53 | msgid ""
54 | "The client and server are unable to negotiate authentication. Please try "
55 | "logging in again later."
56 | msgstr ""
57 | "クライアントとサーバー間で認証ができませんでした。しばらくしてから再度ログイ"
58 | "ンしてください。"
59 |
60 | #: lib/casserver/controllers.rb:54
61 | msgid ""
62 | "The server cannot fulfill this gateway request because no service parameter "
63 | "was given."
64 | msgstr ""
65 | "サービスパラメーターが指定されていないので、サーバーはゲートウェイリクエスト"
66 | "を満たす事ができません。"
67 |
68 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
69 | msgid ""
70 | "The target service your browser supplied appears to be invalid. Please "
71 | "contact your system administrator for help."
72 | msgstr ""
73 | "ブラウザが示す対象サービスは無効のようです。システム管理者に連絡してくださ"
74 | "い。"
75 |
76 | #: lib/casserver/controllers.rb:88
77 | msgid ""
78 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
79 | "with your request."
80 | msgstr ""
81 | "CASのURIを推測することができませんでした。リクエストにsubmitToURIパラメータを"
82 | "指定してください。"
83 |
84 | #: lib/casserver/controllers.rb:168
85 | msgid "You have successfully logged in."
86 | msgstr "ログインしました"
87 |
88 | #: lib/casserver/controllers.rb:184
89 | msgid "Incorrect username or password."
90 | msgstr "ユーザー名またはパスワードが間違っています"
91 |
92 | #: lib/casserver/controllers.rb:267
93 | msgid "You have successfully logged out."
94 | msgstr "ログアウトしました。"
95 |
96 | #: lib/casserver/controllers.rb:269
97 | msgid " Please click on the following link to continue:"
98 | msgstr " 継続するには、以下のリンクをクリックしてください:"
99 |
100 | #: lib/casserver/controllers.rb:419
101 | msgid "To generate a login ticket, you must make a POST request."
102 | msgstr "ログインチケットを発行するには、POSTリクエストを送る必要があります。"
103 |
104 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
105 | msgid " Central Login"
106 | msgstr " 統合ログイン"
107 |
108 | #: lib/casserver/views.rb:68
109 | msgid "Please wait..."
110 | msgstr ""
111 |
112 | #: lib/casserver/views.rb:74
113 | msgid "Username"
114 | msgstr "ユーザー名"
115 |
116 | #: lib/casserver/views.rb:83
117 | msgid "Password"
118 | msgstr "パスワード"
119 |
120 | #: lib/casserver/views.rb:92
121 | msgid "Remeber me on this computer"
122 | msgstr ""
123 |
124 | #: lib/casserver/views.rb:101
125 | msgid "LOGIN"
126 | msgstr "ログイン"
127 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/ntlm.rb:
--------------------------------------------------------------------------------
1 | # THIS AUTHENTICATOR DOES NOT WORK (not even close!)
2 | #
3 | # I started working on this but run into a wall, so I am commiting what I've got
4 | # done and leaving it here with hopes of one day finishing it.
5 | #
6 | # The main problem is that although I've got the Lan Manager/NTLM password hash,
7 | # I'm not sure what to do with it. i.e. I need to check it against the AD or SMB
8 | # server or something... maybe faking an SMB share connection and using the LM
9 | # response for authentication might do the trick?
10 |
11 | require 'casserver/authenticators/base'
12 |
13 | # Ruby/NTLM package from RubyForge
14 | require 'net/ntlm'
15 |
16 | module CASServer
17 | module Authenticators
18 | class NTLM
19 | # This will have to be somehow called by the top of the 'get' method
20 | # in the Login controller (maybe via a hook?)... if this code fails
21 | # then the controller should fall back to some other method of authentication
22 | # (probably AD/LDAP or something).
23 | def filter_for_top_of_login_get_controller_method
24 | $LOG.debug @env.inspect
25 | if @env['HTTP_AUTHORIZATION'] =~ /NTLM ([^\s]+)/
26 | # if we're here, then the client has sent back a Type1 or Type3 message
27 | # in reply to our NTLM challenge or our Type2 message
28 | data_raw = Base64.decode64($~[1])
29 | $LOG.debug "T1 RAW: #{t1_raw}"
30 | t = Net::NTLM::Message::Message.parse(t1_raw)
31 | if t.kind_of? Net::NTLM::Type1
32 | t1 = t
33 | elsif t.kind_of? Net::NTLM::Type3
34 | t3 = t
35 | else
36 | raise "Invalid NTLM reply from client."
37 | end
38 |
39 | if t1
40 | $LOG.debug "T1: #{t1.inspect}"
41 |
42 | # now put together a Type2 message asking for the client to send
43 | # back NTLM credentials (LM hash and such)
44 | t2 = Net::NTLM::Message::Type2.new
45 | t2.set_flag :UNICODE
46 | t2.set_flag :NTLM
47 | t2.context = 0x0000000000000000 # this can probably just be left unassigned
48 | t2.challenge = 0x0123456789abcdef # this should be a random 8-byte integer
49 |
50 | $LOG.debug "T2: #{t2.inspect}"
51 | $LOG.debug "T2: #{t2.serialize}"
52 | headers["WWW-Authenticate"] = "NTLM #{t2.encode64}"
53 |
54 | # the client should respond to this with a Type3 message...
55 | r('401', '', headers)
56 | return
57 | else
58 | # NOTE: for some reason the server never receives the T3 response, even though monitoring
59 | # the HTTP traffic I can see that the client does send it back... there's probably
60 | # another bug hiding somewhere here
61 |
62 | lm_response = t3.lm_response
63 | ntlm_response = t3.ntlm_response
64 | username = t3.user
65 | # this is where we run up against a wall... we need some way to check the lm and/or ntlm
66 | # reponse against the authentication server (probably Active Directory)... maybe a samba
67 | # call would do it?
68 | $LOG.debug "T3 LM: #{lm_response.inspect}"
69 | $LOG.debug "T3 NTLM: #{ntlm_response.inspect}"
70 |
71 | # assuming the authentication was successful, we'll now need to do something in the
72 | # controller acting as if we'd received correct login credentials (i.e. proceed as if
73 | # CAS authentication was successful).... if authentication failed, then we should
74 | # just fall back to old-school web-based authentication, asking the user to enter
75 | # their username and password the normal CAS way
76 | end
77 | else
78 | # this sends the initial NTLM challenge, asking the browser
79 | # to send back a Type1 message
80 | headers['WWW-Authenticate'] = "NTLM"
81 | headers['Connection'] = "Close"
82 | r('401', '', headers)
83 | return
84 | end
85 | end
86 | end
87 | end
88 | end
89 |
--------------------------------------------------------------------------------
/po/pt_BR/rubycas-server.po:
--------------------------------------------------------------------------------
1 | # Brazilian Portuguese translations for RubyCAS-Server package
2 | # Traduções em Português do Brasil para o pacote RubyCAS-Server.
3 | # Copyright (C) 2008 THE RubyCAS-Server'S COPYRIGHT HOLDER
4 | # This file is distributed under the same license as the RubyCAS-Server package.
5 | # Kivanio Barbosa , 2009.
6 | #
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: rubycas-server \n"
10 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
11 | "PO-Revision-Date: 2009-03-17 20:55+0200\n"
12 | "Last-Translator: Kivanio Barbosa \n"
13 | "Language-Team: Brazil\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n"
18 |
19 | #: lib/casserver/cas.rb:117
20 | msgid ""
21 | "Your login request did not include a login ticket. There may be a problem "
22 | "with the authentication system."
23 | msgstr ""
24 |
25 | #: lib/casserver/cas.rb:121
26 | msgid ""
27 | "The login ticket you provided has already been used up. Please try logging "
28 | "in again."
29 | msgstr ""
30 |
31 | #: lib/casserver/cas.rb:126
32 | msgid "You took too long to enter your credentials. Please try again."
33 | msgstr ""
34 |
35 | #: lib/casserver/cas.rb:130
36 | msgid ""
37 | "The login ticket you provided is invalid. There may be a problem with the "
38 | "authentication system."
39 | msgstr ""
40 |
41 | #: lib/casserver/controllers.rb:32
42 | msgid ""
43 | "You are currently logged in as '%s'. If this is not you, please log in below."
44 | msgstr ""
45 | "Você está logado como '%s'. Se este não for você, Por favor, faça o login a "
46 | "baixo."
47 |
48 | #: lib/casserver/controllers.rb:37
49 | msgid ""
50 | "The client and server are unable to negotiate authentication. Please try "
51 | "logging in again later."
52 | msgstr ""
53 | "O cliente e o servidor não puderam efetuar a autenticação. Por favor, tente "
54 | "novamente mais tarde."
55 |
56 | #: lib/casserver/controllers.rb:54
57 | msgid ""
58 | "The server cannot fulfill this gateway request because no service parameter "
59 | "was given."
60 | msgstr ""
61 | "O servidor não pode completar a solicitação porque não foi enviado o "
62 | "paramêtro do serviço."
63 |
64 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
65 | msgid ""
66 | "The target service your browser supplied appears to be invalid. Please "
67 | "contact your system administrator for help."
68 | msgstr ""
69 | "O seu navegador está aparentemente com problemas. Por favor, contate o "
70 | "administrador do sistema para obter ajuda."
71 |
72 | #: lib/casserver/controllers.rb:88
73 | msgid ""
74 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
75 | "with your request."
76 | msgstr ""
77 | "Não encontramos a URI de acesso ao CAS. Por favor, informe corretamente no "
78 | "submitToURI com sua solicitação."
79 |
80 | #: lib/casserver/controllers.rb:168
81 | msgid "You have successfully logged in."
82 | msgstr "Login efetuado com sucesso."
83 |
84 | #: lib/casserver/controllers.rb:184
85 | msgid "Incorrect username or password."
86 | msgstr "Usuário ou Senha está incorreto."
87 |
88 | #: lib/casserver/controllers.rb:267
89 | msgid "You have successfully logged out."
90 | msgstr "Você saiu do sistema com sucesso."
91 |
92 | #: lib/casserver/controllers.rb:269
93 | msgid " Please click on the following link to continue:"
94 | msgstr " Por favor, clique no seguinte link para continuar:"
95 |
96 | #: lib/casserver/controllers.rb:419
97 | msgid "To generate a login ticket, you must make a POST request."
98 | msgstr ""
99 | "Para gerar um ticket de acceso, você deve fazer uma requisição via POST."
100 |
101 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
102 | msgid " Central Login"
103 | msgstr " Central de Autenticação"
104 |
105 | #: lib/casserver/views.rb:68
106 | msgid "Please wait..."
107 | msgstr ""
108 |
109 | #: lib/casserver/views.rb:74
110 | msgid "Username"
111 | msgstr "Usuário"
112 |
113 | #: lib/casserver/views.rb:83
114 | msgid "Password"
115 | msgstr "Senha"
116 |
117 | #: lib/casserver/views.rb:92
118 | msgid "Remeber me on this computer"
119 | msgstr ""
120 |
121 | #: lib/casserver/views.rb:101
122 | msgid "LOGIN"
123 | msgstr "ENTRAR"
124 |
--------------------------------------------------------------------------------
/po/pl_PL/rubycas-server.po:
--------------------------------------------------------------------------------
1 | # Polish translations for RubyCAS-Server package
2 | # Polskie tłumaczenia dla pakietu RubyCAS-Server.
3 | # Copyright (C) 2008 THE RubyCAS-Server'S COPYRIGHT HOLDER
4 | # This file is distributed under the same license as the RubyCAS-Server package.
5 | # Matt Zukowski , 2008.
6 | #
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: rubycas-server \n"
10 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
11 | "PO-Revision-Date: 2008-11-12 11:03-0500\n"
12 | "Last-Translator: Matt Zukowski \n"
13 | "Language-Team: Polish\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
18 | "|| n%100>=20) ? 1 : 2);\n"
19 |
20 | #: lib/casserver/cas.rb:117
21 | msgid ""
22 | "Your login request did not include a login ticket. There may be a problem "
23 | "with the authentication system."
24 | msgstr ""
25 |
26 | #: lib/casserver/cas.rb:121
27 | msgid ""
28 | "The login ticket you provided has already been used up. Please try logging "
29 | "in again."
30 | msgstr ""
31 |
32 | #: lib/casserver/cas.rb:126
33 | msgid "You took too long to enter your credentials. Please try again."
34 | msgstr ""
35 |
36 | #: lib/casserver/cas.rb:130
37 | msgid ""
38 | "The login ticket you provided is invalid. There may be a problem with the "
39 | "authentication system."
40 | msgstr ""
41 |
42 | #: lib/casserver/controllers.rb:32
43 | msgid ""
44 | "You are currently logged in as '%s'. If this is not you, please log in below."
45 | msgstr ""
46 | "Jesteś aktualnie zalogowany jako '%s'. Jeżeli to nie jesteś ty, zaloguj się "
47 | "tutaj."
48 |
49 | #: lib/casserver/controllers.rb:37
50 | msgid ""
51 | "The client and server are unable to negotiate authentication. Please try "
52 | "logging in again later."
53 | msgstr ""
54 | "Klient i serwer nie są w stanie negocjować uwierzytelniania. Proszę "
55 | "spróbować zalogować się ponownie później."
56 |
57 | #: lib/casserver/controllers.rb:54
58 | msgid ""
59 | "The server cannot fulfill this gateway request because no service parameter "
60 | "was given."
61 | msgstr ""
62 | "Serwer nie może spełnić tego żądania bramowego, ponieważ nie został podany "
63 | "parametr usługi."
64 |
65 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
66 | msgid ""
67 | "The target service your browser supplied appears to be invalid. Please "
68 | "contact your system administrator for help."
69 | msgstr ""
70 | "Podany adresat usługi wydaje się nieprawidłowy. Skontaktuj się z "
71 | "administratorem systemu żeby uzyskać pomocy."
72 |
73 | #: lib/casserver/controllers.rb:88
74 | msgid ""
75 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
76 | "with your request."
77 | msgstr ""
78 | "Nie można odgadnąć URI do logowania do CAS. Proszę dostarczyć parametr "
79 | "submitToURI."
80 |
81 | #: lib/casserver/controllers.rb:168
82 | msgid "You have successfully logged in."
83 | msgstr "Jesteś zalogowany."
84 |
85 | #: lib/casserver/controllers.rb:184
86 | msgid "Incorrect username or password."
87 | msgstr "Niepoprawny użytkownik lub hasło."
88 |
89 | #: lib/casserver/controllers.rb:267
90 | msgid "You have successfully logged out."
91 | msgstr "Jesteś wylogowany."
92 |
93 | #: lib/casserver/controllers.rb:269
94 | msgid " Please click on the following link to continue:"
95 | msgstr " Proszę kliknąć na poniższy link, aby kontynuować:"
96 |
97 | #: lib/casserver/controllers.rb:419
98 | msgid "To generate a login ticket, you must make a POST request."
99 | msgstr "Aby wygenerować login bilet, musisz złożyć żądanie POST."
100 |
101 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
102 | msgid " Central Login"
103 | msgstr " Centralna Usługa Uwierzytelniania"
104 |
105 | #: lib/casserver/views.rb:68
106 | msgid "Please wait..."
107 | msgstr "Chwileczke..."
108 |
109 | #: lib/casserver/views.rb:74
110 | msgid "Username"
111 | msgstr "Użytkownik"
112 |
113 | #: lib/casserver/views.rb:83
114 | msgid "Password"
115 | msgstr "Hasło"
116 |
117 | #: lib/casserver/views.rb:92
118 | msgid "Remeber me on this computer"
119 | msgstr ""
120 |
121 | #: lib/casserver/views.rb:101
122 | msgid "LOGIN"
123 | msgstr "ZALOGUJ"
124 |
--------------------------------------------------------------------------------
/po/es_ES/rubycas-server.po:
--------------------------------------------------------------------------------
1 | # Spanish translations for RubyCAS-Server package
2 | # Traducciones al espa ol para el paquete RubyCAS-Server.
3 | # Copyright (C) 2008 THE RubyCAS-Server'S COPYRIGHT HOLDER
4 | # This file is distributed under the same license as the RubyCAS-Server package.
5 | # Matt Zukowski , 2008.
6 | #
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: rubycas-server \n"
10 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
11 | "PO-Revision-Date: 2008-11-12 12:30-0500\n"
12 | "Last-Translator: Matt Zukowski \n"
13 | "Language-Team: Spanish\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
18 |
19 | #: lib/casserver/cas.rb:117
20 | msgid ""
21 | "Your login request did not include a login ticket. There may be a problem "
22 | "with the authentication system."
23 | msgstr ""
24 |
25 | #: lib/casserver/cas.rb:121
26 | msgid ""
27 | "The login ticket you provided has already been used up. Please try logging "
28 | "in again."
29 | msgstr ""
30 |
31 | #: lib/casserver/cas.rb:126
32 | msgid "You took too long to enter your credentials. Please try again."
33 | msgstr ""
34 |
35 | #: lib/casserver/cas.rb:130
36 | msgid ""
37 | "The login ticket you provided is invalid. There may be a problem with the "
38 | "authentication system."
39 | msgstr ""
40 |
41 | #: lib/casserver/controllers.rb:32
42 | msgid ""
43 | "You are currently logged in as '%s'. If this is not you, please log in below."
44 | msgstr ""
45 | "Usted está conectado como '%s'. Si no lo es usted, por favor, acceda a "
46 | "continuación."
47 |
48 | #: lib/casserver/controllers.rb:37
49 | msgid ""
50 | "The client and server are unable to negotiate authentication. Please try "
51 | "logging in again later."
52 | msgstr ""
53 | "El cliente y el servidor no están en condiciones de negociar la "
54 | "autenticación. Por favor, intente acceder de nuevo más tarde."
55 |
56 | #: lib/casserver/controllers.rb:54
57 | msgid ""
58 | "The server cannot fulfill this gateway request because no service parameter "
59 | "was given."
60 | msgstr ""
61 | "El servidor no puede cumplir con esta petición, porque no fue parámetro de "
62 | "servicio prestado."
63 |
64 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
65 | msgid ""
66 | "The target service your browser supplied appears to be invalid. Please "
67 | "contact your system administrator for help."
68 | msgstr ""
69 | "El objetivo de su navegador de servicios ofrecidos parece ser nula. Por "
70 | "favor, póngase en contacto con el administrador del sistema para obtener "
71 | "ayuda."
72 |
73 | #: lib/casserver/controllers.rb:88
74 | msgid ""
75 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
76 | "with your request."
77 | msgstr ""
78 | "No podía adivinar el URI de acceso CAS. Suministro submitToURI un parámetro "
79 | "con su solicitud."
80 |
81 | #: lib/casserver/controllers.rb:168
82 | msgid "You have successfully logged in."
83 | msgstr "Inicio de sesión satisfactorio."
84 |
85 | #: lib/casserver/controllers.rb:184
86 | msgid "Incorrect username or password."
87 | msgstr "Incorrecto nombre de usuario o contraseña."
88 |
89 | #: lib/casserver/controllers.rb:267
90 | msgid "You have successfully logged out."
91 | msgstr "Cierre de sesión satisfactorio."
92 |
93 | #: lib/casserver/controllers.rb:269
94 | msgid " Please click on the following link to continue:"
95 | msgstr " Por favor, haga clic en el vínculo siguiente para continuar:"
96 |
97 | #: lib/casserver/controllers.rb:419
98 | msgid "To generate a login ticket, you must make a POST request."
99 | msgstr "Para generar un ticket de acceso, usted debe hacer una petición POST."
100 |
101 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
102 | msgid " Central Login"
103 | msgstr " Servicio de Autenticación Central"
104 |
105 | #: lib/casserver/views.rb:68
106 | msgid "Please wait..."
107 | msgstr ""
108 |
109 | #: lib/casserver/views.rb:74
110 | msgid "Username"
111 | msgstr "Usuario"
112 |
113 | #: lib/casserver/views.rb:83
114 | msgid "Password"
115 | msgstr "Contraseña"
116 |
117 | #: lib/casserver/views.rb:92
118 | msgid "Remeber me on this computer"
119 | msgstr ""
120 |
121 | #: lib/casserver/views.rb:101
122 | msgid "LOGIN"
123 | msgstr "INICIAR SESIÓN"
124 |
--------------------------------------------------------------------------------
/po/de_DE/rubycas-server.po:
--------------------------------------------------------------------------------
1 | # German translations for RubyCAS-Server package
2 | # German messages for RubyCAS-Server.
3 | # Copyright (C) 2008 THE RubyCAS-Server'S COPYRIGHT HOLDER
4 | # This file is distributed under the same license as the RubyCAS-Server package.
5 | # Matt Zukowski , 2008.
6 | #
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: rubycas-server \n"
10 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
11 | "PO-Revision-Date: 2008-11-12 12:49-0500\n"
12 | "Last-Translator: Matt Zukowski \n"
13 | "Language-Team: German\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
18 |
19 | #: lib/casserver/cas.rb:117
20 | msgid ""
21 | "Your login request did not include a login ticket. There may be a problem "
22 | "with the authentication system."
23 | msgstr ""
24 |
25 | #: lib/casserver/cas.rb:121
26 | msgid ""
27 | "The login ticket you provided has already been used up. Please try logging "
28 | "in again."
29 | msgstr ""
30 |
31 | #: lib/casserver/cas.rb:126
32 | msgid "You took too long to enter your credentials. Please try again."
33 | msgstr ""
34 |
35 | #: lib/casserver/cas.rb:130
36 | msgid ""
37 | "The login ticket you provided is invalid. There may be a problem with the "
38 | "authentication system."
39 | msgstr ""
40 |
41 | #: lib/casserver/controllers.rb:32
42 | msgid ""
43 | "You are currently logged in as '%s'. If this is not you, please log in below."
44 | msgstr ""
45 | "Sie sind derzeit angemeldet als '%s'. Sollten dies nicht Sie sein, melden "
46 | "Sie sich bitte unten an."
47 |
48 | #: lib/casserver/controllers.rb:37
49 | msgid ""
50 | "The client and server are unable to negotiate authentication. Please try "
51 | "logging in again later."
52 | msgstr ""
53 | "Client und Server sind nicht in der Lage eine Authentifizierung "
54 | "auszuhandeln. Bitte versuchen Sie, sich zu einem späteren Zeitpunkt erneut "
55 | "anzumelden."
56 |
57 | #: lib/casserver/controllers.rb:54
58 | msgid ""
59 | "The server cannot fulfill this gateway request because no service parameter "
60 | "was given."
61 | msgstr ""
62 | "Der Server kann diese Gateway-Anfrage nicht erfüllen, da keine Service-"
63 | "Parameter übergeben wurden."
64 |
65 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
66 | msgid ""
67 | "The target service your browser supplied appears to be invalid. Please "
68 | "contact your system administrator for help."
69 | msgstr ""
70 | "Das Ziel-Service, welches Ihr Browsers geliefert hat, scheint ungültig zu "
71 | "sein. Bitte wenden Sie sich an Ihren Systemadministrator, um Hilfe zu "
72 | "erhalten."
73 |
74 | #: lib/casserver/controllers.rb:88
75 | msgid ""
76 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
77 | "with your request."
78 | msgstr ""
79 | "Der CAS-Login-URI konnte nicht erraten werden. Bitte ergänzen Sie Ihre "
80 | "Anfrage um einen submitToURI Parameter."
81 |
82 | #: lib/casserver/controllers.rb:168
83 | msgid "You have successfully logged in."
84 | msgstr ""
85 | "Sie haben sich erfolgreich am Central Authentication Service angemeldet."
86 |
87 | #: lib/casserver/controllers.rb:184
88 | msgid "Incorrect username or password."
89 | msgstr "Falscher Benutzername oder Passwort."
90 |
91 | #: lib/casserver/controllers.rb:267
92 | msgid "You have successfully logged out."
93 | msgstr ""
94 | "Sie haben sich erfolgreich vom Central Authentication Service abgemeldet."
95 |
96 | #: lib/casserver/controllers.rb:269
97 | msgid " Please click on the following link to continue:"
98 | msgstr " Bitte klicken Sie auf den folgenden Link, um fortzufahren:"
99 |
100 | #: lib/casserver/controllers.rb:419
101 | msgid "To generate a login ticket, you must make a POST request."
102 | msgstr ""
103 | "Für die Generierung eines Login-Tickets, ist eine POST-Anfrage erforderlich."
104 |
105 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
106 | msgid " Central Login"
107 | msgstr " Zentrales Login"
108 |
109 | #: lib/casserver/views.rb:68
110 | msgid "Please wait..."
111 | msgstr ""
112 |
113 | #: lib/casserver/views.rb:74
114 | msgid "Username"
115 | msgstr "Benutzername"
116 |
117 | #: lib/casserver/views.rb:83
118 | msgid "Password"
119 | msgstr "Passwort"
120 |
121 | #: lib/casserver/views.rb:92
122 | msgid "Remeber me on this computer"
123 | msgstr ""
124 |
125 | #: lib/casserver/views.rb:101
126 | msgid "LOGIN"
127 | msgstr "ANMELDEN"
128 |
--------------------------------------------------------------------------------
/spec/authenticators/active_resource_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require File.dirname(__FILE__) + '/../spec_helper'
3 |
4 | require 'casserver/authenticators/active_resource'
5 |
6 | describe CASServer::Authenticators::Helpers::Identity do
7 |
8 | it { should be_an ActiveResource::Base }
9 |
10 | it "class should respond to :authenticate" do
11 | subject.class.should respond_to :authenticate
12 | end
13 |
14 | it "class should have a method_name accessor" do
15 | CASServer::Authenticators::Helpers::Identity.method_name.should == :authenticate
16 | end
17 |
18 | it "class should have a method_name accessor" do
19 | CASServer::Authenticators::Helpers::Identity.method_type.should == :post
20 | end
21 |
22 | it "class method_type accessor should validate type" do
23 | expect {
24 | CASServer::Authenticators::Helpers::Identity.method_type = :foo
25 | }.to raise_error(ArgumentError)
26 | end
27 |
28 | end
29 |
30 | describe CASServer::Authenticators::ActiveResource do
31 |
32 | describe "#setup" do
33 |
34 | it "should configure the identity object" do
35 | CASServer::Authenticators::Helpers::Identity.should_receive(:user=).with('httpuser').once
36 | CASServer::Authenticators::ActiveResource.setup :site => 'http://api.example.org', :user => 'httpuser'
37 | end
38 |
39 | it "should configure the method_type" do
40 | CASServer::Authenticators::Helpers::Identity.should_receive(:method_type=).with('get').once
41 | CASServer::Authenticators::ActiveResource.setup :site => 'http://api.example.org', :method_type => 'get'
42 | end
43 |
44 | it "should raise if site option is missing" do
45 | expect {
46 | CASServer::Authenticators::ActiveResource.setup({}).should
47 | }.to raise_error(CASServer::AuthenticatorError, /site option/)
48 | end
49 | end
50 |
51 | describe "#validate" do
52 |
53 | let(:credentials) { {:username => 'validusername',
54 | :password => 'validpassword',
55 | :service => 'test.service'} }
56 |
57 | let(:auth) { CASServer::Authenticators::ActiveResource.new }
58 |
59 | def mock_authenticate identity = nil
60 | identity = CASServer::Authenticators::Helpers::Identity.new if identity.nil?
61 | CASServer::Authenticators::Helpers::Identity.stub!(:authenticate).and_return(identity)
62 | end
63 |
64 | def sample_identity attrs = {}
65 | identity = CASServer::Authenticators::Helpers::Identity.new
66 | attrs.each { |k,v| identity.send "#{k}=", v }
67 | identity
68 | end
69 |
70 | it "should call Identity#autenticate with the given params" do
71 | CASServer::Authenticators::Helpers::Identity.should_receive(:authenticate).with(credentials).once
72 | auth.validate(credentials)
73 | end
74 |
75 | it "should return identity object attributes as extra attributes" do
76 | auth.configure({}.with_indifferent_access)
77 | identity = sample_identity({:email => 'foo@example.org'})
78 | mock_authenticate identity
79 | auth.validate(credentials).should be_true
80 | auth.extra_attributes.should == identity.attributes
81 | end
82 |
83 | it "should return false when http raises" do
84 | CASServer::Authenticators::Helpers::Identity.stub!(:authenticate).and_raise(ActiveResource::ForbiddenAccess.new({}))
85 | auth.validate(credentials).should be_false
86 | end
87 |
88 | it "should apply extra_attribute filter" do
89 | auth.configure({ :extra_attributes => 'age'}.with_indifferent_access)
90 | mock_authenticate sample_identity({ :email => 'foo@example.org', :age => 28 })
91 | auth.validate(credentials).should be_true
92 | auth.extra_attributes.should == { "age" => "28" }
93 | end
94 |
95 | it "should only extract not filtered attributes" do
96 | auth.configure({ :filter_attributes => 'age'}.with_indifferent_access)
97 | mock_authenticate sample_identity({ :email => 'foo@example.org', :age => 28 })
98 | auth.validate(credentials).should be_true
99 | auth.extra_attributes.should == { "email" => 'foo@example.org' }
100 | end
101 |
102 | it "should filter password if filter attributes is not given" do
103 | auth.configure({}.with_indifferent_access)
104 | mock_authenticate sample_identity({ :email => 'foo@example.org', :password => 'secret' })
105 | auth.validate(credentials).should be_true
106 | auth.extra_attributes.should == { "email" => 'foo@example.org' }
107 | end
108 | end
109 | end
110 |
--------------------------------------------------------------------------------
/po/fr_FR/rubycas-server.po:
--------------------------------------------------------------------------------
1 | # French translations for RubyCAS-Server package
2 | # Traductions françaises du paquet RubyCAS-Server.
3 | # Copyright (C) 2008 THE RubyCAS-Server'S COPYRIGHT HOLDER
4 | # This file is distributed under the same license as the RubyCAS-Server package.
5 | # Matt Zukowski , 2008.
6 | #
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: rubycas-server \n"
10 | "POT-Creation-Date: 2009-09-29 17:04+0800\n"
11 | "PO-Revision-Date: 2008-11-12 11:53-0500\n"
12 | "Last-Translator: Matt Zukowski \n"
13 | "Language-Team: French\n"
14 | "MIME-Version: 1.0\n"
15 | "Content-Type: text/plain; charset=UTF-8\n"
16 | "Content-Transfer-Encoding: 8bit\n"
17 | "Plural-Forms: nplurals=2; plural=(n > 1);\n"
18 |
19 | #: lib/casserver/cas.rb:117
20 | msgid ""
21 | "Your login request did not include a login ticket. There may be a problem "
22 | "with the authentication system."
23 | msgstr ""
24 | "Votre requête d'identification n'a pas inclus de ticket d'identification. Il se peut qu'il y ait un problème "
25 | "avec le système d'identification."
26 |
27 | #: lib/casserver/cas.rb:121
28 | msgid ""
29 | "The login ticket you provided has already been used up. Please try logging "
30 | "in again."
31 | msgstr ""
32 | "Le ticket d'identification que vous avez fourni a été consommé. Veuillez essayer "
33 | " de vous reconnecter"
34 |
35 | #: lib/casserver/cas.rb:126
36 | msgid "You took too long to enter your credentials. Please try again."
37 | msgstr "Le délai de saisie de vos login et mot de passe a expiré. Veuillez réessayer."
38 |
39 | #: lib/casserver/cas.rb:130
40 | msgid ""
41 | "The login ticket you provided is invalid. There may be a problem with the "
42 | "authentication system."
43 | msgstr ""
44 | "Le ticket d'identification que vous avez fourni n'est pas valide. Il se peut qu'il y ait "
45 | "un problème avec le système d'identification."
46 |
47 | #: lib/casserver/controllers.rb:32
48 | msgid ""
49 | "You are currently logged in as '%s'. If this is not you, please log in below."
50 | msgstr ""
51 | "Vous êtes actuellement connecté en tant que '%s'. Si ce n'est pas vous, "
52 | "veuillez vous connecter ci-dessous."
53 |
54 | #: lib/casserver/controllers.rb:37
55 | msgid ""
56 | "The client and server are unable to negotiate authentication. Please try "
57 | "logging in again later."
58 | msgstr ""
59 | "Le client et le serveur ne peuvent négocier l'identification. "
60 | "Veuillez réessayer ultérieurement."
61 |
62 | #: lib/casserver/controllers.rb:54
63 | msgid ""
64 | "The server cannot fulfill this gateway request because no service parameter "
65 | "was given."
66 | msgstr ""
67 | "Le serveur ne peut pas répondre à cette demande (aucun paramètre de service "
68 | "n'a été donné)."
69 |
70 | #: lib/casserver/controllers.rb:59 lib/casserver/controllers.rb:179
71 | msgid ""
72 | "The target service your browser supplied appears to be invalid. Please "
73 | "contact your system administrator for help."
74 | msgstr ""
75 | "Le service cible que votre navigateur a fourni semble incorrect. "
76 | "Veuillez contacter votre administrateur système pour assistance."
77 |
78 | #: lib/casserver/controllers.rb:88
79 | msgid ""
80 | "Could not guess the CAS login URI. Please supply a submitToURI parameter "
81 | "with your request."
82 | msgstr ""
83 | "Impossible de trouver l'URI de connection de CAS. Veuillez fournir le paramètre "
84 | "submitToURI avec votre requête."
85 |
86 | #: lib/casserver/controllers.rb:168
87 | msgid "You have successfully logged in."
88 | msgstr "Vous vous êtes authentifié(e) auprès du Service Central d'Identification."
89 |
90 | #: lib/casserver/controllers.rb:184
91 | msgid "Incorrect username or password."
92 | msgstr "Les informations transmises n'ont pas permis de vous authentifier"
93 |
94 | #: lib/casserver/controllers.rb:267
95 | msgid "You have successfully logged out."
96 | msgstr "Vous vous êtes déconnecté(e) du Service Central d'Identification."
97 |
98 | #: lib/casserver/controllers.rb:269
99 | msgid " Please click on the following link to continue:"
100 | msgstr " S'il vous plaît cliquer sur le lien suivant pour continuer:"
101 |
102 | #: lib/casserver/controllers.rb:419
103 | msgid "To generate a login ticket, you must make a POST request."
104 | msgstr "Pour générer un ticket de connexion, vous devez faire une requête POST."
105 |
106 | #: lib/casserver/views.rb:43 lib/casserver/views.rb:120
107 | msgid " Central Login"
108 | msgstr " Service Central d'Identification."
109 |
110 | #: lib/casserver/views.rb:68
111 | msgid "Please wait..."
112 | msgstr "Veuillez patienter..."
113 |
114 | #: lib/casserver/views.rb:74
115 | msgid "Username"
116 | msgstr "Identifiant"
117 |
118 | #: lib/casserver/views.rb:83
119 | msgid "Password"
120 | msgstr "Mot de passe"
121 |
122 | #: lib/casserver/views.rb:92
123 | msgid "Remeber me on this computer"
124 | msgstr "Se souvenir de moi"
125 |
126 | #: lib/casserver/views.rb:101
127 | msgid "LOGIN"
128 | msgstr "SE CONNECTER"
129 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/active_resource.rb:
--------------------------------------------------------------------------------
1 | require 'casserver/authenticators/base'
2 |
3 | begin
4 | require 'active_resource'
5 | rescue LoadError
6 | require 'rubygems'
7 | begin
8 | gem 'activeresource', '~> 3.0.0'
9 | rescue Gem::LoadError
10 | $stderr.puts
11 | $stderr.puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
12 | $stderr.puts
13 | $stderr.puts "To use the ActiveResource authenticator, you must first install the 'activeresource' gem."
14 | $stderr.puts
15 | $stderr.puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
16 | exit 1
17 | end
18 | require 'active_resource'
19 | end
20 |
21 | module CASServer
22 | module Authenticators
23 |
24 | module Helpers
25 | class Identity < ActiveResource::Base
26 |
27 | # define method_name accessor
28 | cattr_accessor(:method_name)
29 | self.method_name = :authenticate
30 |
31 | def self.method_type
32 | @@method_type ||= :post
33 | end
34 |
35 | def self.method_type= type
36 | methods = [:get, :post, :put, :delete]
37 | raise ArgumentError, "Method type should be one of #{methods.map { |m| m.to_s.upcase }.join(', ')}" unless methods.include? type.to_sym
38 | @@method_type = type
39 | end
40 |
41 | # Autenticate an identity using the given method
42 | # @param [Hash] credentials
43 | def self.authenticate(credentials = {})
44 | response = send(method_type, method_name, credentials)
45 | new.from_authentication_data(response)
46 | end
47 |
48 | # Used to load object attributes from the given response
49 | def from_authentication_data response
50 | load_attributes_from_response(response)
51 | end
52 | end
53 | end
54 |
55 | class ActiveResource < Base
56 |
57 | # This is called at server startup.
58 | # Any class-wide initializiation for the authenticator should be done here.
59 | # (e.g. establish database connection).
60 | # You can leave this empty if you don't need to set up anything.
61 | def self.setup(options)
62 | raise AuthenticatorError, 'You must define at least site option' unless options[:site]
63 | # apply options to active resource object
64 | options.each do |method, arg|
65 | Helpers::Identity.send "#{method}=", arg if Helpers::Identity.respond_to? "#{method}="
66 | end
67 | $LOG.info "ActiveResource configuration loaded"
68 | end
69 |
70 | # Override this to implement your authentication credential validation.
71 | # This is called each time the user tries to log in. The credentials hash
72 | # holds the credentials as entered by the user (generally under :username
73 | # and :password keys; :service and :request are also included by default)
74 | #
75 | # Note that the standard credentials can be read in to instance variables
76 | # by calling #read_standard_credentials.
77 | def validate(credentials)
78 | begin
79 | $LOG.debug("Starting Active Resource authentication")
80 | result = Helpers::Identity.authenticate(credentials.except(:request))
81 | extract_extra_attributes(result) if result
82 | !!result
83 | rescue ::ActiveResource::ConnectionError => e
84 | if e.response.blank? # band-aid for ARes 2.3.x -- craps out if to_s is called without a response
85 | e = e.class.to_s
86 | end
87 | $LOG.warn("Error during authentication: #{e}")
88 | false
89 | end
90 | end
91 |
92 | private
93 |
94 | def extract_extra_attributes(resource)
95 | @extra_attributes = {}
96 | $LOG.debug("Parsing extra attributes")
97 | if @options[:extra_attributes]
98 | extra_attributes_to_extract.each do |attr|
99 | @extra_attributes[attr] = resource.send(attr).to_s
100 | end
101 | else
102 | @extra_attributes = resource.attributes
103 | end
104 | # do filtering
105 | extra_attributes_to_filter.each do |attr|
106 | @extra_attributes.delete(attr)
107 | end
108 | end
109 |
110 | # extract attributes to filter from the given configuration
111 | def extra_attributes_to_filter
112 | # default value if not set
113 | return ['password'] unless @options[:filter_attributes]
114 | # parse option value
115 | if @options[:filter_attributes].kind_of? Array
116 | attrs = @options[:filter_attributes]
117 | elsif @options[:filter_attributes].kind_of? String
118 | attrs = @options[:filter_attributes].split(',').collect { |col| col.strip }
119 | else
120 | $LOG.error("Can't figure out attribute list from #{@options[:filter_attributes].inspect}. This must be an Aarray of column names or a comma-separated list.")
121 | attrs = []
122 | end
123 | attrs
124 | end
125 | end
126 | end
127 | end
128 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/sql.rb:
--------------------------------------------------------------------------------
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 | # Authenticates against a plain SQL table.
11 | #
12 | # This assumes that all of your users are stored in a table that has a 'username'
13 | # column and a 'password' column. When the user logs in, CAS conects to the
14 | # database and looks for a matching username/password in the users table. If a
15 | # matching username and password is found, authentication is successful.
16 | #
17 | # Any database backend supported by ActiveRecord can be used.
18 | #
19 | # Config example:
20 | #
21 | # authenticator:
22 | # class: CASServer::Authenticators::SQL
23 | # database:
24 | # adapter: mysql
25 | # database: some_database_with_users_table
26 | # username: root
27 | # password:
28 | # server: localhost
29 | # user_table: users
30 | # username_column: username
31 | # password_column: password
32 | #
33 | # When replying to a CAS client's validation request, the server will normally
34 | # provide the client with the authenticated user's username. However it is now
35 | # possible for the server to provide the client with additional attributes.
36 | # You can configure the SQL authenticator to provide data from additional
37 | # columns in the users table by listing the names of the columns under the
38 | # 'extra_attributes' option. Note though that this functionality is experimental.
39 | # It should work with RubyCAS-Client, but may or may not work with other CAS
40 | # clients.
41 | #
42 | # For example, with this configuration, the 'full_name' and 'access_level'
43 | # columns will be provided to your CAS clients along with the username:
44 | #
45 | # authenticator:
46 | # class: CASServer::Authenticators::SQL
47 | # database:
48 | # adapter: mysql
49 | # database: some_database_with_users_table
50 | # user_table: users
51 | # username_column: username
52 | # password_column: password
53 | # ignore_type_column: true # indicates if you want to ignore Single Table Inheritance 'type' field
54 | # extra_attributes: full_name, access_level
55 | #
56 | class CASServer::Authenticators::SQL < CASServer::Authenticators::Base
57 | def self.setup(options)
58 | raise CASServer::AuthenticatorError, "Invalid authenticator configuration!" unless options[:database]
59 |
60 | user_model_name = "CASUser_#{options[:auth_index]}"
61 | $LOG.debug "CREATING USER MODEL #{user_model_name}"
62 |
63 | class_eval %{
64 | class #{user_model_name} < ActiveRecord::Base
65 | end
66 | }
67 |
68 | @user_model = const_get(user_model_name)
69 | @user_model.establish_connection(options[:database])
70 | @user_model.set_table_name(options[:user_table] || 'users')
71 | @user_model.inheritance_column = 'no_inheritance_column' if options[:ignore_type_column]
72 | end
73 |
74 | def self.user_model
75 | @user_model
76 | end
77 |
78 | def validate(credentials)
79 | read_standard_credentials(credentials)
80 | raise_if_not_configured
81 |
82 | user_model = self.class.user_model
83 |
84 | username_column = @options[:username_column] || 'username'
85 | password_column = @options[:password_column] || 'password'
86 |
87 | $LOG.debug "#{self.class}: [#{user_model}] " + "Connection pool size: #{user_model.connection_pool.instance_variable_get(:@checked_out).length}/#{user_model.connection_pool.instance_variable_get(:@connections).length}"
88 | results = user_model.find(:all, :conditions => ["#{username_column} = ? AND #{password_column} = ?", @username, @password])
89 | user_model.connection_pool.checkin(user_model.connection)
90 |
91 | if results.size > 0
92 | $LOG.warn("#{self.class}: Multiple matches found for user #{@username.inspect}") if results.size > 1
93 |
94 | unless @options[:extra_attributes].blank?
95 | if results.size > 1
96 | $LOG.warn("#{self.class}: Unable to extract extra_attributes because multiple matches were found for #{@username.inspect}")
97 | else
98 | user = results.first
99 |
100 | extract_extra(user)
101 | log_extra
102 | end
103 | end
104 |
105 | return true
106 | else
107 | return false
108 | end
109 | end
110 |
111 | protected
112 |
113 | def raise_if_not_configured
114 | raise CASServer::AuthenticatorError.new(
115 | "Cannot validate credentials because the authenticator hasn't yet been configured"
116 | ) unless @options
117 | end
118 |
119 | def extract_extra user
120 | @extra_attributes = {}
121 | extra_attributes_to_extract.each do |col|
122 | @extra_attributes[col] = user.send(col)
123 | end
124 | end
125 |
126 | def log_extra
127 | if @extra_attributes.empty?
128 | $LOG.warn("#{self.class}: Did not read any extra_attributes for user #{@username.inspect} even though an :extra_attributes option was provided.")
129 | else
130 | $LOG.debug("#{self.class}: Read the following extra_attributes for user #{@username.inspect}: #{@extra_attributes.inspect}")
131 | end
132 | end
133 | end
134 |
--------------------------------------------------------------------------------
/spec/casserver_spec.rb:
--------------------------------------------------------------------------------
1 | # encoding: UTF-8
2 | require File.dirname(__FILE__) + '/spec_helper'
3 |
4 | $LOG = Logger.new(File.basename(__FILE__).gsub('.rb','.log'))
5 |
6 | RSpec.configure do |config|
7 | config.include Capybara
8 | end
9 |
10 | VALID_USERNAME = 'spec_user'
11 | VALID_PASSWORD = 'spec_password'
12 |
13 | ATTACK_USERNAME = '%3E%22%27%3E%3Cscript%3Ealert%2826%29%3C%2Fscript%3E&password=%3E%22%27%3E%3Cscript%3Ealert%2826%29%3C%2Fscript%3E<=%3E%22%27%3E%3Cscript%3Ealert%2826%29%3C%2Fscript%3E&service=%3E%22%27%3E%3Cscript%3Ealert%2826%29%3C%2Fscript%3E'
14 | INVALID_PASSWORD = 'invalid_password'
15 |
16 | describe 'CASServer' do
17 |
18 | before do
19 | @target_service = 'http://my.app.test'
20 | end
21 |
22 | describe "/login" do
23 | before do
24 | load_server(File.dirname(__FILE__) + "/default_config.yml")
25 | reset_spec_database
26 | end
27 |
28 | it "logs in successfully with valid username and password without a target service" do
29 | visit "/login"
30 |
31 | fill_in 'username', :with => VALID_USERNAME
32 | fill_in 'password', :with => VALID_PASSWORD
33 | click_button 'login-submit'
34 |
35 | page.should have_content("You have successfully logged in")
36 | end
37 |
38 | it "fails to log in with invalid password" do
39 | visit "/login"
40 | fill_in 'username', :with => VALID_USERNAME
41 | fill_in 'password', :with => INVALID_PASSWORD
42 | click_button 'login-submit'
43 |
44 | page.should have_content("Incorrect username or password")
45 | end
46 |
47 | it "logs in successfully with valid username and password and redirects to target service" do
48 | visit "/login?service="+CGI.escape(@target_service)
49 |
50 | fill_in 'username', :with => VALID_USERNAME
51 | fill_in 'password', :with => VALID_PASSWORD
52 |
53 | click_button 'login-submit'
54 |
55 | page.current_url.should =~ /^#{Regexp.escape(@target_service)}\/?\?ticket=ST\-[1-9rA-Z]+/
56 | end
57 |
58 | it "preserves target service after invalid login" do
59 | visit "/login?service="+CGI.escape(@target_service)
60 |
61 | fill_in 'username', :with => VALID_USERNAME
62 | fill_in 'password', :with => INVALID_PASSWORD
63 | click_button 'login-submit'
64 |
65 | page.should have_content("Incorrect username or password")
66 | page.should have_xpath('//input[@id="service"]', :value => @target_service)
67 | end
68 |
69 | it "uses appropriate localization when 'lang' prameter is given (make sure you've run `rake localization:mo` first!!)" do
70 | visit "/login?lang=pl"
71 | page.should have_content("Użytkownik")
72 |
73 | visit "/login?lang=pt_BR"
74 | page.should have_content("Usuário")
75 |
76 | visit "/login?lang=en"
77 | page.should have_content("Username")
78 | end
79 |
80 | it "is not vunerable to Cross Site Scripting" do
81 | visit '/login?service=%22%2F%3E%3cscript%3ealert%2832%29%3c%2fscript%3e'
82 | page.should_not have_content("alert(32)")
83 | page.should_not have_xpath("//script")
84 | #page.should have_xpath("")
85 | end
86 |
87 | end # describe '/login'
88 |
89 |
90 | describe '/logout' do
91 |
92 | before do
93 | load_server(File.dirname(__FILE__) + "/default_config.yml")
94 | reset_spec_database
95 | end
96 |
97 | it "logs out successfully" do
98 | visit "/logout"
99 |
100 | page.should have_content("You have successfully logged out")
101 | end
102 |
103 | it "logs out successfully and redirects to target service" do
104 | visit "/logout?gateway=true&service="+CGI.escape(@target_service)
105 |
106 | page.current_url.should =~ /^#{Regexp.escape(@target_service)}\/?/
107 | end
108 |
109 | end # describe '/logout'
110 |
111 | describe 'Configuration' do
112 | it "uri_path value changes prefix of routes" do
113 | load_server(File.dirname(__FILE__) + "/alt_config.yml")
114 | @target_service = 'http://my.app.test'
115 |
116 | visit "/test/login"
117 | page.status_code.should_not == 404
118 |
119 | visit "/test/logout"
120 | page.status_code.should_not == 404
121 | end
122 | end
123 |
124 | describe "proxyValidate" do
125 | before do
126 | load_server(File.dirname(__FILE__) + "/default_config.yml")
127 | reset_spec_database
128 |
129 | visit "/login?service="+CGI.escape(@target_service)
130 |
131 | fill_in 'username', :with => VALID_USERNAME
132 | fill_in 'password', :with => VALID_PASSWORD
133 |
134 | click_button 'login-submit'
135 |
136 | page.current_url.should =~ /^#{Regexp.escape(@target_service)}\/?\?ticket=ST\-[1-9rA-Z]+/
137 | @ticket = page.current_url.match(/ticket=(.*)$/)[1]
138 | end
139 |
140 | it "should have extra attributes in proper format" do
141 | visit "/serviceValidate?service=#{CGI.escape(@target_service)}&ticket=#{@ticket}"
142 |
143 | encoded_utf_string = "Ютф" # actual string is "Ютф"
144 | page.body.should match("#{encoded_utf_string}")
145 | page.body.should match("123.45")
146 | page.body.should match("Ютф")
147 | end
148 | end
149 | end
150 |
--------------------------------------------------------------------------------
/lib/casserver/authenticators/ldap.rb:
--------------------------------------------------------------------------------
1 | require 'casserver/authenticators/base'
2 |
3 | begin
4 | require 'net/ldap'
5 | rescue LoadError
6 | require 'rubygems'
7 | begin
8 | gem 'net-ldap', '~> 0.1.1'
9 | rescue Gem::LoadError
10 | $stderr.puts
11 | $stderr.puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
12 | $stderr.puts
13 | $stderr.puts "To use the LDAP/AD authenticator, you must first install the 'net-ldap' gem."
14 | $stderr.puts " See http://github.com/RoryO/ruby-net-ldap for details."
15 | $stderr.puts
16 | $stderr.puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
17 | exit 1
18 | end
19 | require 'net/ldap'
20 | end
21 |
22 | # Basic LDAP authenticator. Should be compatible with OpenLDAP and other similar LDAP servers,
23 | # although it hasn't been officially tested. See example config file for details on how
24 | # to configure it.
25 | class CASServer::Authenticators::LDAP < CASServer::Authenticators::Base
26 | def validate(credentials)
27 | read_standard_credentials(credentials)
28 |
29 | return false if @password.blank?
30 |
31 | raise CASServer::AuthenticatorError, "Cannot validate credentials because the authenticator hasn't yet been configured" unless @options
32 | raise CASServer::AuthenticatorError, "Invalid LDAP authenticator configuration!" unless @options[:ldap]
33 | raise CASServer::AuthenticatorError, "You must specify a server host in the LDAP configuration!" unless @options[:ldap][:host] || @options[:ldap][:server]
34 |
35 | raise CASServer::AuthenticatorError, "The username '#{@username}' contains invalid characters." if (@username =~ /[*\(\)\0\/]/)
36 |
37 | preprocess_username
38 |
39 | @ldap = Net::LDAP.new
40 |
41 |
42 | @options[:ldap][:host] ||= @options[:ldap][:server]
43 | @ldap.host = @options[:ldap][:host]
44 | @ldap.port = @options[:ldap][:port] if @options[:ldap][:port]
45 | @ldap.encryption(@options[:ldap][:encryption].intern) if @options[:ldap][:encryption]
46 |
47 | begin
48 | if @options[:ldap][:auth_user]
49 | bind_success = bind_by_username_with_preauthentication
50 | else
51 | bind_success = bind_by_username
52 | end
53 |
54 | return false unless bind_success
55 |
56 | entry = find_user
57 | extract_extra_attributes(entry)
58 |
59 | return true
60 | rescue Net::LDAP::LdapError => e
61 | raise CASServer::AuthenticatorError,
62 | "LDAP authentication failed with '#{e}'. Check your authenticator configuration."
63 | end
64 | end
65 |
66 | protected
67 | def default_username_attribute
68 | "cn"
69 | end
70 |
71 | private
72 | # Add prefix to username, if :username_prefix was specified in the :ldap config.
73 | def preprocess_username
74 | @username = @options[:ldap][:username_prefix] + @username if @options[:ldap][:username_prefix]
75 | end
76 |
77 | # Attempt to bind with the LDAP server using the username and password entered by
78 | # the user. If a :filter was specified in the :ldap config, the filter will be
79 | # added to the LDAP query for the username.
80 | def bind_by_username
81 | username_attribute = options[:ldap][:username_attribute] || default_username_attribute
82 |
83 | @ldap.bind_as(:base => @options[:ldap][:base], :password => @password, :filter => user_filter)
84 | end
85 |
86 | # If an auth_user is specified, we will connect ("pre-authenticate") with the
87 | # LDAP server using the authenticator account, and then attempt to bind as the
88 | # user who is actually trying to authenticate. Note that you need to set up
89 | # the special authenticator account first. Also, auth_user must be the authenticator
90 | # user's full CN, which is probably not the same as their username.
91 | #
92 | # This pre-authentication process is necessary because binding can only be done
93 | # using the CN, so having just the username is not enough. We connect as auth_user,
94 | # and then try to find the target user's CN based on the given username. Then we bind
95 | # as the target user to validate their credentials.
96 | def bind_by_username_with_preauthentication
97 | raise CASServer::AuthenticatorError, "A password must be specified in the configuration for the authenticator user!" unless
98 | @options[:ldap][:auth_password]
99 |
100 | @ldap.authenticate(@options[:ldap][:auth_user], @options[:ldap][:auth_password])
101 |
102 | @ldap.bind_as(:base => @options[:ldap][:base], :password => @password, :filter => user_filter)
103 | end
104 |
105 | # Combine the filter for finding the user with the optional extra filter specified in the config
106 | # (if any).
107 | def user_filter
108 | username_attribute = options[:ldap][:username_attribute] || default_username_attribute
109 |
110 | filter = Array(username_attribute).map { |ua| Net::LDAP::Filter.eq(ua, @username) }.reduce(:|)
111 | unless @options[:ldap][:filter].blank?
112 | filter &= Net::LDAP::Filter.construct(@options[:ldap][:filter])
113 | end
114 |
115 | filter
116 | end
117 |
118 | # Finds the user based on the user_filter (this is called after authentication).
119 | # We do this to make it possible to extract extra_attributes.
120 | def find_user
121 | results = @ldap.search( :base => options[:ldap][:base], :filter => user_filter)
122 | return results.first
123 | end
124 |
125 | def extract_extra_attributes(ldap_entry)
126 | @extra_attributes = {}
127 | extra_attributes_to_extract.each do |attr|
128 | v = ldap_entry[attr]
129 | next if !v || (v.respond_to?(:empty?) && v.empty?)
130 | if v.kind_of?(Array)
131 | @extra_attributes[attr] = []
132 | ldap_entry[attr].each do |a|
133 | @extra_attributes[attr] << a.to_s
134 | end
135 | else
136 | @extra_attributes[attr] = v.to_s
137 | end
138 | end
139 |
140 | if @extra_attributes.empty?
141 | $LOG.warn("#{self.class}: Did not read any extra_attributes for user #{@username.inspect} even though an :extra_attributes option was provided.")
142 | else
143 | $LOG.debug("#{self.class}: Read the following extra_attributes for user #{@username.inspect}: #{@extra_attributes.inspect}")
144 | end
145 | ldap_entry
146 | end
147 | end
148 |
--------------------------------------------------------------------------------
/lib/casserver/model.rb:
--------------------------------------------------------------------------------
1 | require 'active_record'
2 | require 'active_record/base'
3 |
4 | module CASServer::Model
5 |
6 | module Consumable
7 | def consume!
8 | self.consumed = Time.now
9 | self.save!
10 | end
11 |
12 | def self.included(mod)
13 | mod.extend(ClassMethods)
14 | end
15 |
16 | module ClassMethods
17 | def cleanup(max_lifetime, max_unconsumed_lifetime)
18 | transaction do
19 | conditions = ["created_on < ? OR (consumed IS NULL AND created_on < ?)",
20 | Time.now - max_lifetime,
21 | Time.now - max_unconsumed_lifetime]
22 |
23 | expired_tickets_count = count(:conditions => conditions)
24 |
25 | $LOG.debug("Destroying #{expired_tickets_count} expired #{self.name.demodulize}"+
26 | "#{'s' if expired_tickets_count > 1}.") if expired_tickets_count > 0
27 |
28 | destroy_all(conditions)
29 | end
30 | end
31 | end
32 | end
33 |
34 | class Base < ActiveRecord::Base
35 | end
36 |
37 | class Ticket < Base
38 | def to_s
39 | ticket
40 | end
41 |
42 | def self.cleanup(max_lifetime)
43 | transaction do
44 | conditions = ["created_on < ?", Time.now - max_lifetime]
45 | expired_tickets_count = count(:conditions => conditions)
46 |
47 | $LOG.debug("Destroying #{expired_tickets_count} expired #{self.name.demodulize}"+
48 | "#{'s' if expired_tickets_count > 1}.") if expired_tickets_count > 0
49 |
50 | destroy_all(conditions)
51 | end
52 | end
53 | end
54 |
55 | class LoginTicket < Ticket
56 | set_table_name 'casserver_lt'
57 | include Consumable
58 | end
59 |
60 | class ServiceTicket < Ticket
61 | set_table_name 'casserver_st'
62 | include Consumable
63 |
64 | belongs_to :granted_by_tgt,
65 | :class_name => 'CASServer::Model::TicketGrantingTicket',
66 | :foreign_key => :granted_by_tgt_id
67 | has_one :proxy_granting_ticket,
68 | :foreign_key => :created_by_st_id
69 |
70 | def matches_service?(service)
71 | CASServer::CAS.clean_service_url(self.service) ==
72 | CASServer::CAS.clean_service_url(service)
73 | end
74 | end
75 |
76 | class ProxyTicket < ServiceTicket
77 | belongs_to :granted_by_pgt,
78 | :class_name => 'CASServer::Model::ProxyGrantingTicket',
79 | :foreign_key => :granted_by_pgt_id
80 | end
81 |
82 | class TicketGrantingTicket < Ticket
83 | set_table_name 'casserver_tgt'
84 |
85 | serialize :extra_attributes
86 |
87 | has_many :granted_service_tickets,
88 | :class_name => 'CASServer::Model::ServiceTicket',
89 | :foreign_key => :granted_by_tgt_id
90 | end
91 |
92 | class ProxyGrantingTicket < Ticket
93 | set_table_name 'casserver_pgt'
94 | belongs_to :service_ticket
95 | has_many :granted_proxy_tickets,
96 | :class_name => 'CASServer::Model::ProxyTicket',
97 | :foreign_key => :granted_by_pgt_id
98 | end
99 |
100 | class Error
101 | attr_reader :code, :message
102 |
103 | def initialize(code, message)
104 | @code = code
105 | @message = message
106 | end
107 |
108 | def to_s
109 | message
110 | end
111 | end
112 |
113 | # class CreateCASServer < V 0.1
114 | # def self.up
115 | # if ActiveRecord::Base.connection.table_alias_length > 30
116 | # $LOG.info("Creating database with long table names...")
117 | #
118 | # create_table :casserver_login_tickets, :force => true do |t|
119 | # t.column :ticket, :string, :null => false
120 | # t.column :created_on, :timestamp, :null => false
121 | # t.column :consumed, :datetime, :null => true
122 | # t.column :client_hostname, :string, :null => false
123 | # end
124 | #
125 | # create_table :casserver_service_tickets, :force => true do |t|
126 | # t.column :ticket, :string, :null => false
127 | # t.column :service, :string, :null => false
128 | # t.column :created_on, :timestamp, :null => false
129 | # t.column :consumed, :datetime, :null => true
130 | # t.column :client_hostname, :string, :null => false
131 | # t.column :username, :string, :null => false
132 | # t.column :type, :string, :null => false
133 | # t.column :proxy_granting_ticket_id, :integer, :null => true
134 | # end
135 | #
136 | # create_table :casserver_ticket_granting_tickets, :force => true do |t|
137 | # t.column :ticket, :string, :null => false
138 | # t.column :created_on, :timestamp, :null => false
139 | # t.column :client_hostname, :string, :null => false
140 | # t.column :username, :string, :null => false
141 | # end
142 | #
143 | # create_table :casserver_proxy_granting_tickets, :force => true do |t|
144 | # t.column :ticket, :string, :null => false
145 | # t.column :created_on, :timestamp, :null => false
146 | # t.column :client_hostname, :string, :null => false
147 | # t.column :iou, :string, :null => false
148 | # t.column :service_ticket_id, :integer, :null => false
149 | # end
150 | # end
151 | # end
152 | #
153 | # def self.down
154 | # if ActiveRecord::Base.connection.table_alias_length > 30
155 | # drop_table :casserver_proxy_granting_tickets
156 | # drop_table :casserver_ticket_granting_tickets
157 | # drop_table :casserver_service_tickets
158 | # drop_table :casserver_login_tickets
159 | # end
160 | # end
161 | # end
162 | #
163 | # # Oracle table names cannot exceed 30 chars...
164 | # # See http://code.google.com/p/rubycas-server/issues/detail?id=15
165 | # class ShortenTableNames < V 0.5
166 | # def self.up
167 | # if ActiveRecord::Base.connection.table_alias_length > 30
168 | # $LOG.info("Shortening table names")
169 | # rename_table :casserver_login_tickets, :casserver_lt
170 | # rename_table :casserver_service_tickets, :casserver_st
171 | # rename_table :casserver_ticket_granting_tickets, :casserver_tgt
172 | # rename_table :casserver_proxy_granting_tickets, :casserver_pgt
173 | # else
174 | # create_table :casserver_lt, :force => true do |t|
175 | # t.column :ticket, :string, :null => false
176 | # t.column :created_on, :timestamp, :null => false
177 | # t.column :consumed, :datetime, :null => true
178 | # t.column :client_hostname, :string, :null => false
179 | # end
180 | #
181 | # create_table :casserver_st, :force => true do |t|
182 | # t.column :ticket, :string, :null => false
183 | # t.column :service, :string, :null => false
184 | # t.column :created_on, :timestamp, :null => false
185 | # t.column :consumed, :datetime, :null => true
186 | # t.column :client_hostname, :string, :null => false
187 | # t.column :username, :string, :null => false
188 | # t.column :type, :string, :null => false
189 | # t.column :proxy_granting_ticket_id, :integer, :null => true
190 | # end
191 | #
192 | # create_table :casserver_tgt, :force => true do |t|
193 | # t.column :ticket, :string, :null => false
194 | # t.column :created_on, :timestamp, :null => false
195 | # t.column :client_hostname, :string, :null => false
196 | # t.column :username, :string, :null => false
197 | # end
198 | #
199 | # create_table :casserver_pgt, :force => true do |t|
200 | # t.column :ticket, :string, :null => false
201 | # t.column :created_on, :timestamp, :null => false
202 | # t.column :client_hostname, :string, :null => false
203 | # t.column :iou, :string, :null => false
204 | # t.column :service_ticket_id, :integer, :null => false
205 | # end
206 | # end
207 | # end
208 | #
209 | # def self.down
210 | # if ActiveRecord::Base.connection.table_alias_length > 30
211 | # rename_table :casserver_lt, :cassserver_login_tickets
212 | # rename_table :casserver_st, :casserver_service_tickets
213 | # rename_table :casserver_tgt, :casserver_ticket_granting_tickets
214 | # rename_table :casserver_pgt, :casserver_proxy_granting_tickets
215 | # else
216 | # drop_table :casserver_pgt
217 | # drop_table :casserver_tgt
218 | # drop_table :casserver_st
219 | # drop_table :casserver_lt
220 | # end
221 | # end
222 | # end
223 | #
224 | # class AddTgtToSt < V 0.7
225 | # def self.up
226 | # add_column :casserver_st, :tgt_id, :integer, :null => true
227 | # end
228 | #
229 | # def self.down
230 | # remove_column :casserver_st, :tgt_id, :integer
231 | # end
232 | # end
233 | #
234 | # class ChangeServiceToText < V 0.71
235 | # def self.up
236 | # # using change_column to change the column type from :string to :text
237 | # # doesn't seem to work, at least under MySQL, so we drop and re-create
238 | # # the column instead
239 | # remove_column :casserver_st, :service
240 | # say "WARNING: All existing service tickets are being deleted."
241 | # add_column :casserver_st, :service, :text
242 | # end
243 | #
244 | # def self.down
245 | # change_column :casserver_st, :service, :string
246 | # end
247 | # end
248 | #
249 | # class AddExtraAttributes < V 0.72
250 | # def self.up
251 | # add_column :casserver_tgt, :extra_attributes, :text
252 | # end
253 | #
254 | # def self.down
255 | # remove_column :casserver_tgt, :extra_attributes
256 | # end
257 | # end
258 | #
259 | # class RenamePgtForeignKeys < V 0.80
260 | # def self.up
261 | # rename_column :casserver_st, :proxy_granting_ticket_id, :granted_by_pgt_id
262 | # rename_column :casserver_st, :tgt_id, :granted_by_tgt_id
263 | # end
264 | #
265 | # def self.down
266 | # rename_column :casserver_st, :granted_by_pgt_id, :proxy_granting_ticket_id
267 | # rename_column :casserver_st, :granted_by_tgt_id, :tgt_id
268 | # end
269 | # end
270 | end
271 |
--------------------------------------------------------------------------------
/lib/casserver/cas.rb:
--------------------------------------------------------------------------------
1 | require 'uri'
2 | require 'net/https'
3 |
4 | require 'casserver/model'
5 |
6 | # Encapsulates CAS functionality. This module is meant to be included in
7 | # the CASServer::Controllers module.
8 | module CASServer::CAS
9 |
10 | include CASServer::Model
11 |
12 | def generate_login_ticket
13 | # 3.5 (login ticket)
14 | lt = LoginTicket.new
15 | lt.ticket = "LT-" + CASServer::Utils.random_string
16 |
17 | lt.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
18 | lt.save!
19 | $LOG.debug("Generated login ticket '#{lt.ticket}' for client" +
20 | " at '#{lt.client_hostname}'")
21 | lt
22 | end
23 |
24 | # Creates a TicketGrantingTicket for the given username. This is done when the user logs in
25 | # for the first time to establish their SSO session (after their credentials have been validated).
26 | #
27 | # The optional 'extra_attributes' parameter takes a hash of additional attributes
28 | # that will be sent along with the username in the CAS response to subsequent
29 | # validation requests from clients.
30 | def generate_ticket_granting_ticket(username, extra_attributes = {})
31 | # 3.6 (ticket granting cookie/ticket)
32 | tgt = TicketGrantingTicket.new
33 | tgt.ticket = "TGC-" + CASServer::Utils.random_string
34 | tgt.username = username
35 | tgt.extra_attributes = extra_attributes
36 | tgt.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
37 | tgt.save!
38 | $LOG.debug("Generated ticket granting ticket '#{tgt.ticket}' for user" +
39 | " '#{tgt.username}' at '#{tgt.client_hostname}'" +
40 | (extra_attributes.blank? ? "" : " with extra attributes #{extra_attributes.inspect}"))
41 | tgt
42 | end
43 |
44 | def generate_service_ticket(service, username, tgt)
45 | # 3.1 (service ticket)
46 | st = ServiceTicket.new
47 | st.ticket = "ST-" + CASServer::Utils.random_string
48 | st.service = service
49 | st.username = username
50 | st.granted_by_tgt_id = tgt.id
51 | st.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
52 | st.save!
53 | $LOG.debug("Generated service ticket '#{st.ticket}' for service '#{st.service}'" +
54 | " for user '#{st.username}' at '#{st.client_hostname}'")
55 | st
56 | end
57 |
58 | def generate_proxy_ticket(target_service, pgt)
59 | # 3.2 (proxy ticket)
60 | pt = ProxyTicket.new
61 | pt.ticket = "PT-" + CASServer::Utils.random_string
62 | pt.service = target_service
63 | pt.username = pgt.service_ticket.username
64 | pt.granted_by_pgt_id = pgt.id
65 | pt.granted_by_tgt_id = pgt.service_ticket.granted_by_tgt.id
66 | pt.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
67 | pt.save!
68 | $LOG.debug("Generated proxy ticket '#{pt.ticket}' for target service '#{pt.service}'" +
69 | " for user '#{pt.username}' at '#{pt.client_hostname}' using proxy-granting" +
70 | " ticket '#{pgt.ticket}'")
71 | pt
72 | end
73 |
74 | def generate_proxy_granting_ticket(pgt_url, st)
75 | uri = URI.parse(pgt_url)
76 | https = Net::HTTP.new(uri.host,uri.port)
77 | https.use_ssl = true
78 |
79 | # Here's what's going on here:
80 | #
81 | # 1. We generate a ProxyGrantingTicket (but don't store it in the database just yet)
82 | # 2. Deposit the PGT and it's associated IOU at the proxy callback URL.
83 | # 3. If the proxy callback URL responds with HTTP code 200, store the PGT and return it;
84 | # otherwise don't save it and return nothing.
85 | #
86 | https.start do |conn|
87 | path = uri.path.empty? ? '/' : uri.path
88 | path += '?' + uri.query unless (uri.query.nil? || uri.query.empty?)
89 |
90 | pgt = ProxyGrantingTicket.new
91 | pgt.ticket = "PGT-" + CASServer::Utils.random_string(60)
92 | pgt.iou = "PGTIOU-" + CASServer::Utils.random_string(57)
93 | pgt.service_ticket_id = st.id
94 | pgt.client_hostname = @env['HTTP_X_FORWARDED_FOR'] || @env['REMOTE_HOST'] || @env['REMOTE_ADDR']
95 |
96 | # FIXME: The CAS protocol spec says to use 'pgt' as the parameter, but in practice
97 | # the JA-SIG and Yale server implementations use pgtId. We'll go with the
98 | # in-practice standard.
99 | path += (uri.query.nil? || uri.query.empty? ? '?' : '&') + "pgtId=#{pgt.ticket}&pgtIou=#{pgt.iou}"
100 |
101 | response = conn.request_get(path)
102 | # TODO: follow redirects... 2.5.4 says that redirects MAY be followed
103 | # NOTE: The following response codes are valid according to the JA-SIG implementation even without following redirects
104 |
105 | if %w(200 202 301 302 304).include?(response.code)
106 | # 3.4 (proxy-granting ticket IOU)
107 | pgt.save!
108 | $LOG.debug "PGT generated for pgt_url '#{pgt_url}': #{pgt.inspect}"
109 | pgt
110 | else
111 | $LOG.warn "PGT callback server responded with a bad result code '#{response.code}'. PGT will not be stored."
112 | nil
113 | end
114 | end
115 | end
116 |
117 | def validate_login_ticket(ticket)
118 | $LOG.debug("Validating login ticket '#{ticket}'")
119 |
120 | success = false
121 | if ticket.nil?
122 | error = _("Your login request did not include a login ticket. There may be a problem with the authentication system.")
123 | $LOG.warn "Missing login ticket."
124 | elsif lt = LoginTicket.find_by_ticket(ticket)
125 | if lt.consumed?
126 | error = _("The login ticket you provided has already been used up. Please try logging in again.")
127 | $LOG.warn "Login ticket '#{ticket}' previously used up"
128 | elsif Time.now - lt.created_on < settings.config[:maximum_unused_login_ticket_lifetime]
129 | $LOG.info "Login ticket '#{ticket}' successfully validated"
130 | else
131 | error = _("You took too long to enter your credentials. Please try again.")
132 | $LOG.warn "Expired login ticket '#{ticket}'"
133 | end
134 | else
135 | error = _("The login ticket you provided is invalid. There may be a problem with the authentication system.")
136 | $LOG.warn "Invalid login ticket '#{ticket}'"
137 | end
138 |
139 | lt.consume! if lt
140 |
141 | error
142 | end
143 |
144 | def validate_ticket_granting_ticket(ticket)
145 | $LOG.debug("Validating ticket granting ticket '#{ticket}'")
146 |
147 | if ticket.nil?
148 | error = "No ticket granting ticket given."
149 | $LOG.debug error
150 | elsif tgt = TicketGrantingTicket.find_by_ticket(ticket)
151 | if settings.config[:maximum_session_lifetime] && Time.now - tgt.created_on > settings.config[:maximum_session_lifetime]
152 | tgt.destroy
153 | error = "Your session has expired. Please log in again."
154 | $LOG.info "Ticket granting ticket '#{ticket}' for user '#{tgt.username}' expired."
155 | else
156 | $LOG.info "Ticket granting ticket '#{ticket}' for user '#{tgt.username}' successfully validated."
157 | end
158 | else
159 | error = "Invalid ticket granting ticket '#{ticket}' (no matching ticket found in the database)."
160 | $LOG.warn(error)
161 | end
162 |
163 | [tgt, error]
164 | end
165 |
166 | def validate_service_ticket(service, ticket, allow_proxy_tickets = false)
167 | $LOG.debug "Validating service/proxy ticket '#{ticket}' for service '#{service}'"
168 |
169 | if service.nil? or ticket.nil?
170 | error = Error.new(:INVALID_REQUEST, "Ticket or service parameter was missing in the request.")
171 | $LOG.warn "#{error.code} - #{error.message}"
172 | elsif st = ServiceTicket.find_by_ticket(ticket)
173 | if st.consumed?
174 | error = Error.new(:INVALID_TICKET, "Ticket '#{ticket}' has already been used up.")
175 | $LOG.warn "#{error.code} - #{error.message}"
176 | elsif st.kind_of?(CASServer::Model::ProxyTicket) && !allow_proxy_tickets
177 | error = Error.new(:INVALID_TICKET, "Ticket '#{ticket}' is a proxy ticket, but only service tickets are allowed here.")
178 | $LOG.warn "#{error.code} - #{error.message}"
179 | elsif Time.now - st.created_on > settings.config[:maximum_unused_service_ticket_lifetime]
180 | error = Error.new(:INVALID_TICKET, "Ticket '#{ticket}' has expired.")
181 | $LOG.warn "Ticket '#{ticket}' has expired."
182 | elsif !st.matches_service? service
183 | error = Error.new(:INVALID_SERVICE, "The ticket '#{ticket}' belonging to user '#{st.username}' is valid,"+
184 | " but the requested service '#{service}' does not match the service '#{st.service}' associated with this ticket.")
185 | $LOG.warn "#{error.code} - #{error.message}"
186 | else
187 | $LOG.info("Ticket '#{ticket}' for service '#{service}' for user '#{st.username}' successfully validated.")
188 | end
189 | else
190 | error = Error.new(:INVALID_TICKET, "Ticket '#{ticket}' not recognized.")
191 | $LOG.warn("#{error.code} - #{error.message}")
192 | end
193 |
194 | if st
195 | st.consume!
196 | end
197 |
198 |
199 | [st, error]
200 | end
201 |
202 | def validate_proxy_ticket(service, ticket)
203 | pt, error = validate_service_ticket(service, ticket, true)
204 |
205 | if pt.kind_of?(CASServer::Model::ProxyTicket) && !error
206 | if not pt.granted_by_pgt
207 | error = Error.new(:INTERNAL_ERROR, "Proxy ticket '#{pt}' belonging to user '#{pt.username}' is not associated with a proxy granting ticket.")
208 | elsif not pt.granted_by_pgt.service_ticket
209 | error = Error.new(:INTERNAL_ERROR, "Proxy granting ticket '#{pt.granted_by_pgt}'"+
210 | " (associated with proxy ticket '#{pt}' and belonging to user '#{pt.username}' is not associated with a service ticket.")
211 | end
212 | end
213 |
214 | [pt, error]
215 | end
216 |
217 | def validate_proxy_granting_ticket(ticket)
218 | if ticket.nil?
219 | error = Error.new(:INVALID_REQUEST, "pgt parameter was missing in the request.")
220 | $LOG.warn("#{error.code} - #{error.message}")
221 | elsif pgt = ProxyGrantingTicket.find_by_ticket(ticket)
222 | if pgt.service_ticket
223 | $LOG.info("Proxy granting ticket '#{ticket}' belonging to user '#{pgt.service_ticket.username}' successfully validated.")
224 | else
225 | error = Error.new(:INTERNAL_ERROR, "Proxy granting ticket '#{ticket}' is not associated with a service ticket.")
226 | $LOG.error("#{error.code} - #{error.message}")
227 | end
228 | else
229 | error = Error.new(:BAD_PGT, "Invalid proxy granting ticket '#{ticket}' (no matching ticket found in the database).")
230 | $LOG.warn("#{error.code} - #{error.message}")
231 | end
232 |
233 | [pgt, error]
234 | end
235 |
236 | # Takes an existing ServiceTicket object (presumably pulled from the database)
237 | # and sends a POST with logout information to the service that the ticket
238 | # was generated for.
239 | #
240 | # This makes possible the "single sign-out" functionality added in CAS 3.1.
241 | # See http://www.ja-sig.org/wiki/display/CASUM/Single+Sign+Out
242 | def send_logout_notification_for_service_ticket(st)
243 | uri = URI.parse(st.service)
244 | uri.path = '/' if uri.path.empty?
245 | time = Time.now
246 | rand = CASServer::Utils.random_string
247 |
248 | begin
249 | response = Net::HTTP.post_form(uri, {'logoutRequest' => URI.escape(%{
250 |
251 | #{st.ticket}
252 | })})
253 | if response.kind_of? Net::HTTPSuccess
254 | $LOG.info "Logout notification successfully posted to #{st.service.inspect}."
255 | return true
256 | else
257 | $LOG.error "Service #{st.service.inspect} responed to logout notification with code '#{response.code}'!"
258 | return false
259 | end
260 | rescue Exception => e
261 | $LOG.error "Failed to send logout notification to service #{st.service.inspect} due to #{e}"
262 | return false
263 | end
264 | end
265 |
266 | def service_uri_with_ticket(service, st)
267 | raise ArgumentError, "Second argument must be a ServiceTicket!" unless st.kind_of? CASServer::Model::ServiceTicket
268 |
269 | # This will choke with a URI::InvalidURIError if service URI is not properly URI-escaped...
270 | # This exception is handled further upstream (i.e. in the controller).
271 | service_uri = URI.parse(service)
272 |
273 | if service.include? "?"
274 | if service_uri.query.empty?
275 | query_separator = ""
276 | else
277 | query_separator = "&"
278 | end
279 | else
280 | query_separator = "?"
281 | end
282 |
283 | service_with_ticket = service + query_separator + "ticket=" + st.ticket
284 | service_with_ticket
285 | end
286 |
287 | # Strips CAS-related parameters from a service URL and normalizes it,
288 | # removing trailing / and ?. Also converts any spaces to +.
289 | #
290 | # For example, "http://google.com?ticket=12345" will be returned as
291 | # "http://google.com". Also, "http://google.com/" would be returned as
292 | # "http://google.com".
293 | #
294 | # Note that only the first occurance of each CAS-related parameter is
295 | # removed, so that "http://google.com?ticket=12345&ticket=abcd" would be
296 | # returned as "http://google.com?ticket=abcd".
297 | def clean_service_url(dirty_service)
298 | return dirty_service if dirty_service.blank?
299 | clean_service = dirty_service.dup
300 | ['service', 'ticket', 'gateway', 'renew'].each do |p|
301 | clean_service.sub!(Regexp.new("&?#{p}=[^&]*"), '')
302 | end
303 |
304 | clean_service.gsub!(/[\/\?&]$/, '') # remove trailing ?, /, or &
305 | clean_service.gsub!('?&', '?')
306 | clean_service.gsub!(' ', '+')
307 |
308 | $LOG.debug("Cleaned dirty service URL #{dirty_service.inspect} to #{clean_service.inspect}") if
309 | dirty_service != clean_service
310 |
311 | return clean_service
312 | end
313 | module_function :clean_service_url
314 |
315 | end
316 |
--------------------------------------------------------------------------------
/CHANGELOG:
--------------------------------------------------------------------------------
1 | === 1.0.1 :: In Progress...
2 |
3 | * NEW:
4 | * On startup the server now checks for a config.yml file in its own root directory,
5 | then in /etc/rubycas-server.
6 |
7 | * FIXED:
8 | * Specs now pass under Active Record 2.3.12
9 |
10 | === 1.0.0 :: 2011-08-03
11 |
12 | * NEW:
13 | * Rewrite to replace Camping/Picnic with Sinatra
14 | * Support for Ruby 1.9.2
15 | * Support for Active Record 3
16 |
17 | * CHANGED:
18 | * Google authenticator proxy configuration has been changed (see config.example.yml)
19 |
20 | === 0.8.0
21 |
22 | * NEW:
23 | * Support for localization via Ruby-GetText.
24 | See http://code.google.com/p/rubycas-server/wiki/Localization
25 | for details. [antono]
26 | * Switched to Picnic 0.8.x, so RubyCAS-Server is now based on Rack
27 | and Camping 2.0 and is now compatible with Passenger Phusion
28 | * Change to authenticator API: every authenticator now has a class 'setup'
29 | method that gets called at server startup. This is where class-level
30 | configuration should be done (e.g. establishing a database connection).
31 | This is different from the 'configure' method which gets called on a per-
32 | instance basis for each authenticator. [godfat]
33 | * Database connections are now automatically released back to the connection
34 | pool at the end of each request. This should allow the server to handle
35 | many more concurrent requests, since database connections are no longer left
36 | checked out of the pool.
37 | * Added new SQL authenticator (sql_rest_auth) compatible with the
38 | restful_authentication Rails plugin. [antono]
39 | * Re-licensed under the MIT License.
40 |
41 | * FIXED:
42 | * Fixed weird problems with loading controllers when using older versions of
43 | activesupport and/or rubygems.
44 | * Failure to connect to a service during a single sign out request is now
45 | handled gracefully.
46 | * Required gem dependencies have been re-enabled in the gemspec.
47 | * Authlogic authenticator files added to gemspec. [rajiv]
48 | * Authenticators are now instantiated on a per-request basis (rather than
49 | once at startup) to ensure thread safety.
50 |
51 | === 0.7.1 :: 2008-11-10
52 |
53 | * Fixed dependency loading problems introduced by upstream changes in RubyGems
54 | 1.3.1.
55 |
56 | === 0.7.0 :: 2008-11-04
57 |
58 | * NEW:
59 | * Implemented single-sign-out functionality as specified in CAS 3.3. See
60 | http://www.ja-sig.org/wiki/display/CASUM/Single+Sign+Out.
61 | * It is now possible to configure Authenticators to return extra attributes
62 | to CAS clients alongside the username. For an example of how to do this see
63 | the included SQL authenticator. Also have a look at:
64 | http://groups.google.com/group/rubycas-server/browse_thread/thread/5eade3793cb590e9
65 | Note that extra attributes of type other than String or Numeric are serialized
66 | into YAML format before being sent along to the client.
67 | * Added an MD5-password version of the SQL authenticator for Drupal and any other
68 | database that stores its passwords in hashed form (thanks malcolmm).
69 | * Added new Google authenticator for authenticating against Google/GMail
70 | accounts.
71 |
72 | * CHANGED:
73 | * Service URIs are now automatically normalized. For example, if the service
74 | URI given to the server has a 'ticket' parameter, the ticket will now be
75 | automatically stripped. This is to avert any possible issues raised by
76 | misbehaving CAS clients (the CAS ticket should never be part of the service
77 | URI). Same goes for other CAS-related parameters like 'service', 'renew',
78 | and 'gateway'. Additionally, the trailing '/' and '?' characters are
79 | automatically stripped from URLs, since, for example, "http://google.com/"
80 | is almost certainly equivalent to "http://google.com".
81 | * The expire_sessions config variable is now respected -- ticket granting
82 | ticket cookies are set with an expiry datetime, so that the SSO session
83 | is effectively terminated once the ticket_granting_ticket_expiry period
84 | is reached.
85 | * If present, the HTTP_X_FORWARDED_FOR header is used for recording the
86 | client's address. This is useful when the server is running behind a reverse
87 | proxy, but it should not be considered authoritative since it can be
88 | easily spoofed.
89 | * The 'service' field in the 'casserver_st' table has been changed from
90 | VARCHAR(255) to TEXT in order to accomodate service URIs longer than 255
91 | characters (fixes issue #46).
92 | * The CAS XML responses are no longer whitespace-formatted (i.e. Markaby's
93 | auto-indentation has been turned off). Apparently the whitespace was
94 | causing problems with mod_auth_cas. See:
95 | http://groups.google.com/group/rubycas-server/browse_thread/thread/e482fe09999b73d3
96 | * When used without pre-authentication, the LDAP authenticator now tries to
97 | bind by searching for the given username in the LDAP directory based on the
98 | configured username_attribute. Prior to this change the authenticator
99 | attempted to bind with the LDAP server by assuming that the username credential
100 | matches the user's CN. This is no longer the case.
101 | * CAS responses to invalid requests (for example where required parameters
102 | are missing or incorrect) will now have HTTP status code 422. Internal server
103 | errors (where the server rather than the client is at fault) have error 500.
104 | Previously most responses had error code 200, regardless of their contents.
105 |
106 | * FIXED:
107 | * Fixed logout action to work properly with ActiveRecord 2.1 (eager loading behaviour
108 | was changed upstream forcing a change to the way we look for ProxyGrantingTickets
109 | to delete on logout).
110 | * When running under Mongrel, the USR2 signal should now restart the server as
111 | expected -- however currently this only works when the server is running
112 | in the foregaround. When daemonized, USR2 will shut down the server without
113 | restarting (see issue #58).
114 | * Fixed activerecord/activesupport gem load problems, hopefully once and for all
115 | (however picnic-0.7.0 is now required).
116 |
117 | === 0.6.0 :: 2008-03-28
118 |
119 | * Much of the supporting functionality that makes RubyCAS-Server
120 | act as a well-behaved Linux service has been abstracted out
121 | into its own library. This new library is called Picnic and is
122 | now a gem dependency for RubyCAS-Server. You can find out more about
123 | it at http://code.google.com/p/camping-picnic/.
124 | * The logout action will now accept a 'destination' parameter in lieu of
125 | 'service'. This means that if a 'destination' parameter is given with
126 | some URL, the logout action will show the login form, allowing the user
127 | to immedietly log back in to the service specified by 'destination'.
128 | * The logout action will now accept a 'url' parameter. If given, the logout
129 | page will show a message indicating that the CAS session has been terminated
130 | and instructing the user to click on a link to follow the given URL. If the
131 | 'url' parameter is given, the login form will NOT be shown on the logout
132 | page (see above).
133 | * When an authentication failure occurs (because the user submitted
134 | invalid credentials or the login ticket is missing), the server
135 | now returns a 401 (Unauthorized) response instead of 200.
136 | * An encryption-enabled version of the SQL authenticator is now
137 | available. For more info have a look at:
138 | http://code.google.com/p/rubycas-server/wiki/UsingTheSQLEncryptedAuthenticator
139 | * Better compatibility with Oracle databases. The database migration
140 | no longer tries to create tables with long names when long
141 | table names are not supported by the underlying database connector
142 | (issue #15).
143 | * The server now automatically removes leading and trailing whitespace from
144 | the username entered by users. Passwords however are left intact, with no
145 | whitespace removed.
146 | * The server can now be configured to automatically downcase the
147 | username entered by users (dowcase_username option). So if a user
148 | enters "JSmith", the system will convert it to "jsmith" if the
149 | downcase_username option is set to true.
150 | * The server can now be made to bind to a specific address. See the
151 | :bind_address option in the config.example.yml file.
152 | * Fixed bug with ActiveRecord 2.0.2 where service tickets were not
153 | being given a type (issue #37).
154 |
155 | === 0.5.1 :: 2007-12-20
156 |
157 | * Tickets generated by the server should now be a lot more secure.
158 | The random string generator used for generating tickets now uses
159 | Crypt::ISAAC. Tickets have also been extended in length; STs, PTs
160 | and LTs can now extend up to 32 characters, and PGTs and PGT-IOUs
161 | up to 64.
162 |
163 | === 0.5.0 :: 2007-09-20
164 |
165 | * Gateway requests should now be handled correctly. When the request to the
166 | login page is made with gateway=true as one of the parameters, the CAS
167 | server will immediately redirect back to the target service along with
168 | a service ticket if an SSO session exists for the user (or without a
169 | service ticket if there is no pre-existing SSO session).
170 | Note that if you are using RubyCAS-Client and want gatewaying, you will
171 | need to upgrade it to 1.1.0 as gatewaying was broken in prior versions.
172 | * If gateway=true is specified as part of the logout URI, the server will
173 | log the user out and immediately redirect them back to the specified
174 | service. In other words, you can now do "gatewayed logouts" as well
175 | as logins.
176 | * A login ticket can now be remotely requested from the server by placing
177 | a POST request to '/loginTicket'.
178 | * The login view can now be made to return only the login form. This is
179 | done by adding the 'onlyLoginForm' parameter to the '/login' request.
180 | Optionally, a 'submitToURI' parameter can be supplied to force the login
181 | form to submit to the given URI (otherwise the server will try to figure
182 | out the full URI to its own login controller). This functionality may be
183 | useful when you want to embed the login form in some external page, as
184 | an IFRAME otherwise.
185 | * Custom views can now be used to override the default Markaby templates
186 | by specifying a 'custom_views_file' option in the configuration. See
187 | custom_views.example.rb. [jzylks]
188 | * Table names have been shortened to work with Oracle. A migration has
189 | been added that should do the shortening for you the first time you run
190 | this new RubyCAS-Server version.
191 | * Multiple authenticators can now be specified. During authentication,
192 | credentials are presented to the first authenticator, then the second,
193 | and so on, until the user is validated by any one authenticator or fails
194 | validation for all of them. [jzylks]
195 | * When using webrick, you can now run with SSL disabled by omitting the
196 | ssl_cert and ssl_key parameters.
197 | * Changed incorrect MySQL example database configuration -- option should
198 | be 'host:' not 'server:' (issue #22).
199 |
200 | === 0.4.2 :: 2007-07-26
201 |
202 | * The LDAP/AD authenticator has been largely re-written. The code is a bit
203 | cleaner now, and should work better with non-Active Directory LDAP servers
204 | (although this has yet to be tested since I don't have access to a non-AD
205 | LDAP server).
206 | * The validate() method in your authenticators now receives a :service element
207 | (in addition to :username, and :password). This is simply the service
208 | url (if any) specified in the user's CAS request. If you call
209 | read_standard_credentials(credentials) at the top of your validator, the value
210 | will also be available as @service along with @username and @password.
211 | * By request, a :username_prefix option has been added to the ldap
212 | configuration. If entered, this string will be automatically prefixed to
213 | the username entered by the user.
214 | * A bug having to do with handling authenticator errors has been fixed.
215 | Any authenticator error messages should now be correctly shown on the
216 | login page.
217 | * Minor improvements to error messages having to do with login tickets.
218 | They're a bit more prescriptive now, explaining to the user what steps
219 | they should take to correct the error.
220 |
221 | === 0.4.1 :: 2007-06-07
222 |
223 | * This release restores compatiblity with older versions of rubygems
224 | (pre-0.9.0). To achieve this, we alias the 'gem' method to the old
225 | 'require_gem' if 'gem' is not already defined.
226 | * rubycas-server-ctl will now quiety delete an orphaned .pid file
227 | instead complaining loudly and refusing to start up.
228 | * Fixed minor bug in rubycas-server-ctl that sometimes incorrectly reported
229 | startup problems when in fact the server had started just fine.
230 |
231 |
232 | === 0.4.0 :: 2007-06-05
233 |
234 | * Added rubycas-server-ctl script for controlling daemonized server.
235 | * rubygems-0.9.0 or later is now required.
236 | * Added system startup script to be used in /etc/init.d on Linux systems.
237 | * Authenticator can now be loaded from an external file using the 'source'
238 | configuration option.
239 | * Better preemptive detection of startup problems with mongrel.
240 | * User now sees an error message if the service URI is not a valid URI (i.e.
241 | if it's not URI-encoded or otherwise malformed).
242 |
243 |
244 | === 0.3.0 :: 2007-03-29
245 |
246 | * Fixed glaring security problem with LDAP/AD Authenticator where under some
247 | circumstances blank passwords were accepted as valid.
248 | * Autocomplete has been turned off on the password field for better security.
249 | In the future we may allow autocomplete to be re-enabled using a
250 | configuration setting.
251 | * When the user visits the login page and is already authenticated (i.e. they
252 | have a valid ticket granting cookie), a message is shown at the top
253 | indicating that they are already logged in.
254 | * sqlite3-ruby is no longer required by the gem as a dependency. The user
255 | must now install it manually prior to installing rubycas-server. The
256 | building of sqlite3 native extensions appears to be somewhat flakey
257 | and probably defeats the original purpose of using it (which was
258 | to have a CAS server up and running with no additional DB configuration).
259 | We will use MySQL as the default database adapter instead, since it does
260 | not require additional libraries and many users will have a MySQL server
261 | already available.
262 | * Fixed bug that was causing all proxy-granting tickets to be deleted whenever
263 | any user logged out. Only the PGTs for the user that is logging out are now
264 | being deleted.
265 | * Trailing slashes in service URLs are now ignored when validating service
266 | and proxy tickets (e.g. "http://www.google.com" and "http://www.google.com/"
267 | are now considered to be the same service URL).
268 | * Authenticators now raise AuthenticatorError exceptions when encountering
269 | a problem/error. This makes it easier to send feedback to the user.
270 | However, other exceptions should still be raised when errors ought
271 | not be recoverable (i.e. programming errors).
272 | * Fixed serious vulnerability in LDAP authenticator where under some
273 | cirumstances the user could just enter '*' as their username to match
274 | any username. The LDAP authenticator will now refuse to process logins
275 | with usernames that contain the characters * ( ) \ / and the NULL
276 | character \0.
277 | * Views are no longer xhtml-validated. Markaby's auto-validation was turned
278 | off to allow for use of the autocomplete property on inputs, since this is
279 | the only viable way of turning off password storage in IE and Firefox at
280 | the page level.
281 | * You can now limit the maximum length of a login session by setting the
282 | expire_sessions config setting to true.
283 | * Fixed some minor bugs in the login view.
284 |
285 |
286 | === 0.2.0 :: 2007-03-20
287 |
288 | * ruby-casserver now behaves more like a real command-line app, accepting
289 | various command line arguments including -h (help), -v (version), -c (use
290 | an alternate config.yml), and -d (daemonize, when using webrick or mongrel
291 | mode).
292 | * Special characters in CAS XML responses are now properly encoded into XML
293 | entities
294 | * CAS XML responses are no longer auto-indented... Markaby's indentation
295 | seemed to be causing problems with the PHP CAS client.
296 | * Misc minor bug fixes/cleanup.
297 |
298 |
299 | === 0.1.0 :: 2007-03-01
300 |
301 | * First public release.
302 |
--------------------------------------------------------------------------------
/config/config.example.yml:
--------------------------------------------------------------------------------
1 | # IMPORTANT NOTE ABOUT YAML CONFIGURATION FILES
2 | # ---> Be sure to use spaces instead of tabs for indentation. YAML is
3 | # white-space sensitive!
4 |
5 | ##### SERVER SETUP ################################################################
6 |
7 | # There are several ways to run RubyCAS-Server:
8 | #
9 | # webrick -- stand-alone WEBrick server; should work out-of-the-box; this is
10 | # the default method, but probably not suited for high-traffic usage
11 | # mongrel -- stand-alone Mongrel server; fast, but you'll need to install
12 | # and compile Mongrel and run it behind an https reverse proxy like
13 | # Pound or Apache 2.2's mod_proxy (since Mongrel cannot serve out
14 | # over SSL on its own).
15 | # passenger -- served out by Apache via the mod_rails/mod_rack module
16 | # (see http://www.modrails.com/)
17 | #
18 | # The following are exampe configurations for each of these three methods:
19 | #
20 |
21 |
22 | ###
23 | ### WEBrick example
24 | ###
25 | # WEBrick is a simple, all-Ruby web server. This is the easiest method for running
26 | # RubyCAS-Server. All you need is an SSL certificate (enter its path under the
27 | # ssl_cert option). WEBrick is fine for sites with low to medium traffic, but for
28 | # high-performance scenarios you may want to look into deploying using Mongrel
29 | # or Passenger.
30 |
31 | server: webrick
32 | port: 443
33 | ssl_cert: /path/to/your/ssl.pem
34 |
35 | # If your private key is in a separate file from the cert
36 |
37 | #ssl_key: /path/to/your/private_key.pem
38 |
39 | # If you do not already have an SSL certificate and would like to automatically
40 | # generate one, run the "generate_ssl_certificate" rake task and use the following
41 | # settings:
42 |
43 | # ssl_cert: ssl/cert.pem
44 | # ssl_key: ssl/key.pem
45 |
46 |
47 | # By default the login page will be available at the root path
48 | # (e.g. https://login.example.net/). The uri_path option lets you serve it from a
49 | # different path (e.g. https://login.example.net/cas).
50 |
51 | #uri_path: /cas
52 |
53 |
54 | # This lets you bind the server to a specific address. Use 0.0.0.0 to listen on
55 | # all available interfaces (this is the default).
56 |
57 | #bind_address: 0.0.0.0
58 |
59 |
60 | ###
61 | ### Mongrel example
62 | ###
63 | # Mongrel is much faster than WEBrick, but there are two caveats:
64 | # 1. Since Mongrel can't serve out encrypted HTTP on its own (and CAS requires this),
65 | # you will have to set up a reverse proxy like Pound or Apache's mod_proxy and
66 | # route through it requests to the Mongrel server. So for example,
67 | # your Pound server will receive all of the requests to RubyCAS-Server on port 443,
68 | # and forward them to the Mongrel server listening on port 11011.
69 | # 2. Some of Mongrel's components are compiled into native binaries, so if you are
70 | # installing on Linux, make sure you have all of the standard build tools
71 | # available. The binaries should be automatically compiled for you when you
72 | # install the mogrel gem (if you're runnings Windows, pre-compiled
73 | # binaries will be downloaded and installed, so don't worry about this).
74 |
75 | #server: mongrel
76 | #port: 11011
77 |
78 |
79 | # Bind the server to a specific address. Use 0.0.0.0 to listen on all
80 | # available interfaces (this is the default).
81 |
82 | #bind_address: 0.0.0.0
83 |
84 | ### Reverse proxy configuration examples
85 | # If you're using mod_proxy, your Apache vhost config should look something like this:
86 | #
87 | # Listen 443
88 | #
89 | # ServerAdmin admin@example.net
90 | # ServerName login.example.net
91 | #
92 | # SSLEngine On
93 | # SSLCertificateFile /etc/apache2/ssl.crt/example.pem
94 | #
95 | # # Don't do forward proxying, we only want reverse proxying
96 | # ProxyRequests Off
97 | #
98 | #
99 | # Order allow,deny
100 | # Allow from all
101 | # BalancerMember http://127.0.0.1:11011
102 | #
103 | #
104 | #
105 | # For Pound, the config should be something like:
106 | #
107 | # ListenHTTPS
108 | # Address 0.0.0.0
109 | # Port 11011
110 | # Cert "/etc/ssl/example.pem"
111 | #
112 | # Service
113 | # BackEnd
114 | # Address localhost
115 | # Port 443
116 | # End
117 | # End
118 | # End
119 |
120 |
121 | ###
122 | ### Phusion Passenger (running under Apache configured for SSL)
123 | ###
124 |
125 | # No additional configuration is requried to run RubyCAS-Server under
126 | # passsenger. Just follow the normal instructions for a Passenger app
127 | # (see http://www.modrails.com/).
128 | #
129 | # Here's an example Apache vhost config for RubyCAS-Server and Passenger:
130 | #
131 | # Listen 443
132 | #
133 | # ServerAdmin admin@example.net
134 | # ServerName login.example.net
135 | #
136 | # SSLEngine On
137 | # SSLCertificateFile /etc/apache2/ssl.crt/example.pem
138 | #
139 | # RailsAutoDetect off
140 | #
141 | # DocumentRoot /usr/lib/ruby/gems/1.8/gems/rubycas-server-0.8.0/public
142 | #
143 | #
144 | # AllowOverride all
145 | # Allow from all
146 | #
147 | #
148 | #
149 |
150 |
151 | ##### DATABASE #################################################################
152 |
153 | # Set up the database connection. Make sure that this database is secure!
154 | #
155 | # By default, we use MySQL, since it is widely used and does not require any
156 | # additional ruby libraries besides ActiveRecord.
157 | #
158 | # With MySQL, your config would be something like the following:
159 | # (be sure to create the casserver database in MySQL beforehand,
160 | # i.e. `mysqladmin -u root create casserver`)
161 |
162 | database:
163 | adapter: mysql
164 | database: casserver
165 | username: root
166 | password:
167 | host: localhost
168 | reconnect: true
169 |
170 | # IMPORTANT! By default, the server can handle up to ~5 concurrent requests
171 | # (without queuing). You can increase this by setting the database connection
172 | # pool size to a higher number. For example, to handle up to ~10 concurrent
173 | # requests:
174 | #
175 | #database:
176 | # pool: 10
177 | # adapter: mysql
178 | # database: casserver
179 | # username: root
180 | # password:
181 | # host: localhost
182 |
183 | #
184 | # Instead of MySQL you can use SQLite3, PostgreSQL, MSSQL, or anything else
185 | # supported by ActiveRecord.
186 | #
187 | # With SQLite3 (which does not require a separate database server), your
188 | # configuration would look something like the following (don't forget to install
189 | # the sqlite3-ruby gem beforehand!):
190 |
191 | #database:
192 | # adapter: sqlite3
193 | # database: /var/lib/casserver.db
194 |
195 |
196 | # By default RubyCAS-Server will run migrations at every startup to ensure
197 | # that its database schema is up-to-date. To disable this behaviour set
198 | # the following option to true:
199 |
200 | #disable_auto_migrations: true
201 |
202 | ##### AUTHENTICATION ###########################################################
203 |
204 | # Configure how username/passwords are validated.
205 | #
206 | # !!! YOU MUST CONFIGURE AT LEAST ONE OF THESE AUTHENTICATION METHODS !!!
207 | #
208 | # There are several built-in methods for authentication:
209 | # SQL, ActiveDirectory, LDAP, and GoogleAccounts. If none of these work for you,
210 | # it is relatively easy to write your own custom Authenticator class (see below).
211 | #
212 | # === SQL Authentication =======================================================
213 | #
214 | # The simplest method is to validate against a SQL database. This assumes
215 | # that all of your users are stored in a table that has a 'username' column
216 | # and a 'password' column. When the user logs in, CAS connects to this database
217 | # and looks for a matching username/password in the users table. If a matching
218 | # username and password is found, authentication is successful.
219 | #
220 | # If you prefer to have your passwords stored in an encrypted form, have a
221 | # look at the SQLEncrypted authenticator:
222 | # http://code.google.com/p/rubycas-server/wiki/UsingTheSQLEncryptedAuthenticator
223 | #
224 | # If your users table stores passwords with MD5 hashing (for example as with
225 | # Drupal) try using the SQLMd5 version of the SQL authenticator.
226 | #
227 | # Example:
228 | #
229 | #authenticator:
230 | # class: CASServer::Authenticators::SQL
231 | # database:
232 | # adapter: mysql
233 | # database: some_database_with_users_table
234 | # username: root
235 | # password:
236 | # host: localhost
237 | # user_table: users
238 | # username_column: username
239 | # password_column: password
240 | #
241 | # When replying to a CAS client's validation request, the server will normally
242 | # provide the client with the authenticated user's username. However it is
243 | # possible for the server to provide the client with additional attributes.
244 | # You can configure the SQL authenticator to provide data from additional
245 | # columns in the users table by listing the names of the columns under the
246 | # 'extra_attributes' option. Note though that this functionality is experimental.
247 | # It should work with RubyCAS-Client, but may or may not work with other CAS
248 | # clients.
249 | #
250 | # For example, with this configuration, the 'full_name' and 'access_level'
251 | # columns will be provided to your CAS clients along with the username:
252 | #
253 | #authenticator:
254 | # class: CASServer::Authenticators::SQL
255 | # database:
256 | # adapter: mysql
257 | # database: some_database_with_users_table
258 | # user_table: users
259 | # username_column: username
260 | # password_column: password
261 | # extra_attributes: full_name, access_level
262 | #
263 | #
264 | #
265 | # === Google Authentication ====================================================
266 | #
267 | # The Google authenticator allows users to log in to your CAS server using
268 | # their Google account credentials (i.e. the same email and password they
269 | # would use to log in to Google services like Gmail). This authenticator
270 | # requires no special configuration -- just specify its class name:
271 | #
272 | #authenticator:
273 | # class: CASServer::Authenticators::Google
274 | #
275 | # If you are behind an http proxy, you can try specifying proxy settings as follows:
276 | #
277 | #authenticator:
278 | # class: CASServer::Authenticators::Google
279 | # proxy:
280 | # host: your-proxy-server
281 | # port: 8080
282 | # username: nil
283 | # password: nil
284 | #
285 | # Note that as with all authenticators, it is possible to use the Google
286 | # authenticator alongside other authenticators. For example, CAS can first
287 | # attempt to validate the account with Google, and if that fails, fall back
288 | # to some other local authentication mechanism.
289 | #
290 | # For example:
291 | #
292 | #authenticator:
293 | # - class: CASServer::Authenticators::Google
294 | # - class: CASServer::Authenticators::SQL
295 | # database:
296 | # adapter: mysql
297 | # database: some_database_with_users_table
298 | # username: root
299 | # password:
300 | # host: localhost
301 | # user_table: user
302 | # username_column: username
303 | # password_column: password
304 | #
305 | #
306 | # === ActiveDirectory Authentication ===========================================
307 | #
308 | # This method authenticates against Microsoft's Active Directory using LDAP.
309 | # You must configure the ActiveDirectory server, and base DN. The port number
310 | # and LDAP filter are optional. You must also enter a CN and password
311 | # for a special "authenticator" user. This account is used to log in to
312 | # the ActiveDirectory server and search LDAP. This does not have to be an
313 | # administrative account -- it only has to be able to search for other
314 | # users.
315 | #
316 | # Note that the auth_user parameter must be the user's CN (Common Name).
317 | # In Active Directory, the CN is genarally the user's full name, which is usually
318 | # NOT the same as their username (sAMAccountName).
319 | #
320 | # For example:
321 | #
322 | #authenticator:
323 | # class: CASServer::Authenticators::ActiveDirectoryLDAP
324 | # ldap:
325 | # host: ad.example.net
326 | # port: 389
327 | # base: dc=example,dc=net
328 | # filter: (objectClass=person)
329 | # auth_user: authenticator
330 | # auth_password: itsasecret
331 | #
332 | # A more complicated example, where the authenticator will use TLS encryption,
333 | # will ignore users with disabled accounts, and will pass on the 'cn' and 'mail'
334 | # attributes to CAS clients:
335 | #
336 | #authenticator:
337 | # class: CASServer::Authenticators::ActiveDirectoryLDAP
338 | # ldap:
339 | # host: ad.example.net
340 | # port: 636
341 | # base: dc=example,dc=net
342 | # filter: (objectClass=person) & !(msExchHideFromAddressLists=TRUE)
343 | # auth_user: authenticator
344 | # auth_password: itsasecret
345 | # encryption: simple_tls
346 | # extra_attributes: cn, mail
347 | #
348 | # It is possible to authenticate against Active Directory without the
349 | # authenticator user, but this requires that users type in their CN as
350 | # the username rather than typing in their sAMAccountName. In other words
351 | # users will likely have to authenticate by typing their full name,
352 | # rather than their username. If you prefer to do this, then just
353 | # omit the auth_user and auth_password values in the above example.
354 | #
355 | #
356 | # === LDAP Authentication ======================================================
357 | #
358 | # This is a more general version of the ActiveDirectory authenticator.
359 | # The configuration is similar, except you don't need an authenticator
360 | # username or password. The following example has been reported to work
361 | # for a basic OpenLDAP setup.
362 | #
363 | #authenticator:
364 | # class: CASServer::Authenticators::LDAP
365 | # ldap:
366 | # host: ldap.example.net
367 | # port: 389
368 | # base: dc=example,dc=net
369 | # username_attribute: uid
370 | # filter: (objectClass=person)
371 | #
372 | # If you need more secure connections via TSL, specify the 'encryption'
373 | # option and change the port. This example also forces the authenticator
374 | # to connect using a special "authenticator" user with the given
375 | # username and password (see the ActiveDirectoryLDAP authenticator
376 | # explanation above):
377 | #
378 | #authenticator:
379 | # class: CASServer::Authenticators::LDAP
380 | # ldap:
381 | # host: ldap.example.net
382 | # port: 636
383 | # base: dc=example,dc=net
384 | # filter: (objectClass=person)
385 | # encryption: simple_tls
386 | # auth_user: cn=admin,dc=example,dc=net
387 | # auth_password: secret
388 | #
389 | # If you need additional data about the user passed to the client (for example,
390 | # their 'cn' and 'mail' attributes, you can specify the list of attributes
391 | # under the extra_attributes config option:
392 | #
393 | #authenticator:
394 | # class: CASServer::Authenticators::LDAP
395 | # ldap:
396 | # host: ldap.example.net
397 | # port: 389
398 | # base: dc=example,dc=net
399 | # filter: (objectClass=person)
400 | # extra_attributes: cn, mail
401 | #
402 | # Note that the above functionality is somewhat limited by client compatibility.
403 | # See the SQL authenticator notes above for more info.
404 | #
405 | #
406 | # === Custom Authentication ====================================================
407 | #
408 | # It should be relatively easy to write your own Authenticator class. Have a look
409 | # at the built-in authenticators in the casserver/authenticators directory. Your
410 | # authenticator should extend the CASServer::Authenticators::Base class and must
411 | # implement a validate() method that takes a single hash argument. When the user
412 | # submits the login form, the username and password they entered is passed to
413 | # validate() as a hash under :username and :password keys. In the future, this
414 | # hash might also contain other data such as the domain that the user is logging
415 | # in to.
416 | #
417 | # To use your custom authenticator, specify it's class name and path to the
418 | # source file in the authenticator section of the config. Any other parameters
419 | # you specify in the authenticator configuration will be passed on to the
420 | # authenticator and made availabe in the validate() method as an @options hash.
421 | #
422 | # Example:
423 | #
424 | #authenticator:
425 | # class: FooModule::MyCustomAuthenticator
426 | # source: /path/to/source.rb
427 | # option_a: foo
428 | # another_option: yeeha
429 | #
430 | # === Multiple Authenticators ==================================================
431 | #
432 | # If you need to have more than one source for authentication, such as an LDAP
433 | # directory and a database, you can use multiple authenticators by making
434 | # :authenticator an array of authenticators.
435 | #
436 | #authenticator:
437 | # -
438 | # class: CASServer::Authenticators::ActiveDirectoryLDAP
439 | # ldap:
440 | # host: ad.example.net
441 | # port: 389
442 | # base: dc=example,dc=net
443 | # filter: (objectClass=person)
444 | # -
445 | # class: CASServer::Authenticators::SQL
446 | # database:
447 | # adapter: mysql
448 | # database: some_database_with_users_table
449 | # username: root
450 | # password:
451 | # host: localhost
452 | # user_table: user
453 | # username_column: username
454 | # password_column: password
455 | #
456 | # During authentication, the user credentials will be checked against the first
457 | # authenticator and on failure fall through to the second authenticator.
458 | #
459 |
460 |
461 | ##### LOOK & FEEL ##############################################################
462 |
463 | # Set the path to the theme directory that determines how your CAS pages look.
464 | #
465 | # Custom themes are not well supported yet, but will be in the near future. In
466 | # the meantime, if you want to create a custom theme, you can create a
467 | # subdirectory under the CASServer's themes dir (for example,
468 | # '/usr/lib/ruby/1.8/gems/casserver-xxx/public/themes', if you installed CASServer
469 | # on Linux as a gem). A theme is basically just a theme.css file that overrides
470 | # the themes/cas.css styles along with a collection of image files
471 | # like logo.png and bg.png.
472 | #
473 | # By default, we use the 'simple' theme which you can find in themes/simple.
474 | theme: simple
475 |
476 | # The name of your company/organization. This will show up on the login page.
477 | organization: CAS
478 |
479 | # A short bit of text that shows up on the login page. You can make this blank
480 | # if you prefer to have no extra text shown at the bottom of the login box.
481 | infoline: Powered by RubyCAS-Server
482 |
483 | # Custom views directory. If set, this will be used instead of 'lib/casserver/views'.
484 | #custom_views: /path/to/custom/views
485 |
486 | # Custom public directory. If set, static content (css, etc.) will be served from here rather
487 | # than from rubycas-server's internal 'public' directory (but be mindful of any overriding
488 | # settings you may have in your web server's config).
489 | #public_dir: /path/to/custom/public
490 |
491 | ##### LOCALIZATION (L10N) #######################################################
492 | # The server will attempt to detect the user's locale and show text in the
493 | # appropriate language based on:
494 | #
495 | # 1. The 'lang' URL parameter (if any)
496 | # 2. The 'lang' cookie (if any)
497 | # 3. The HTTP_ACCEPT_LANGUAGE header supplied by the user's browser.
498 | # 4. The HTTP_USER_AGENT header supplied by the user's browser.
499 | #
500 | # If the locale cannot be established based on one of the above checks (in the
501 | # shown order), then the below 'default_locale' option will be used.
502 | #
503 | # The format is the same as standard linux locales (langagecode_COUNTRYCODE):
504 | #
505 | # ru_RU - Russian, Russia
506 | # eo_AQ - Esperanto, Antarctica
507 | #
508 | # It will also work if you leave out the region (i.e. just "ru" for Russian,
509 | # "eo" for Esperanto).
510 | #
511 | # If you are interested in contributing new translations or have corrections
512 | # to the existing translations, see
513 | # http://code.google.com/p/rubycas-server/wiki/HowToContribueTranslations
514 | #
515 | default_locale: en
516 |
517 | ##### LOGGING ##################################################################
518 |
519 | # Configure general logging. This log is where you'll want to look in case of
520 | # problems.
521 | #
522 | # You may want to change the file to something like /var/log/casserver.log
523 | # Set the level to DEBUG if you want more detailed logging.
524 |
525 | log:
526 | file: /var/log/casserver.log
527 | level: INFO
528 |
529 |
530 | # If you want full database logging, uncomment this next section.
531 | # Every SQL query will be logged here. This is useful for debugging database
532 | # problems.
533 |
534 | #db_log:
535 | # file: /var/log/casserver_db.log
536 |
537 |
538 | # Setting the following option to true will disable CLI output to stdout.
539 | # i.e. this will get rid of messages like ">>> Redirecting RubyCAS-Server log..."
540 | # This is useful when, for example, you're running rspecs.
541 |
542 | #quiet: true
543 |
544 |
545 | ##### SINGLE SIGN-OUT ##########################################################
546 |
547 | # When a user logs in to a CAS-enabled client application, that application
548 | # generally opens its own local user session. When the user then logs out
549 | # through the CAS server, each of the CAS-enabled client applications need
550 | # to be notified so that they can close their own local sessions for that user.
551 | #
552 | # Up until recently this was not possible within CAS. However, a method for
553 | # performing this notification was recently added to the protocol (in CAS 3.1).
554 | # This works exactly as described above -- when the user logs out, the CAS
555 | # server individually contacts each client service and notifies it of the
556 | # logout. Currently not all client applications support this, so this
557 | # behaviour is disabled by default. To enable it, uncomment the following
558 | # configuration line. Note that currently it is not possible to enable
559 | # or disable single-sign-out on a per-service basis, but this functionality
560 | # is planned for a future release.
561 |
562 | #enable_single_sign_out: true
563 |
564 |
565 | ##### OTHER ####################################################################
566 |
567 | # You can set various ticket expiry times (specify the value in seconds).
568 |
569 | # Unused login and service tickets become unusable this many seconds after
570 | # they are created. (Defaults to 5 minutes)
571 |
572 | #maximum_unused_login_ticket_lifetime: 300
573 | #maximum_unused_service_ticket_lifetime: 300
574 |
575 | # The server must periodically delete old tickets (login tickets, service tickets
576 | # proxy-granting tickets, and ticket-granting tickets) to prevent buildup of
577 | # stale data. This effectively limits the maximum length of a CAS session to
578 | # the lifetime given here (in seconds). (Defaults to 48 hours)
579 | #
580 | # Note that this limit is not enforced on the client side; it refers only to the
581 | # the maximum lifetime of tickets on the CAS server.
582 |
583 | #maximum_session_lifetime: 172800
584 |
585 |
586 | # If you want the usernames entered on the login page to be automatically
587 | # downcased (converted to lowercase), enable the following option. When this
588 | # option is set to true, if the user enters "JSmith" as their username, the
589 | # system will automatically
590 | # convert this to "jsmith".
591 |
592 | #downcase_username: true
593 |
--------------------------------------------------------------------------------