devise-multi-radius-authenticatable 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +7 -0
  5. data/Gemfile +3 -0
  6. data/MIT-LICENSE +21 -0
  7. data/README.md +85 -0
  8. data/Rakefile +16 -0
  9. data/devise-multi-radius-authenticatable.gemspec +32 -0
  10. data/lib/devise-radius-authenticatable.rb +8 -0
  11. data/lib/devise/models/radius_authenticatable.rb +187 -0
  12. data/lib/devise/radius_authenticatable.rb +45 -0
  13. data/lib/devise/radius_authenticatable/test_helpers.rb +126 -0
  14. data/lib/devise/radius_authenticatable/version.rb +5 -0
  15. data/lib/devise/strategies/radius_authenticatable.rb +30 -0
  16. data/lib/generators/devise_radius_authenticatable/install_generator.rb +93 -0
  17. data/spec/devise/models/radius_authenticatable_spec.rb +170 -0
  18. data/spec/factories/admins.rb +10 -0
  19. data/spec/fixtures/devise.rb +238 -0
  20. data/spec/generators/install_generator_spec.rb +66 -0
  21. data/spec/integration/radius_authenticatable_spec.rb +115 -0
  22. data/spec/rails_app/.gitignore +15 -0
  23. data/spec/rails_app/Gemfile +4 -0
  24. data/spec/rails_app/Rakefile +7 -0
  25. data/spec/rails_app/app/assets/images/rails.png +0 -0
  26. data/spec/rails_app/app/assets/javascripts/application.js +13 -0
  27. data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
  28. data/spec/rails_app/app/controllers/admins_controller.rb +83 -0
  29. data/spec/rails_app/app/controllers/application_controller.rb +11 -0
  30. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  31. data/spec/rails_app/app/mailers/.gitkeep +0 -0
  32. data/spec/rails_app/app/models/.gitkeep +0 -0
  33. data/spec/rails_app/app/models/admin.rb +11 -0
  34. data/spec/rails_app/app/views/admins/_form.html.erb +17 -0
  35. data/spec/rails_app/app/views/admins/edit.html.erb +6 -0
  36. data/spec/rails_app/app/views/admins/index.html.erb +21 -0
  37. data/spec/rails_app/app/views/admins/new.html.erb +5 -0
  38. data/spec/rails_app/app/views/admins/show.html.erb +5 -0
  39. data/spec/rails_app/app/views/devise/confirmations/new.html.erb +12 -0
  40. data/spec/rails_app/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  41. data/spec/rails_app/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  42. data/spec/rails_app/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  43. data/spec/rails_app/app/views/devise/passwords/edit.html.erb +16 -0
  44. data/spec/rails_app/app/views/devise/passwords/new.html.erb +12 -0
  45. data/spec/rails_app/app/views/devise/registrations/edit.html.erb +29 -0
  46. data/spec/rails_app/app/views/devise/registrations/new.html.erb +18 -0
  47. data/spec/rails_app/app/views/devise/sessions/new.html.erb +17 -0
  48. data/spec/rails_app/app/views/devise/shared/_links.erb +25 -0
  49. data/spec/rails_app/app/views/devise/unlocks/new.html.erb +12 -0
  50. data/spec/rails_app/app/views/layouts/application.html.erb +25 -0
  51. data/spec/rails_app/bin/bundle +3 -0
  52. data/spec/rails_app/bin/rails +4 -0
  53. data/spec/rails_app/bin/rake +4 -0
  54. data/spec/rails_app/config.ru +4 -0
  55. data/spec/rails_app/config/application.rb +23 -0
  56. data/spec/rails_app/config/boot.rb +4 -0
  57. data/spec/rails_app/config/database.yml +25 -0
  58. data/spec/rails_app/config/environment.rb +5 -0
  59. data/spec/rails_app/config/environments/development.rb +29 -0
  60. data/spec/rails_app/config/environments/production.rb +80 -0
  61. data/spec/rails_app/config/environments/test.rb +36 -0
  62. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  63. data/spec/rails_app/config/initializers/devise.rb +308 -0
  64. data/spec/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
  65. data/spec/rails_app/config/initializers/inflections.rb +16 -0
  66. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  67. data/spec/rails_app/config/initializers/secret_token.rb +12 -0
  68. data/spec/rails_app/config/initializers/session_store.rb +3 -0
  69. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  70. data/spec/rails_app/config/locales/devise.en.yml +59 -0
  71. data/spec/rails_app/config/locales/en.yml +23 -0
  72. data/spec/rails_app/config/routes.rb +63 -0
  73. data/spec/rails_app/db/migrate/20120627042556_devise_create_admins.rb +48 -0
  74. data/spec/rails_app/db/schema.rb +37 -0
  75. data/spec/rails_app/db/seeds.rb +7 -0
  76. data/spec/rails_app/lib/assets/.gitkeep +0 -0
  77. data/spec/rails_app/lib/tasks/.gitkeep +0 -0
  78. data/spec/rails_app/public/404.html +26 -0
  79. data/spec/rails_app/public/422.html +26 -0
  80. data/spec/rails_app/public/500.html +25 -0
  81. data/spec/rails_app/public/favicon.ico +0 -0
  82. data/spec/rails_app/public/robots.txt +5 -0
  83. data/spec/rails_app/script/rails +6 -0
  84. data/spec/spec_helper.rb +28 -0
  85. data/spec/support/devise_helpers.rb +18 -0
  86. data/spec/support/generator_helpers.rb +16 -0
  87. metadata +359 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 33be02fd9225ced103b86617484e87c2008602a3
