kch-rubycas-server 0.8.0.20090715

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. data/CHANGELOG.txt +1 -0
  2. data/History.txt +270 -0
  3. data/LICENSE.txt +504 -0
  4. data/Manifest.txt +85 -0
  5. data/PostInstall.txt +3 -0
  6. data/README.rdoc +26 -0
  7. data/Rakefile +4 -0
  8. data/bin/rubycas-server +13 -0
  9. data/bin/rubycas-server-ctl +9 -0
  10. data/config.example.yml +544 -0
  11. data/config.ru +38 -0
  12. data/config/hoe.rb +78 -0
  13. data/config/requirements.rb +15 -0
  14. data/custom_views.example.rb +11 -0
  15. data/lib/casserver.rb +58 -0
  16. data/lib/casserver/authenticators/active_directory_ldap.rb +11 -0
  17. data/lib/casserver/authenticators/base.rb +48 -0
  18. data/lib/casserver/authenticators/client_certificate.rb +46 -0
  19. data/lib/casserver/authenticators/google.rb +54 -0
  20. data/lib/casserver/authenticators/ldap.rb +147 -0
  21. data/lib/casserver/authenticators/ntlm.rb +88 -0
  22. data/lib/casserver/authenticators/open_id.rb +22 -0
  23. data/lib/casserver/authenticators/sql.rb +102 -0
  24. data/lib/casserver/authenticators/sql_encrypted.rb +77 -0
  25. data/lib/casserver/authenticators/sql_md5.rb +19 -0
  26. data/lib/casserver/authenticators/sql_rest_auth.rb +77 -0
  27. data/lib/casserver/authenticators/test.rb +19 -0
  28. data/lib/casserver/cas.rb +322 -0
  29. data/lib/casserver/conf.rb +75 -0
  30. data/lib/casserver/controllers.rb +456 -0
  31. data/lib/casserver/load_picnic.rb +19 -0
  32. data/lib/casserver/localization.rb +82 -0
  33. data/lib/casserver/models.rb +265 -0
  34. data/lib/casserver/postambles.rb +174 -0
  35. data/lib/casserver/utils.rb +30 -0
  36. data/lib/casserver/version.rb +9 -0
  37. data/lib/casserver/views.rb +245 -0
  38. data/lib/rubycas-server.rb +1 -0
  39. data/lib/rubycas-server/version.rb +1 -0
  40. data/po/de_DE/rubycas-server.po +119 -0
  41. data/po/es_ES/rubycas-server.po +115 -0
  42. data/po/fr_FR/rubycas-server.po +116 -0
  43. data/po/ja_JP/rubycas-server.po +118 -0
  44. data/po/pl_PL/rubycas-server.po +115 -0
  45. data/po/pt_BR/rubycas-server.po +115 -0
  46. data/po/ru_RU/rubycas-server.po +110 -0
  47. data/po/rubycas-server.pot +104 -0
  48. data/public/themes/cas.css +121 -0
  49. data/public/themes/notice.png +0 -0
  50. data/public/themes/ok.png +0 -0
  51. data/public/themes/simple/bg.png +0 -0
  52. data/public/themes/simple/login_box_bg.png +0 -0
  53. data/public/themes/simple/logo.png +0 -0
  54. data/public/themes/simple/theme.css +28 -0
  55. data/public/themes/urbacon/bg.png +0 -0
  56. data/public/themes/urbacon/login_box_bg.png +0 -0
  57. data/public/themes/urbacon/logo.png +0 -0
  58. data/public/themes/urbacon/theme.css +33 -0
  59. data/public/themes/warning.png +0 -0
  60. data/resources/init.d.sh +58 -0
  61. data/script/console +10 -0
  62. data/script/destroy +14 -0
  63. data/script/generate +14 -0
  64. data/script/txt2html +82 -0
  65. data/setup.rb +1585 -0
  66. data/tasks/deployment.rake +34 -0
  67. data/tasks/environment.rake +7 -0
  68. data/tasks/localization.rake +11 -0
  69. data/tasks/website.rake +17 -0
  70. data/vendor/isaac_0.9.1/LICENSE +26 -0
  71. data/vendor/isaac_0.9.1/README +78 -0
  72. data/vendor/isaac_0.9.1/TODO +3 -0
  73. data/vendor/isaac_0.9.1/VERSIONS +3 -0
  74. data/vendor/isaac_0.9.1/crypt/ISAAC.rb +171 -0
  75. data/vendor/isaac_0.9.1/isaac.gemspec +39 -0
  76. data/vendor/isaac_0.9.1/setup.rb +596 -0
  77. data/vendor/isaac_0.9.1/test/TC_ISAAC.rb +76 -0
  78. metadata +193 -0
