synapses-cas 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/CHANGELOG +3 -0
  2. data/Gemfile +3 -0
  3. data/LICENSE +27 -0
  4. data/README.md +20 -0
  5. data/Rakefile +2 -0
  6. data/bin/synapses-cas +30 -0
  7. data/config.ru +17 -0
  8. data/config/config.example.yml +592 -0
  9. data/config/unicorn.rb +88 -0
  10. data/db/migrate/001_create_initial_structure.rb +47 -0
  11. data/lib/casserver.rb +11 -0
  12. data/lib/casserver/authenticators/active_directory_ldap.rb +19 -0
  13. data/lib/casserver/authenticators/active_resource.rb +127 -0
  14. data/lib/casserver/authenticators/authlogic_crypto_providers/aes256.rb +43 -0
  15. data/lib/casserver/authenticators/authlogic_crypto_providers/bcrypt.rb +92 -0
  16. data/lib/casserver/authenticators/authlogic_crypto_providers/md5.rb +34 -0
  17. data/lib/casserver/authenticators/authlogic_crypto_providers/sha1.rb +59 -0
  18. data/lib/casserver/authenticators/authlogic_crypto_providers/sha512.rb +50 -0
  19. data/lib/casserver/authenticators/base.rb +67 -0
  20. data/lib/casserver/authenticators/client_certificate.rb +47 -0
  21. data/lib/casserver/authenticators/google.rb +58 -0
  22. data/lib/casserver/authenticators/ldap.rb +147 -0
  23. data/lib/casserver/authenticators/ntlm.rb +88 -0
  24. data/lib/casserver/authenticators/open_id.rb +22 -0
  25. data/lib/casserver/authenticators/sql.rb +133 -0
  26. data/lib/casserver/authenticators/sql_authlogic.rb +93 -0
  27. data/lib/casserver/authenticators/sql_encrypted.rb +75 -0
  28. data/lib/casserver/authenticators/sql_md5.rb +19 -0
  29. data/lib/casserver/authenticators/sql_rest_auth.rb +82 -0
  30. data/lib/casserver/authenticators/test.rb +22 -0
  31. data/lib/casserver/cas.rb +323 -0
  32. data/lib/casserver/localization.rb +13 -0
  33. data/lib/casserver/model.rb +270 -0
  34. data/lib/casserver/server.rb +758 -0
  35. data/lib/casserver/utils.rb +32 -0
  36. data/lib/casserver/views/_login_form.erb +42 -0
  37. data/lib/casserver/views/layout.erb +18 -0
  38. data/lib/casserver/views/login.erb +30 -0
  39. data/lib/casserver/views/proxy.builder +12 -0
  40. data/lib/casserver/views/proxy_validate.builder +25 -0
  41. data/lib/casserver/views/service_validate.builder +18 -0
  42. data/lib/casserver/views/validate.erb +2 -0
  43. data/locales/de.yml +27 -0
  44. data/locales/en.yml +26 -0
  45. data/locales/es.yml +26 -0
  46. data/locales/es_ar.yml +26 -0
  47. data/locales/fr.yml +26 -0
  48. data/locales/jp.yml +26 -0
  49. data/locales/pl.yml +26 -0
  50. data/locales/pt.yml +26 -0
  51. data/locales/ru.yml +26 -0
  52. data/locales/zh.yml +26 -0
  53. data/locales/zh_tw.yml +26 -0
  54. data/public/themes/cas.css +126 -0
  55. data/public/themes/notice.png +0 -0
  56. data/public/themes/ok.png +0 -0
  57. data/public/themes/simple/bg.png +0 -0
  58. data/public/themes/simple/favicon.png +0 -0
  59. data/public/themes/simple/login_box_bg.png +0 -0
  60. data/public/themes/simple/logo.png +0 -0
  61. data/public/themes/simple/theme.css +28 -0
  62. data/public/themes/urbacon/bg.png +0 -0
  63. data/public/themes/urbacon/login_box_bg.png +0 -0
  64. data/public/themes/urbacon/logo.png +0 -0
  65. data/public/themes/urbacon/theme.css +33 -0
  66. data/public/themes/warning.png +0 -0
  67. data/resources/init.d.sh +58 -0
  68. data/setup.rb +1585 -0
  69. data/spec/alt_config.yml +50 -0
  70. data/spec/authenticators/active_resource_spec.rb +109 -0
  71. data/spec/authenticators/ldap_spec.rb +53 -0
  72. data/spec/casserver_spec.rb +156 -0
  73. data/spec/default_config.yml +50 -0
  74. data/spec/model_spec.rb +42 -0
  75. data/spec/spec.opts +4 -0
  76. data/spec/spec_helper.rb +89 -0
  77. data/spec/utils_spec.rb +53 -0
  78. data/tasks/bundler.rake +4 -0
  79. data/tasks/db/migrate.rake +12 -0
  80. data/tasks/spec.rake +10 -0
  81. metadata +380 -0
@@ -0,0 +1,59 @@
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
@@ -0,0 +1,50 @@
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
@@ -0,0 +1,67 @@
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 Array 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
@@ -0,0 +1,47 @@
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
@@ -0,0 +1,58 @@
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
@@ -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 '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
@@ -0,0 +1,88 @@
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