4
+ data.tar.gz: 52486855979a8293a3dc4b4cdde25cd7899273cc
5
+ SHA512:
6
+ metadata.gz: fddfcf4842b11236fc1e261191fcbdbe354cf8208c151f1a226136b49384c664607a5f4ed4da1706e24ef9b9ff0b22b4b3588840253b056c84554cc28d6dbd2b
7
+ data.tar.gz: 30f6afd9306663d2167a809654f50d38d4d983b50955833dd4736f7fed8e208d1f7dc3df9e3838771cf9a7e63405a58320372ade242ad50cd754cee86cb84055
@@ -0,0 +1,7 @@
1
+ Gemfile.lock
2
+ log
3
+ *~
4
+ *.gem
5
+ tmp
6
+ rdoc
7
+
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
6
+ bundler_args: --binstubs
7
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2012 Calvin Bascom
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,85 @@
1
+ Devise Radius Authenticatable
2
+ =============================
3
+
4
+ [![Gem Version](https://badge.fury.io/rb/devise-radius-authenticatable.png)](http://badge.fury.io/rb/devise-radius-authenticatable)
5
+ [![Build Status](https://travis-ci.org/mzaccari/devise-radius-authenticatable.png)](https://travis-ci.org/mzaccari/devise-radius-authenticatable)
6
+ [![Code Climate](https://codeclimate.com/github/cbascom/devise-radius-authenticatable.png)](https://codeclimate.com/github/cbascom/devise-radius-authenticatable)
7
+
8
+ Devise Radius Authenticatable is a Radius authentication strategy for [Devise](http://github.com/plataformatec/devise).
9
+
10
+ Dependencies
11
+ ------------
12
+
13
+ - Rails ~> 3.x or 4.x
14
+ - Devise ~> 3.x
15
+ - radiustar ~> 0.0.8
16
+
17
+ Installation
18
+ ------------
19
+
20
+ In the Gemfile for your application:
21
+
22
+ gem "devise", "~> 3.2"
23
+ gem "devise-radius-authenticatable"
24
+
25
+ Setup
26
+ -----
27
+
28
+ Run the rails generators for devise (please check the [devise](http://github.com/plataformatec/devise) documents for further instructions)
29
+
30
+ rails generate devise:install
31
+ rails generate devise MODEL_NAME
32
+
33
+ Run the rails generator for devise-radius-authenticatable. Note that the generator is named with underscores instead of hyphens due to rails restrictions.
34
+
35
+ rails generate devise_radius_authenticatable:install <IP> <SECRET> [options]
36
+
37
+ This will update the devise.rb initializer. The IP and SECRET parameters specify the IP address and shared secret for the radius server. There are also some options you can pass to the generator to customize some default settings:
38
+
39
+ Options:
40
+
41
+ [--uid-field=UID_FIELD] # What database column to use for the UID
42
+ # Default: uid
43
+ [--port=PORT] # The port to connect to the radius server on
44
+ # Default: 1812
45
+ [--timeout=TIMEOUT] # How long to wait for a response from the radius server
46
+ # Default: 60
47
+ [--retries=RETRIES] # How many times to retry a radius request
48
+ # Default: 0
49
+ [--dictionary-path=DICTIONARY_PATH] # The path to load radius dictionary files from
50
+ [--handle-timeout-as-failure=HANDLE_TIMEOUT_AS_FAILURE] # Option to handle radius timeout as authentication failure
51
+ # Default: false
52
+
53
+ Documentation
54
+ -------------
55
+
56
+ The rdocs for the gem are available here: http://rubydoc.info/github/cbascom/devise-radius-authenticatable/master/frames
57
+
58
+ Usage
59
+ -----
60
+
61
+ In order to use the radius_authenticatable strategy, you must modify your user model to use the :radius_authenticatable module. The radius_authenticatable strategy can be used standalone or along with database_authenticatable and any other strategies you wish to include. If you use radius_authenticatable alongside other authentication strategies, the default order for the strategies is determined by the order they are loaded in. The last loaded strategy will be the first strategy executed. The order of these strategies can be configured in the devise.rb initializer as follows:
62
+
63
+ config.warden do |warden_config|
64
+ warden_config.default_strategies(:database_authenticatable,
65
+ :radius_authenticatable,
66
+ {:scope => :admin})
67
+ end
68
+
69
+ The radius_authenticatable strategy will stop warden from continuing to the next strategy if authentication to the radius server is successful, but will have warden continue to the next stratgey if authentication to the radius server fails.
70
+
71
+ The field that is used for logins is the first key that's configured in the Devise `config.authentication_keys` settings, which by default is email. For help changing this, please see the [Railscast](http://railscasts.com/episodes/210-customizing-devise) that goes through how to customize Devise.
72
+
73
+ Configuration
74
+ -------------
75
+
76
+ The radius_authenticatable module is configured through the normal devise initializer `config/initializers/devise.rb`. The initial values are added to the file when you run the devise_radius_authenticatable:install generator as described previously.
77
+
78
+ References
79
+ ----------
80
+
81
+ * [FreeRadius](http://www.freeradius.org/)
82
+ * [Devise](http://github.com/plataformatec/devise)
83
+ * [Warden](http://github.com/hassox/warden)
84
+
85
+ Copyright (c) 2012-2013 Calvin Bascom Released under the MIT license
@@ -0,0 +1,16 @@
1
+ require File.expand_path('spec/rails_app/config/environment', File.dirname(__FILE__))
2
+ require 'rdoc/task'
3
+
4
+ desc 'Default: run test suite.'
5
+ task :default => :spec
6
+
7
+ desc 'Generate documentation for the devise-radius-authenticatable gem.'
8
+ Rake::RDocTask.new(:rdoc) do |rdoc|
9
+ rdoc.rdoc_dir = 'rdoc'
10
+ rdoc.title = 'Devise Radius Authenticatable'
11
+ rdoc.options << '--line-numbers'
12
+ rdoc.rdoc_files.include('README.md')
13
+ rdoc.rdoc_files.include('lib/**/*.rb')
14
+ end
15
+
16
+ RailsApp::Application.load_tasks
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path("../lib/devise/radius_authenticatable/version", __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "devise-multi-radius-authenticatable"
7
+ s.version = Devise::RadiusAuthenticatable::VERSION.dup
8
+ s.platform = Gem::Platform::RUBY
9
+ s.summary = "Devise extension to allow authentication via one or more RADIUS servers"
10
+ s.email = "michael.zaccari@gmail.com"
11
+ s.homepage = "http://github.com/mzaccari/devise-radius-authenticatable"
12
+ s.description = "A new authentication strategy named radius_authenticatable is added to the list of warden strategies when the model requests it. The radius server information is configured through the devise initializer. One or more servers may be configured. When a user attempts to authenticate via radius, the radiustar gem is used to perform the authentication with each server until a response is received. This authentication strategy can be used in place of the database_authenticatable or alongside it depending on the needs of the application."
13
+ s.authors = ['Michael Zaccari']
14
+ s.license = 'MIT'
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- spec/*`.split("\n")
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency('devise', '~> 3')
21
+ s.add_dependency('radiustar', '~> 0.0.8')
22
+
23
+ s.add_development_dependency('rake', '~> 10.2.2')
24
+ s.add_development_dependency('rails', '~> 4')
25
+ s.add_development_dependency('sqlite3', '~> 1.3')
26
+ s.add_development_dependency('rspec', '~> 2.14')
27
+ s.add_development_dependency('rspec-rails', '~> 2.14')
28
+ s.add_development_dependency('factory_girl', '~> 4.4')
29
+ s.add_development_dependency('capybara', '~> 2.2.1')
30
+ s.add_development_dependency('launchy')
31
+ s.add_development_dependency('ammeter', '~> 0.2')
32
+ end
@@ -0,0 +1,8 @@
1
+ require 'devise/radius_authenticatable'
2
+
3
+ module Devise
4
+ module RadiusAuthenticatable
5
+ autoload :TestHelpers, 'devise/radius_authenticatable/test_helpers'
6
+ end
7
+ end
8
+
@@ -0,0 +1,187 @@
1
+ require 'radiustar'
2
+ require 'devise/strategies/radius_authenticatable'
3
+
4
+ module Devise
5
+ module Models
6
+ # The RadiusAuthenticatable module is responsible for validating a user's credentials
7
+ # against the configured radius server. When authentication is successful, the
8
+ # attributes returned by the radius server are made available via the
9
+ # +radius_attributes+ accessor in the user model.
10
+ #
11
+ # The RadiusAuthenticatable module works by using the configured
12
+ # +radius_uid_generator+ to generate a UID based on the username and the radius server
13
+ # hostname or IP address. This UID is used to see if an existing record representing
14
+ # the user already exists. If it does, radius authentication proceeds through that
15
+ # user record. Otherwise, a new user record is built and authentication proceeds.
16
+ # If authentication is successful, the +after_radius_authentication+ callback is
17
+ # invoked, the default implementation of which simply saves the user record with
18
+ # validations disabled.
19
+ #
20
+ # The radius username is extracted from the parameters hash by using the first
21
+ # configured value in the +Devise.authentication_keys+ array. If the authentication
22
+ # key is in the list of case insensitive keys, the username will be converted to
23
+ # lowercase prior to authentication.
24
+ #
25
+ # == Options
26
+ #
27
+ # RadiusAuthenticable adds the following options to devise_for:
28
+ # * +radius_server+: The hostname or IP address of the radius server.
29
+ # * +radius_servers+: An array of hostnames or IP addresses for radius servers,
30
+ # with optional port.
31
+ # * +radius_server_port+: The port the radius server is listening on.
32
+ # * +radius_server_secret+: The shared secret configured on the radius server.
33
+ # * +radius_server_timeout+: The number of seconds to wait for a response from the
34
+ # radius server.
35
+ # * +radius_server_retries+: The number of times to retry a request to the radius
36
+ # server.
37
+ # * +radius_uid_field+: The database column to store the UID in
38
+ # * +radius_uid_generator+: A proc that takes the username and server as parameters
39
+ # and returns a string representing the UID
40
+ # * +radius_dictionary_path+: The path containing the radius dictionary files to load
41
+ # * +handle_radius_timeout_as_failure+: Option to handle radius timeout as authentication failure
42
+ #
43
+ # == Callbacks
44
+ #
45
+ # The +after_radius_authentication+ callback is invoked on the user record when
46
+ # radius authentication succeeds for that user but prior to Devise checking if the
47
+ # user is active for authentication. Its default implementation simply saves the
48
+ # user record with validations disabled. This method should be overriden if further
49
+ # actions should be taken to make the user valid or active for authentication. If
50
+ # you override it, be sure to either call super to save the record or to save the
51
+ # record yourself.
52
+ #
53
+ # Authorization callbacks are triggered when +after_radius_authentication is
54
+ # called:
55
+ # * +before_radius_authorization :method_name
56
+ # * +around_radius_authorization :method_name
57
+ # * +after_radius_authorization :method_name
58
+ module RadiusAuthenticatable
59
+ extend ActiveSupport::Concern
60
+
61
+ ACCESS_ACCEPT = 'Access-Accept'
62
+
63
+ included do
64
+ attr_accessor :radius_attributes
65
+ define_model_callbacks :radius_authorization
66
+ end
67
+
68
+ # Use the currently configured radius server to attempt to authenticate the
69
+ # supplied username and password. If authentication succeeds, make the radius
70
+ # attributes returned by the server available via the radius_attributes accessor.
71
+ # Returns true if authentication was successful and false otherwise.
72
+ #
73
+ # Parameters::
74
+ # * +username+: The username to send to the radius server
75
+ # * +password+: The password to send to the radius server
76
+ def valid_radius_password?(username, password)
77
+ reply = nil
78
+ secret = self.class.radius_server_secret
79
+ options = {
80
+ reply_timeout: self.class.radius_server_timeout,
81
+ retries_number: self.class.radius_server_retries
82
+ }
83
+
84
+ if self.class.radius_dictionary_path
85
+ options[:dict] = Radiustar::Dictionary.new(self.class.radius_dictionary_path)
86
+ end
87
+
88
+ self.class.radius_servers_with_ports.each do |server, port|
89
+ req = Radiustar::Request.new("#{server}:#{port}", options)
90
+
91
+ # The authenticate method will raise a RuntimeError if we time
92
+ # out waiting for a response from the server. If the server responds,
93
+ # break and process the radius response. If not, try the next server.
94
+ begin
95
+ reply = req.authenticate(username, password, secret)
96
+ break
97
+ rescue
98
+ next
99
+ end
100
+ end
101
+
102
+ # Handle the error if no servers respond.
103
+ unless reply
104
+ return false if self.class.handle_radius_timeout_as_failure
105
+ raise
106
+ end
107
+
108
+ if reply[:code] == ACCESS_ACCEPT
109
+ reply.extract!(:code)
110
+ self.radius_attributes = reply
111
+ true
112
+ else
113
+ false
114
+ end
115
+ end
116
+
117
+ # Callback invoked by the RadiusAuthenticatable strategy after
118
+ # authentication with the radius server has succeeded and devise has
119
+ # indicated the model is valid. This callback is invoked prior to devise
120
+ # checking if the model is active for authentication.
121
+ def after_radius_authentication
122
+ run_callbacks :radius_authorization do
123
+ self.save(validate: false)
124
+ end
125
+ end
126
+
127
+ module ClassMethods
128
+
129
+ Devise::Models.config(self, :radius_server, :radius_servers, :radius_server_port,
130
+ :radius_server_secret, :radius_server_timeout,
131
+ :radius_server_retries, :radius_uid_field,
132
+ :radius_uid_generator, :radius_dictionary_path,
133
+ :handle_radius_timeout_as_failure)
134
+
135
+ # Invoked by the RadiusAuthenticatable stratgey to perform the authentication
136
+ # against the radius server. The username is extracted from the authentication
137
+ # hash and a UID is generated from the username and server IP. We then search
138
+ # for an existing resource using the UID and configured UID field. If no resource
139
+ # is found, a new resource is built (not created). If authentication is
140
+ # successful the callback is responsible for saving the resource. Returns the
141
+ # resource if authentication succeeds and nil if it does not.
142
+ def find_for_radius_authentication(authentication_hash)
143
+ uid_field = self.radius_uid_field.to_sym
144
+ username, password = radius_credentials(authentication_hash)
145
+ uid = self.radius_uid_generator.call(username, self.radius_server)
146
+
147
+ resource = find_for_authentication({ uid_field => uid }) ||
148
+ new(uid_field => uid)
149
+
150
+ resource.valid_radius_password?(username, password) ? resource : nil
151
+ end
152
+
153
+ # Extract the username and password from the supplied authentication hash. The
154
+ # username is extracted using the first value from +Devise.authentication_keys+.
155
+ # The username is converted to lowercase if the authentication key is in the list
156
+ # of case insensitive keys configured for Devise.
157
+ def radius_credentials(authentication_hash)
158
+ key = self.authentication_keys.first
159
+ value = authentication_hash[key]
160
+ value.downcase! if (self.case_insensitive_keys || []).include?(key)
161
+
162
+ [value, authentication_hash[:password]]
163
+ end
164
+
165
+ # Returns an enumerable that provides each server with it's configured
166
+ # port. If no port is given with the server config, the default
167
+ # +radius_server_port is used.
168
+ def radius_servers_with_ports
169
+ @radius_servers_with_ports ||= begin
170
+ retval = []
171
+ servers = self.radius_servers
172
+ servers += [self.radius_server] if self.radius_server
173
+
174
+ servers.each do |server|
175
+ server, port = server.split(':')
176
+ port ||= self.radius_server_port
177
+
178
+ retval << [server, port]
179
+ end
180
+
181
+ retval
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,45 @@
1
+ require 'devise'
2
+
3
+ module Devise
4
+ # The hostname or IP address of the radius server
5
+ mattr_accessor :radius_server
6
+
7
+ # A list of radius servers with optional port.
8
+ # Example: ['127.0.0.1:11812', '10.20.30.40']
9
+ mattr_accessor :radius_servers
10
+ @@radius_servers = []
11
+
12
+ # The port for the radius server
13
+ mattr_accessor :radius_server_port
14
+ @@radius_server_port = 1812
15
+
16
+ # The secret for the radius server
17
+ mattr_accessor :radius_server_secret
18
+
19
+ # The timeout in seconds for radius requests
20
+ mattr_accessor :radius_server_timeout
21
+ @@radius_server_timeout = 60
22
+
23
+ # The number of times to retry radius requests
24
+ mattr_accessor :radius_server_retries
25
+ @@radius_server_retries = 0
26
+
27
+ # The database column that holds the unique identifier for the radius user
28
+ mattr_accessor :radius_uid_field
29
+ @@radius_uid_field = :radius_uid
30
+
31
+ # The procedure to use to build the unique identifier for the radius user
32
+ mattr_accessor :radius_uid_generator
33
+ @@radius_uid_generator = Proc.new { |username, server| "#{username}@#{server}" }
34
+
35
+ # The path to load radius dictionary files from
36
+ mattr_accessor :radius_dictionary_path
37
+
38
+ # Option to handle radius timeout as authentication failure
39
+ mattr_accessor :handle_radius_timeout_as_failure
40
+ @@handle_radius_timeout_as_failure = false
41
+
42
+ end
43
+
44
+ Devise.add_module(:radius_authenticatable, :route => :session, :strategy => true,
45
+ :controller => :sessions, :model => true)
@@ -0,0 +1,126 @@
1
+ require 'singleton'
2
+
3
+ module Devise
4
+ module RadiusAuthenticatable
5
+ # The Devise::RadiusAuthenticatable::TestHelpers module provides a very simple stub
6
+ # server through the RadiusServer class. It modifies the Radiustar::Request.new
7
+ # method to create a request in the stub server that can be used to check that the
8
+ # proper information is passed to the radius server. It also modifies the
9
+ # Radiustar::Request#authenticate method to perform authentication against the stub
10
+ # server.
11
+ #
12
+ # The RadiusServer class offers a simple interface for creating users prior to your
13
+ # tests. The create_radius_user method allows for the creation of a radius user
14
+ # within the stub server. The radius_server method provides the RadiusServer instance
15
+ # that test assertions can be performed against.
16
+ #
17
+ # The stub server is a singleton class to provide easy access. This means that it
18
+ # needs to have all state cleared out between tests. The clear_radius_users and
19
+ # clear_radius_request methods offer an easy way to clear the user and request info
20
+ # out of the server between tests.
21
+ module TestHelpers
22
+ def radius_server
23
+ RadiusServer.instance
24
+ end
25
+
26
+ def create_radius_user(username, password, attributes = {})
27
+ RadiusServer.instance.add_user(username, password, attributes)
28
+ end
29
+
30
+ def clear_radius_users
31
+ RadiusServer.instance.clear_users
32
+ end
33
+
34
+ def clear_radius_request
35
+ RadiusServer.instance.clear_request
36
+ end
37
+
38
+ # Stub RadiusServer that allows testing of radius authentication without a real
39
+ # server.
40
+ class RadiusServer
41
+ include Singleton
42
+
43
+ attr_reader :url, :options
44
+
45
+ def initialize
46
+ clear_users
47
+ clear_request
48
+ end
49
+
50
+ # Stores the information about the radius request that would have been sent to
51
+ # the radius server. This information can be queried to determine that the
52
+ # proper information is being sent.
53
+ def create_request(url, options)
54
+ @url = url
55
+ @options = options
56
+ end
57
+
58
+ # Clear the request information that is stored.
59
+ def clear_request
60
+ @url = nil
61
+ @options = nil
62
+ end
63
+
64
+ # Add a user to the radius server to use for authentication purposes. A couple
65
+ # of default attributes will be returned in the auth response if no attributes
66
+ # are supplied when creating the user.
67
+ def add_user(username, password, attributes = {})
68
+ @users[username] = {}
69
+ @users[username][:password] = password
70
+ if attributes.empty?
71
+ @users[username][:attributes] = {
72
+ 'User-Name' => username,
73
+ 'Filter-Id' => 60
74
+ }
75
+ else
76
+ @users[username][:attributes] = attributes
77
+ end
78
+ end
79
+
80
+ # Clear the users that have been configured for the radius server.
81
+ def clear_users
82
+ @users = {}
83
+ end
84
+
85
+ # Accessor to retrieve the attributes configured for the specified user.
86
+ def attributes(username)
87
+ @users[username][:attributes]
88
+ end
89
+
90
+ # Called to perform authentication using the specified username and password. If
91
+ # the authentication is successful, an Access-Accept is returned along with the
92
+ # radius attributes configured for the user. If authentication fails, an
93
+ # Access-Reject is returned.
94
+ def authenticate(username, password)
95
+ if @users[username] && @users[username][:password] == password
96
+ { :code => 'Access-Accept' }.merge(@users[username][:attributes])
97
+ else
98
+ { :code => 'Access-Reject' }
99
+ end
100
+ end
101
+ end
102
+
103
+ def self.included(mod)
104
+ Radiustar::Request.class_eval do
105
+ def initialize(url, options = {})
106
+ Devise::RadiusAuthenticatable::TestHelpers::RadiusServer.instance.
107
+ create_request(url, options)
108
+ end
109
+
110
+ def authenticate(username, password, secret)
111
+ Devise::RadiusAuthenticatable::TestHelpers::RadiusServer.instance.
112
+ authenticate(username, password)
113
+ end
114
+ end
115
+
116
+ if mod.respond_to?(:after)
117
+ mod.after(:each) do
118
+ Devise::RadiusAuthenticatable::TestHelpers::RadiusServer.instance.
119
+ clear_request
120
+ Devise::RadiusAuthenticatable::TestHelpers::RadiusServer.instance.clear_users
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end