@@ -0,0 +1,38 @@
1
+ # This is the Rackup initialization script for running RubyCAS-Server under Passenger/Rack.
2
+ #
3
+ # This file doesn't really have anything to do with your server's "configuration",
4
+ # and you almost certainly don't need to modify it. Instead, a config file should
5
+ # have been created for you (probably under /etc/rubycas-server/config.yml) -- this
6
+ # is the file you'll want to modify. If for some reason the configuration file
7
+ # was not created for you, have a look at the config.example.yml template and
8
+ # go from there.
9
+
10
+ require 'rubygems'
11
+ require 'rack'
12
+
13
+ $APP_NAME = 'rubycas-server'
14
+ $APP_ROOT = File.dirname(File.expand_path(__FILE__))
15
+
16
+ if File.exists?("#{$APP_ROOT}/tmp/debug.txt")
17
+ require 'ruby-debug'
18
+ Debugger.wait_connection = true
19
+ Debugger.start_remote
20
+ end
21
+
22
+ $: << $APP_ROOT + "/lib"
23
+
24
+ require 'casserver/load_picnic'
25
+ require 'picnic'
26
+ require 'casserver'
27
+
28
+ CASServer.create
29
+
30
+ if $CONF.uri_path
31
+ map($CONF.uri_path) do
32
+ # FIXME: this probably isn't the smartest way of remapping the themes dir to uri_path/themes
33
+ use Rack::Static, $CONF[:static] if $CONF[:static]
34
+ run CASServer
35
+ end
36
+ else
37
+ run CASServer
38
+ end
@@ -0,0 +1,78 @@
1
+ require 'rubycas-server/version'
2
+
3
+ AUTHOR = 'Matt Zukowski' # can also be an array of Authors
4
+ EMAIL = "matt@zukowski.ca"
5
+ DESCRIPTION = "Provides single sign-on authentication for web applications using the CAS protocol."
6
+ GEM_NAME = 'rubycas-server' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'rubycas-server' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+ EXTRA_DEPENDENCIES = [
11
+ 'activesupport',
12
+ 'activerecord',
13
+ 'locale',
14
+ 'gettext',
15
+ ['picnic', '>= 0.8.1']
16
+ ] # An array of rubygem dependencies [name, version]
17
+
18
+ @config_file = "~/.rubyforge/user-config.yml"
19
+ @config = nil
20
+ RUBYFORGE_USERNAME = "unknown"
21
+ def rubyforge_username
22
+ unless @config
23
+ begin
24
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
25
+ rescue
26
+ puts <<-EOS
27
+ ERROR: No rubyforge config file found: #{@config_file}
28
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
29
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
30
+ EOS
31
+ exit
32
+ end
33
+ end
34
+ RUBYFORGE_USERNAME.replace @config["username"]
35
+ end
36
+
37
+ ENV['NODOT'] = '1'
38
+
39
+ #REV = nil
40
+ # UNCOMMENT IF REQUIRED:
41
+ REV = Time.now.strftime('%Y%m%d%H%M')
42
+ VERS = CASServer::VERSION::STRING + (REV ? ".#{REV}" : "")
43
+ RDOC_OPTS = ['--quiet', '--title', 'rubycas-server documentation',
44
+ "--opname", "index.html",
45
+ "--line-numbers",
46
+ "--main", "README",
47
+ "--inline-source"]
48
+
49
+ class Hoe
50
+ def extra_deps
51
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
52
+ @extra_deps
53
+ end
54
+ end
55
+
56
+ # Generate all the Rake tasks
57
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
58
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
59
+ p.developer(AUTHOR, EMAIL)
60
+ p.description = DESCRIPTION
61
+ p.summary = DESCRIPTION
62
+ p.url = HOMEPATH
63
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
64
+ p.test_globs = ["test/**/test_*.rb"]
65
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
66
+
67
+ # == Optional
68
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
69
+ p.extra_deps = EXTRA_DEPENDENCIES
70
+
71
+ p.spec_extras = {:executables => ['rubycas-server', 'rubycas-server-ctl']} # A hash of extra values to set in the gemspec.
72
+ end
73
+
74
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
75
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}"
76
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
77
+ $hoe.rsync_args = '-av --delete --ignore-errors'
78
+ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
@@ -0,0 +1,11 @@
1
+ # Custom views file; add methods to the module definition below
2
+
3
+ module CASServer::Views
4
+
5
+ # Override views here, for example, a custom login form:
6
+ def login_form
7
+ # Add your custom login form here, using Markaby
8
+ # See the original views.rb file at lib/casserver/views.rb for method names and usage
9
+ end
10
+
11
+ end
@@ -0,0 +1,58 @@
1
+ unless Object.const_defined?(:Picnic)
2
+ $APP_NAME ||= 'rubycas-server'
3
+ $APP_ROOT ||= File.expand_path(File.dirname(__FILE__)+'/..')
4
+
5
+ require 'casserver/load_picnic'
6
+ end
7
+
8
+ require 'yaml'
9
+ require 'markaby'
10
+
11
+ require "casserver/conf"
12
+ require "picnic/logger"
13
+
14
+ $: << File.dirname(File.expand_path(__FILE__))
15
+
16
+ $: << File.expand_path("#{File.dirname(__FILE__)}/../vendor/isaac_0.9.1")
17
+ require 'crypt/ISAAC'
18
+
19
+ Camping.goes :CASServer
20
+
21
+ Picnic::Logger.init_global_logger!
22
+
23
+ require "casserver/utils"
24
+ require "casserver/models"
25
+ require "casserver/cas"
26
+ require "casserver/views"
27
+ require "casserver/controllers"
28
+ require "casserver/localization"
29
+
30
+ def CASServer.create
31
+ $LOG.info "Creating RubyCAS-Server with pid #{Process.pid}."
32
+
33
+
34
+ CASServer::Models::Base.establish_connection($CONF.database)
35
+ CASServer::Models.create_schema
36
+
37
+ #TODO: these warnings should eventually be deleted
38
+ if $CONF.service_ticket_expiry
39
+ $LOG.warn "The 'service_ticket_expiry' option has been renamed to 'maximum_unused_service_ticket_lifetime'. Please make the necessary change to your config file!"
40
+ $CONF.maximum_unused_service_ticket_lifetime ||= $CONF.service_ticket_expiry
41
+ end
42
+ if $CONF.login_ticket_expiry
43
+ $LOG.warn "The 'login_ticket_expiry' option has been renamed to 'maximum_unused_login_ticket_lifetime'. Please make the necessary change to your config file!"
44
+ $CONF.maximum_unused_login_ticket_lifetime ||= $CONF.login_ticket_expiry
45
+ end
46
+ if $CONF.ticket_granting_ticket_expiry || $CONF.proxy_granting_ticket_expiry
47
+ $LOG.warn "The 'ticket_granting_ticket_expiry' and 'proxy_granting_ticket_expiry' options have been renamed to 'maximum_session_lifetime'. Please make the necessary change to your config file!"
48
+ $CONF.maximum_session_lifetime ||= $CONF.ticket_granting_ticket_expiry || $CONF.proxy_granting_ticket_expiry
49
+ end
50
+
51
+ if $CONF.maximum_session_lifetime
52
+ CASServer::Models::ServiceTicket.cleanup($CONF.maximum_session_lifetime, $CONF.maximum_unused_service_ticket_lifetime)
53
+ CASServer::Models::LoginTicket.cleanup($CONF.maximum_session_lifetime, $CONF.maximum_unused_login_ticket_lifetime)
54
+ CASServer::Models::ProxyGrantingTicket.cleanup($CONF.maximum_session_lifetime)
55
+ CASServer::Models::TicketGrantingTicket.cleanup($CONF.maximum_session_lifetime)
56
+ end
57
+ end
58
+
@@ -0,0 +1,11 @@
1
+ require 'casserver/authenticators/ldap'
2
+
3
+ # Slightly modified version of the LDAP authenticator for Microsoft's ActiveDirectory.
4
+ # The only difference is that the default_username_attribute for AD is 'sAMAccountName'
5
+ # rather than 'uid'.
6
+ class CASServer::Authenticators::ActiveDirectoryLDAP < CASServer::Authenticators::LDAP
7
+ protected
8
+ def default_username_attribute
9
+ "sAMAccountName"
10
+ end
11
+ end
@@ -0,0 +1,48 @@
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
+ def validate(credentials)
9
+ raise NotImplementedError, "This method must be implemented by a class extending #{self.class}"
10
+ end
11
+
12
+ def configure(options)
13
+ raise ArgumentError, "options must be a HashWithIndifferentAccess" unless options.kind_of? HashWithIndifferentAccess
14
+ @options = options.dup
15
+ @extra_attributes = {}
16
+ end
17
+
18
+ def extra_attributes
19
+ @extra_attributes
20
+ end
21
+
22
+ protected
23
+ def read_standard_credentials(credentials)
24
+ @username = credentials[:username]
25
+ @password = credentials[:password]
26
+ @service = credentials[:service]
27
+ @request = credentials[:request]
28
+ end
29
+
30
+ def extra_attributes_to_extract
31
+ if @options[:extra_attributes].kind_of? Array
32
+ attrs = @options[:extra_attributes]
33
+ elsif @options[:extra_attributes].kind_of? String
34
+ attrs = @options[:extra_attributes].split(',').collect{|col| col.strip}
35
+ else
36
+ $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.")
37
+ attrs = []
38
+ end
39
+
40
+ $LOG.debug("#{self.class.name} will try to extract the following extra_attributes: #{attrs.inspect}")
41
+ return attrs
42
+ end
43
+ end
44
+ end
45
+
46
+ class AuthenticatorError < Exception
47
+ end
48
+ end
@@ -0,0 +1,46 @@
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
+
44
+ return true # if SSL certificate is valid, false otherwise
45
+ end
46
+ end
@@ -0,0 +1,54 @@
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
+ http = Net::HTTP.new(url.host, url.port)
26
+ http.use_ssl = true
27
+
28
+ # TODO: make the timeout configurable
29
+ wait_seconds = 10
30
+ begin
31
+ timeout(wait_seconds) do
32
+ res = http.start do |conn|
33
+ req = Net::HTTP::Post.new(url.path)
34
+ req.set_form_data(auth_data,'&')
35
+ conn.request(req)
36
+ end
37
+
38
+ case res
39
+ when Net::HTTPSuccess
40
+ true
41
+ when Net::HTTPForbidden
42
+ false
43
+ else
44
+ $LOG.error("Unexpected response from Google while validating credentials: #{res.inspect} ==> #{res.body}.")
45
+ raise CASServer::AuthenticatorError, "Unexpected response received from Google while validating credentials."
46
+ end
47
+ end
48
+ rescue Timeout::Error
49
+ $LOG.error("Google did not respond to the credential validation request. We waited for #{wait_seconds.inspect} seconds before giving up.")
50
+ raise CASServer::AuthenticatorError, "Timeout while waiting for Google to validate credentials."
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,147 @@
1
+ require 'casserver/authenticators/base'
2
+
3
+ begin
4
+ require 'net/ldap'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ begin
8
+ gem 'ruby-net-ldap', '~> 0.0.4'
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 'ruby-net-ldap' gem."
14
+ $stderr.puts
15
+ $stderr.puts "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"
16
+ exit 1
17
+ end
18
+ require 'net/ldap'
19
+ end
20
+
21
+ # Basic LDAP authenticator. Should be compatible with OpenLDAP and other similar LDAP servers,
22
+ # although it hasn't been officially tested. See example config file for details on how
23
+ # to configure it.
24
+ class CASServer::Authenticators::LDAP < CASServer::Authenticators::Base
25
+ def validate(credentials)
26
+ read_standard_credentials(credentials)
27
+
28
+ return false if @password.blank?
29
+
30
+ raise CASServer::AuthenticatorError, "Cannot validate credentials because the authenticator hasn't yet been configured" unless @options
31
+ raise CASServer::AuthenticatorError, "Invalid LDAP authenticator configuration!" unless @options[:ldap]
32
+ raise CASServer::AuthenticatorError, "You must specify a server host in the LDAP configuration!" unless @options[:ldap][:host] || @options[:ldap][:server]
33
+
34
+ raise CASServer::AuthenticatorError, "The username '#{@username}' contains invalid characters." if (@username =~ /[*\(\)\0\/]/)
35
+
36
+ preprocess_username
37
+
38
+ @ldap = Net::LDAP.new
39
+
40
+
41
+ @options[:ldap][:host] ||= @options[:ldap][:server]
42
+ @ldap.host = @options[:ldap][:host]
43
+ @ldap.port = @options[:ldap][:port] if @options[:ldap][:port]
44
+ @ldap.encryption(@options[:ldap][:encryption].intern) if @options[:ldap][:encryption]
45
+
46
+ begin
47
+ if @options[:ldap][:auth_user]
48
+ bind_success = bind_by_username_with_preauthentication
49
+ else
50
+ bind_success = bind_by_username
51
+ end
52
+
53
+ return false unless bind_success
54
+
55
+ entry = find_user
56
+ extract_extra_attributes(entry)
57
+
58
+ return true
59
+ rescue Net::LDAP::LdapError => e
60
+ raise CASServer::AuthenticatorError,
61
+ "LDAP authentication failed with '#{e}'. Check your authenticator configuration."
62
+ end
63
+ end
64
+
65
+ protected
66
+ def default_username_attribute
67
+ "cn"
68
+ end
69
+
70
+ private
71
+ # Add prefix to username, if :username_prefix was specified in the :ldap config.
72
+ def preprocess_username
73
+ @username = @options[:ldap][:username_prefix] + @username if @options[:ldap][:username_prefix]
74
+ end
75
+
76
+ # Attempt to bind with the LDAP server using the username and password entered by
77
+ # the user. If a :filter was specified in the :ldap config, the filter will be
78
+ # added to the LDAP query for the username.
79
+ def bind_by_username
80
+ username_attribute = options[:ldap][:username_attribute] || default_username_attribute
81
+
82
+ @ldap.bind_as(:base => @options[:ldap][:base], :password => @password, :filter => user_filter)
83
+ end
84
+
85
+ # If an auth_user is specified, we will connect ("pre-authenticate") with the
86
+ # LDAP server using the authenticator account, and then attempt to bind as the
87
+ # user who is actually trying to authenticate. Note that you need to set up
88
+ # the special authenticator account first. Also, auth_user must be the authenticator
89
+ # user's full CN, which is probably not the same as their username.
90
+ #
91
+ # This pre-authentication process is necessary because binding can only be done
92
+ # using the CN, so having just the username is not enough. We connect as auth_user,
93
+ # and then try to find the target user's CN based on the given username. Then we bind
94
+ # as the target user to validate their credentials.
95
+ def bind_by_username_with_preauthentication
96
+ raise CASServer::AuthenticatorError, "A password must be specified in the configuration for the authenticator user!" unless
97
+ @options[:ldap][:auth_password]
98
+
99
+ @ldap.authenticate(@options[:ldap][:auth_user], @options[:ldap][:auth_password])
100
+
101
+ @ldap.bind_as(:base => @options[:ldap][:base], :password => @password, :filter => user_filter)
102
+ end
103
+
104
+ # Combine the filter for finding the user with the optional extra filter specified in the config
105
+ # (if any).
106
+ def user_filter
107
+ username_attribute = options[:ldap][:username_attribute] || default_username_attribute
108
+
109
+ filter = Net::LDAP::Filter.eq(username_attribute, @username)
110
+ unless @options[:ldap][:filter].blank?
111
+ filter &= Net::LDAP::Filter.construct(@options[:ldap][:filter])
112
+ end
113
+
114
+ filter
115
+ end
116
+
117
+ # Finds the user based on the user_filter (this is called after authentication).
118
+ # We do this to make it possible to extract extra_attributes.
119
+ def find_user
120
+ results = @ldap.search( :base => options[:ldap][:base], :filter => user_filter)
121
+ return results.first
122
+ end
123
+
124
+ def extract_extra_attributes(ldap_entry)
125
+ @extra_attributes = {}
126
+ extra_attributes_to_extract.each do |attr|
127
+ v = !ldap_entry[attr].blank? && ldap_entry[attr].first
128
+ if v
129
+ if ldap_entry[attr].kind_of?(Array)
130
+ @extra_attributes[attr] = []
131
+ ldap_entry[attr].each do |a|
132
+ @extra_attributes[attr] << a
133
+ end
134
+ else
135
+ @extra_attributes[attr] = v.to_s
136
+ end
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