synapses-cas 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,22 @@
1
+ require 'casserver/authenticators/base'
2
+
3
+ require 'openid'
4
+ require 'openid/extensions/sreg'
5
+ require 'openid/extensions/pape'
6
+ require 'openid/store/memory'
7
+
8
+
9
+ # CURRENTLY UNIMPLEMENTED
10
+ # This is just starter code.
11
+ class CASServer::Authenticators::OpenID < CASServer::Authenticators::Base
12
+
13
+ def validate(credentials)
14
+ raise NotImplementedError, "The OpenID authenticator is not yet implemented. "+
15
+ "See http://code.google.com/p/rubycas-server/issues/detail?id=36 if you are interested in helping this along."
16
+
17
+ read_standard_credentials(credentials)
18
+
19
+ store = OpenID::Store::Memory.new
20
+ consumer = OpenID::Consumer.new({}, store)
21
+ end
22
+ end
@@ -0,0 +1,133 @@
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
@@ -0,0 +1,93 @@
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
@@ -0,0 +1,75 @@
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
@@ -0,0 +1,19 @@
1
+ require 'casserver/authenticators/sql'
2
+
3
+ require 'digest/md5'
4
+
5
+ # Essentially the same as the standard SQL authenticator, but this version
6
+ # assumes that your password is stored as an MD5 hash.
7
+ #
8
+ # This was contributed by malcomm for Drupal authentication. To work with
9
+ # Drupal, you should use 'name' for the :username_column config option, and
10
+ # 'pass' for the :password_column.
11
+ class CASServer::Authenticators::SQLMd5 < CASServer::Authenticators::SQL
12
+
13
+ protected
14
+ def read_standard_credentials(credentials)
15
+ super
16
+ @password = Digest::MD5.hexdigest(@password)
17
+ end
18
+
19
+ end
@@ -0,0 +1,82 @@
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,@options[:site_key],@options[:digest_streches])
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,site_key,digest_streches)
67
+ password_digest(password, self.salt,site_key,digest_streches)
68
+ end
69
+
70
+ def secure_digest(*args)
71
+ Digest::SHA1.hexdigest(args.flatten.join('--'))
72
+ end
73
+
74
+ def password_digest(password, salt,site_key,digest_streches)
75
+ digest = site_key
76
+ digest_streches.times do
77
+ digest = secure_digest(digest, salt, password, site_key)
78
+ end
79
+ digest
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,22 @@
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