devise-radius-authenticatable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/.gitignore +7 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +3 -0
  5. data/MIT-LICENSE +21 -0
  6. data/README.md +83 -0
  7. data/Rakefile +16 -0
  8. data/devise-radius-authenticatable.gemspec +32 -0
  9. data/lib/devise/models/radius_authenticatable.rb +131 -0
  10. data/lib/devise/radius_authenticatable/version.rb +5 -0
  11. data/lib/devise/radius_authenticatable.rb +32 -0
  12. data/lib/devise/strategies/radius_authenticatable.rb +27 -0
  13. data/lib/devise-radius-authenticatable.rb +2 -0
  14. data/lib/generators/devise_radius_authenticatable/install_generator.rb +78 -0
  15. data/spec/devise/models/radius_authenticatable_spec.rb +136 -0
  16. data/spec/factories/admins.rb +10 -0
  17. data/spec/fixtures/devise.rb +238 -0
  18. data/spec/generators/install_generator_spec.rb +46 -0
  19. data/spec/integration/radius_authenticatable_spec.rb +101 -0
  20. data/spec/rails_app/.gitignore +15 -0
  21. data/spec/rails_app/Gemfile +4 -0
  22. data/spec/rails_app/Rakefile +7 -0
  23. data/spec/rails_app/app/assets/images/rails.png +0 -0
  24. data/spec/rails_app/app/assets/javascripts/application.js +15 -0
  25. data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
  26. data/spec/rails_app/app/controllers/admins_controller.rb +83 -0
  27. data/spec/rails_app/app/controllers/application_controller.rb +11 -0
  28. data/spec/rails_app/app/helpers/application_helper.rb +2 -0
  29. data/spec/rails_app/app/mailers/.gitkeep +0 -0
  30. data/spec/rails_app/app/models/.gitkeep +0 -0
  31. data/spec/rails_app/app/models/admin.rb +13 -0
  32. data/spec/rails_app/app/views/admins/_form.html.erb +17 -0
  33. data/spec/rails_app/app/views/admins/edit.html.erb +6 -0
  34. data/spec/rails_app/app/views/admins/index.html.erb +21 -0
  35. data/spec/rails_app/app/views/admins/new.html.erb +5 -0
  36. data/spec/rails_app/app/views/admins/show.html.erb +5 -0
  37. data/spec/rails_app/app/views/devise/confirmations/new.html.erb +12 -0
  38. data/spec/rails_app/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  39. data/spec/rails_app/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  40. data/spec/rails_app/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  41. data/spec/rails_app/app/views/devise/passwords/edit.html.erb +16 -0
  42. data/spec/rails_app/app/views/devise/passwords/new.html.erb +12 -0
  43. data/spec/rails_app/app/views/devise/registrations/edit.html.erb +25 -0
  44. data/spec/rails_app/app/views/devise/registrations/new.html.erb +18 -0
  45. data/spec/rails_app/app/views/devise/sessions/new.html.erb +17 -0
  46. data/spec/rails_app/app/views/devise/shared/_links.erb +25 -0
  47. data/spec/rails_app/app/views/devise/unlocks/new.html.erb +12 -0
  48. data/spec/rails_app/app/views/layouts/application.html.erb +25 -0
  49. data/spec/rails_app/config/application.rb +62 -0
  50. data/spec/rails_app/config/boot.rb +6 -0
  51. data/spec/rails_app/config/database.yml +25 -0
  52. data/spec/rails_app/config/environment.rb +5 -0
  53. data/spec/rails_app/config/environments/development.rb +37 -0
  54. data/spec/rails_app/config/environments/production.rb +67 -0
  55. data/spec/rails_app/config/environments/test.rb +37 -0
  56. data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  57. data/spec/rails_app/config/initializers/devise.rb +278 -0
  58. data/spec/rails_app/config/initializers/inflections.rb +15 -0
  59. data/spec/rails_app/config/initializers/mime_types.rb +5 -0
  60. data/spec/rails_app/config/initializers/secret_token.rb +7 -0
  61. data/spec/rails_app/config/initializers/session_store.rb +8 -0
  62. data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
  63. data/spec/rails_app/config/locales/devise.en.yml +58 -0
  64. data/spec/rails_app/config/locales/en.yml +5 -0
  65. data/spec/rails_app/config/routes.rb +66 -0
  66. data/spec/rails_app/config.ru +4 -0
  67. data/spec/rails_app/db/migrate/20120627042556_devise_create_admins.rb +48 -0
  68. data/spec/rails_app/db/schema.rb +37 -0
  69. data/spec/rails_app/db/seeds.rb +7 -0
  70. data/spec/rails_app/lib/assets/.gitkeep +0 -0
  71. data/spec/rails_app/lib/tasks/.gitkeep +0 -0
  72. data/spec/rails_app/public/404.html +26 -0
  73. data/spec/rails_app/public/422.html +26 -0
  74. data/spec/rails_app/public/500.html +25 -0
  75. data/spec/rails_app/public/favicon.ico +0 -0
  76. data/spec/rails_app/public/robots.txt +5 -0
  77. data/spec/rails_app/script/rails +6 -0
  78. data/spec/spec_helper.rb +25 -0
  79. data/spec/support/devise_helpers.rb +18 -0
  80. data/spec/support/generator_helpers.rb +19 -0
  81. data/spec/support/radius_helpers.rb +84 -0
  82. metadata +389 -0
data/.gitignore ADDED
@@ -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
+
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ bundler_args: --binstubs
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/MIT-LICENSE ADDED
@@ -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
+
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ Devise Radius Authenticatable
2
+ =============================
3
+
4
+ [![Build Status](https://secure.travis-ci.org/cbascom/devise-radius-authenticatable.png)](http://travis-ci.org/cbascom/devise-radius-authenticatable)
5
+
6
+ Devise Radius Authenticatable is a Radius authentication strategy for [Devise](http://github.com/plataformatec/devise).
7
+
8
+ Dependencies
9
+ ------------
10
+
11
+ - Rails ~> 3.2
12
+ - Devise ~> 2.0
13
+ - radiustar ~> 0.0.6
14
+
15
+ Installation
16
+ ------------
17
+
18
+ In the Gemfile for your application:
19
+
20
+ gem "devise", "~> 2.0"
21
+ gem "devise-radius-authenticatable"
22
+
23
+ Setup
24
+ -----
25
+
26
+ Run the rails generators for devise (please check the [devise](http://github.com/plataformatec/devise) documents for further instructions)
27
+
28
+ rails generate devise:install
29
+ rails generate devise MODEL_NAME
30
+
31
+ Run the rails generator for devise-radius-authenticatable. Note that the generator is named with underscores instead of hyphens due to rails restrictions.
32
+
33
+ rails generate devise_radius_authenticatable:install <IP> <SECRET> [options]
34
+
35
+ 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:
36
+
37
+ Options:
38
+
39
+ [--uid-field=UID_FIELD] # What database column to use for the UID
40
+ # Default: uid
41
+ [--port=PORT] # The port to connect to the radius server on
42
+ # Default: 1812
43
+ [--timeout=TIMEOUT] # How long to wait for a response from the radius server
44
+ # Default: 60
45
+ [--retries=RETRIES] # How many times to retry a radius request
46
+ # Default: 0
47
+
48
+ Documentation
49
+ -------------
50
+
51
+ The rdocs for the gem are available here: http://rubydoc.info/github/cbascom/devise-radius-authenticatable/master/frames
52
+
53
+ Usage
54
+ -----
55
+
56
+ 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:
57
+
58
+ config.warden do |warden_config|
59
+ warden_config.default_strategies(:token_authenticatable,
60
+ :database_authenticatable,
61
+ :radius_authenticatable,
62
+ {:scope => :admin})
63
+ end
64
+
65
+ 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.
66
+
67
+ 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.
68
+
69
+ Configuration
70
+ -------------
71
+
72
+ 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 describe previously.
73
+
74
+ References
75
+ ----------
76
+
77
+ * [FreeRadius](http://www.freeradius.org/)
78
+ * [Devise](http://github.com/plataformatec/devise)
79
+ * [Warden](http://github.com/hassox/warden)
80
+
81
+ Released under the MIT license
82
+
83
+ Copyright (c) 2012 Calvin Bascom
data/Rakefile ADDED
@@ -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-radius-authenticatable"
7
+ s.version = Devise::RadiusAuthenticatable::VERSION.dup
8
+ s.platform = Gem::Platform::RUBY
9
+ s.summary = "Devise extension to allow authentication via Radius"
10
+ s.email = "cbascom@gmail.com"
11
+ s.homepage = "http://github.com/cbascom/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. When a user attempts to authenticate via radius, the radiustar gem is used to perform the authentication with the radius server. 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 = ['Calvin Bascom']
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- spec/*`.split("\n")
17
+ s.require_paths = ["lib"]
18
+
19
+ s.add_dependency('devise', '~> 2.0')
20
+ s.add_dependency('radiustar', '~> 0.0.6')
21
+
22
+ s.add_development_dependency('rake', '~> 0.9')
23
+ s.add_development_dependency('rails', '~> 3.2')
24
+ s.add_development_dependency('jquery-rails', '~> 2.0')
25
+ s.add_development_dependency('sqlite3', '~> 1.3')
26
+ s.add_development_dependency('rspec', '~> 2.10')
27
+ s.add_development_dependency('rspec-rails', '~> 2.10')
28
+ s.add_development_dependency('factory_girl', '~> 3.4')
29
+ s.add_development_dependency('capybara', '~> 1.1')
30
+ s.add_development_dependency('launchy')
31
+ s.add_development_dependency('ammeter', '~> 0.2')
32
+ end
@@ -0,0 +1,131 @@
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_server_port+: The port the radius server is listening on.
30
+ # * +radius_server_secret+: The shared secret configured on the radius server.
31
+ # * +radius_server_timeout+: The number of seconds to wait for a response from the
32
+ # radius server.
33
+ # * +radius_server_retries+: The number of times to retry a request to the radius
34
+ # server.
35
+ # * +radius_uid_field+: The database column to store the UID in
36
+ # * +radius_uid_generator+: A proc that takes the username and server as parameters
37
+ # and returns a string representing the UID
38
+ #
39
+ # == Callbacks
40
+ #
41
+ # The +after_radius_authentication+ callback is invoked on the user record when
42
+ # radius authentication succeeds for that user but prior to Devise checking if the
43
+ # user is active for authentication. Its default implementation simply saves the
44
+ # user record with validations disabled. This method should be overriden if further
45
+ # actions should be taken to make the user valid or active for authentication. If
46
+ # you override it, be sure to either call super to save the record or to save the
47
+ # record yourself.
48
+ module RadiusAuthenticatable
49
+ extend ActiveSupport::Concern
50
+
51
+ included do
52
+ attr_accessor :radius_attributes
53
+ end
54
+
55
+ # Use the currently configured radius server to attempt to authenticate the
56
+ # supplied username and password. If authentication succeeds, make the radius
57
+ # attributes returned by the server available via the radius_attributes accessor.
58
+ # Returns true if authentication was successful and false otherwise.
59
+ #
60
+ # Parameters::
61
+ # * +username+: The username to send to the radius server
62
+ # * +password+: The password to send to the radius server
63
+ def valid_radius_password?(username, password)
64
+ server = self.class.radius_server
65
+ port = self.class.radius_server_port
66
+ secret = self.class.radius_server_secret
67
+ options = {
68
+ :reply_timeout => self.class.radius_server_timeout,
69
+ :retries_number => self.class.radius_server_retries
70
+ }
71
+
72
+ req = Radiustar::Request.new("#{server}:#{port}", options)
73
+ reply = req.authenticate(username, password, secret)
74
+
75
+ if reply[:code] == 'Access-Accept'
76
+ reply.extract!(:code)
77
+ self.radius_attributes = reply
78
+ true
79
+ else
80
+ false
81
+ end
82
+ end
83
+
84
+ # Callback invoked by the RadiusAuthenticatable strategy after authentication
85
+ # with the radius server has succeeded and devise has indicated the model is valid.
86
+ # This callback is invoked prior to devise checking if the model is active for
87
+ # authentication.
88
+ def after_radius_authentication
89
+ self.save(:validate => false)
90
+ end
91
+
92
+ module ClassMethods
93
+
94
+ Devise::Models.config(self, :radius_server, :radius_server_port,
95
+ :radius_server_secret, :radius_server_timeout,
96
+ :radius_server_retries, :radius_uid_field,
97
+ :radius_uid_generator)
98
+
99
+ # Invoked by the RadiusAuthenticatable stratgey to perform the authentication
100
+ # against the radius server. The username is extracted from the authentication
101
+ # hash and a UID is generated from the username and server IP. We then search
102
+ # for an existing resource using the UID and configured UID field. If no resource
103
+ # is found, a new resource is built (not created). If authentication is
104
+ # successful the callback is responsible for saving the resource. Returns the
105
+ # resource if authentication succeeds and nil if it does not.
106
+ def find_for_radius_authentication(authentication_hash)
107
+ uid_field = self.radius_uid_field.to_sym
108
+ username, password = radius_credentials(authentication_hash)
109
+ uid = self.radius_uid_generator.call(username, self.radius_server)
110
+
111
+ resource = find_for_authentication({ uid_field => uid }) ||
112
+ new(uid_field => uid)
113
+
114
+ resource.valid_radius_password?(username, password) ? resource : nil
115
+ end
116
+
117
+ # Extract the username and password from the supplied authentication hash. The
118
+ # username is extracted using the first value from +Devise.authentication_keys+.
119
+ # The username is converted to lowercase if the authentication key is in the list
120
+ # of case insensitive keys configured for Devise.
121
+ def radius_credentials(authentication_hash)
122
+ key = self.authentication_keys.first
123
+ value = authentication_hash[key]
124
+ value.downcase! if (self.case_insensitive_keys || []).include?(key)
125
+
126
+ [value, authentication_hash[:password]]
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,5 @@
1
+ module Devise
2
+ module RadiusAuthenticatable
3
+ VERSION = "0.0.1".freeze
4
+ end
5
+ end
@@ -0,0 +1,32 @@
1
+ require 'devise'
2
+
3
+ module Devise
4
+ # The hostname or IP address of the radius server
5
+ mattr_accessor :radius_server
6
+
7
+ # The port for the radius server
8
+ mattr_accessor :radius_server_port
9
+ @@radius_server_port = 1812
10
+
11
+ # The secret for the radius server
12
+ mattr_accessor :radius_server_secret
13
+
14
+ # The timeout in seconds for radius requests
15
+ mattr_accessor :radius_server_timeout
16
+ @@radius_server_timeout = 60
17
+
18
+ # The number of times to retry radius requests
19
+ mattr_accessor :radius_server_retries
20
+ @@radius_server_retries = 0
21
+
22
+ # The database column that holds the unique identifier for the radius user
23
+ mattr_accessor :radius_uid_field
24
+ @@radius_uid_field = :radius_uid
25
+
26
+ # The procedure to use to build the unique identifier for the radius user
27
+ mattr_accessor :radius_uid_generator
28
+ @@radius_uid_generator = Proc.new { |username, server| "#{username}@#{server}" }
29
+ end
30
+
31
+ Devise.add_module(:radius_authenticatable, :route => :session, :strategy => true,
32
+ :controller => :sessions, :model => true)
@@ -0,0 +1,27 @@
1
+ require 'devise/strategies/authenticatable'
2
+
3
+ module Devise
4
+ module Strategies
5
+ # Strategy for authenticating users with a radius server. If authentication with
6
+ # the radius server fails, allow warden to move on to the next strategy. When
7
+ # authentication succeeds and Devise indicates that the resource has been
8
+ # successfully validated, invoke the +after_radius_authentication+ callback on the
9
+ # resource and let warden know we were successful and not to continue with executing
10
+ # further strategies.
11
+ class RadiusAuthenticatable < Authenticatable
12
+ # Invoked by warden to execute the strategy.
13
+ def authenticate!
14
+ resource = valid_password? &&
15
+ mapping.to.find_for_radius_authentication(params[scope])
16
+ return fail(:invalid) unless resource
17
+
18
+ if validate(resource)
19
+ resource.after_radius_authentication
20
+ success!(resource)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ Warden::Strategies.add(:radius_authenticatable, Devise::Strategies::RadiusAuthenticatable)
@@ -0,0 +1,2 @@
1
+ require 'devise/radius_authenticatable'
2
+
@@ -0,0 +1,78 @@
1
+ module DeviseRadiusAuthenticatable
2
+ class InstallGenerator < Rails::Generators::Base
3
+ source_root File.expand_path("../../templates", __FILE__)
4
+
5
+ desc <<-DESC.gsub(/ {6}/, '')
6
+ Description:
7
+ Adds radius_authenticatable strategy to the devise initializer
8
+
9
+ <SERVER IP> - The IP address of the radius server
10
+ <SHARED SECRET> - The shared secret for the radius server
11
+ DESC
12
+
13
+ argument(:server, :banner => '<SERVER IP>',
14
+ :desc => 'The IP address of the radius server')
15
+ argument(:secret, :banner => '<SHARED SECRET>',
16
+ :desc => 'The shared secret for the radius server')
17
+ class_option(:uid_field, :default => :uid,
18
+ :desc => 'What database column to use for the UID')
19
+ class_option(:port, :default => 1812,
20
+ :desc => 'The port to connect to the radius server on')
21
+ class_option(:timeout, :default => 60,
22
+ :desc => 'How long to wait for a response from the radius server')
23
+ class_option(:retries, :default => 0,
24
+ :desc => 'How many times to retry a radius request')
25
+
26
+ def install
27
+ inject_into_file("config/initializers/devise.rb", default_devise_settings,
28
+ :before => /^\s*.*==> Scopes configuration/)
29
+ end
30
+
31
+ private
32
+
33
+ def default_devise_settings
34
+ <<-CONFIG.gsub(/ {6}/, '')
35
+
36
+ # ==> Configuration for radius_authenticatable
37
+ # The radius_authenticatable strategy can be used in place of the
38
+ # database_authenticatable strategy or alongside it. The default order of the
39
+ # strategies is the reverse of how they were loaded. You can control this
40
+ # order by explicitly telling warden the order in which to apply the strategies.
41
+ # See the Warden Configuration section for further details.
42
+ #
43
+ # Configure the hostname or IP address of the radius server to use.
44
+ config.radius_server = '#{server}'
45
+
46
+ # Configure the port to use when connecting to the radius server.
47
+ config.radius_server_port = #{options[:port]}
48
+
49
+ # Configure the shared secret needed to connect to the radius server.
50
+ config.radius_server_secret = '#{secret}'
51
+
52
+ # Configure the time in seconds to wait for a radius server to respond.
53
+ config.radius_server_timeout = #{options[:timeout]}
54
+
55
+ # Configure the number of times a request should be retried when a radius server
56
+ # does not immediately respond to requests.
57
+ config.radius_server_retries = #{options[:retries]}
58
+
59
+ # In some cases you may want to support authentication attempts against
60
+ # multiple radius servers. In these cases the same username could be used on
61
+ # each of the servers. In order to create unique database records, a unique
62
+ # username is generated by using the radius username and the radius server IP
63
+ # address once the authentication has succeeded. This configuration option
64
+ # allows you to chose which database column this calculated UID field will be
65
+ # stored in.
66
+ config.radius_uid_field = :#{options[:uid_field]}
67
+
68
+ # If you want to control how the unique identifier is created for each radius
69
+ # user, this can be customized by configuring a proc that accepts the username
70
+ # and the radius server as parameters and returns the uid.
71
+ #
72
+ # config.radius_uid_generator = Proc.new do |username, server|
73
+ # "\#{username}@\#{server}"
74
+ # end
75
+ CONFIG
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,136 @@
1
+ require 'spec_helper'
2
+
3
+ class Configurable < Admin
4
+ devise(:radius_authenticatable, :radius_server => '1.2.3.4',
5
+ :radius_server_port => 1813, :radius_server_secret => 'secret',
6
+ :radius_server_timeout => 120, :radius_server_retries => 3,
7
+ :radius_uid_field => :email,
8
+ :radius_uid_generator => Proc.new { |username, server|
9
+ "#{username}_#{server}"
10
+ })
11
+ end
12
+
13
+ describe Devise::Models::RadiusAuthenticatable do
14
+ let(:auth_key) { Devise.authentication_keys.first }
15
+
16
+ it "allows configuration of the radius server IP" do
17
+ Configurable.radius_server.should == '1.2.3.4'
18
+ end
19
+
20
+ it "allows configuration of the radius server port" do
21
+ Configurable.radius_server_port.should == 1813
22
+ end
23
+
24
+ it "allows configuration of the radius server shared secret" do
25
+ Configurable.radius_server_secret.should == 'secret'
26
+ end
27
+
28
+ it "allows configuration of the radius server timeout" do
29
+ Configurable.radius_server_timeout.should == 120
30
+ end
31
+
32
+ it "allows configuration of the radius server retries" do
33
+ Configurable.radius_server_retries.should == 3
34
+ end
35
+
36
+ it "allows configuration of the radius uid field" do
37
+ Configurable.radius_uid_field.should == :email
38
+ end
39
+
40
+ it "allows configuration of the radius uid generator" do
41
+ Configurable.radius_uid_generator.call('test', '1.2.3.4').should == 'test_1.2.3.4'
42
+ end
43
+
44
+ it "extracts radius credentials based on the configured authentication keys" do
45
+ swap(Devise, :authentication_keys => [:username, :domain]) do
46
+ auth_hash = { :username => 'cbascom', :password => 'testing' }
47
+ Configurable.radius_credentials(auth_hash).should == ['cbascom', 'testing']
48
+ end
49
+ end
50
+
51
+ it "converts the username to lower case if the key is case insensitive" do
52
+ swap(Devise, {:authentication_keys => [:username, :domain],
53
+ :case_insensitive_keys => [:username]}) do
54
+ auth_hash = { :username => 'Cbascom', :password => 'testing' }
55
+ Configurable.radius_credentials(auth_hash).should == ['cbascom', 'testing']
56
+ end
57
+ end
58
+
59
+ it "does not convert the username to lower case if the key is not case insensitive" do
60
+ swap(Devise, {:authentication_keys => [:username, :domain],
61
+ :case_insensitive_keys => []}) do
62
+ auth_hash = { :username => 'Cbascom', :password => 'testing' }
63
+ Configurable.radius_credentials(auth_hash).should == ['Cbascom', 'testing']
64
+ end
65
+ end
66
+
67
+ context "when finding the user record for authentication" do
68
+ let(:good_auth_hash) { {auth_key => 'testuser', :password => 'password'} }
69
+ let(:bad_auth_hash) { {auth_key => 'testuser', :password => 'wrongpassword'} }
70
+
71
+ before do
72
+ @uid_field = Admin.radius_uid_field.to_sym
73
+ @uid = Admin.radius_uid_generator.call('testuser', Admin.radius_server)
74
+ create_radius_user('testuser', 'password')
75
+ end
76
+
77
+ it "uses the generated uid and configured uid field to find the record" do
78
+ Admin.should_receive(:find_for_authentication).with(@uid_field => @uid)
79
+ Admin.find_for_radius_authentication(good_auth_hash)
80
+ end
81
+
82
+ context "and authentication succeeds" do
83
+ it "creates a new user record if none was found" do
84
+ Admin.find_for_radius_authentication(good_auth_hash).should be_new_record
85
+ end
86
+
87
+ it "fills in the uid when creating the new record" do
88
+ admin = Admin.find_for_radius_authentication(good_auth_hash)
89
+ admin.send(@uid_field).should == @uid
90
+ end
91
+
92
+ it "uses the existing user record when one is found" do
93
+ admin = FactoryGirl.create(:admin, @uid_field => @uid)
94
+ Admin.find_for_radius_authentication(good_auth_hash).should == admin
95
+ end
96
+ end
97
+
98
+ context "and authentication fails" do
99
+ it "does not create a new user record" do
100
+ Admin.find_for_radius_authentication(bad_auth_hash).should be_nil
101
+ end
102
+ end
103
+ end
104
+
105
+ context "when validating a radius user's password" do
106
+ before do
107
+ @admin = Admin.new
108
+ create_radius_user('testuser', 'password')
109
+ end
110
+
111
+ it "passes the configured options when building the radius request" do
112
+ server_url = "#{Admin.radius_server}:#{Admin.radius_server_port}"
113
+ server_options = {
114
+ :reply_timeout => Admin.radius_server_timeout,
115
+ :retries_number => Admin.radius_server_retries
116
+ }
117
+ @admin.valid_radius_password?('testuser', 'password')
118
+
119
+ radius_server.url.should == server_url
120
+ radius_server.options.should == server_options
121
+ end
122
+
123
+ it "returns false when the password is incorrect" do
124
+ @admin.valid_radius_password?('testuser', 'wrongpassword').should be_false
125
+ end
126
+
127
+ it "returns true when the password is correct" do
128
+ @admin.valid_radius_password?('testuser', 'password').should be_true
129
+ end
130
+
131
+ it "stores the returned attributes in the model" do
132
+ @admin.valid_radius_password?('testuser', 'password')
133
+ @admin.radius_attributes.should == radius_server.attributes('testuser')
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,10 @@
1
+ FactoryGirl.define do
2
+ sequence :admin_email do |n|
3
+ "admin#{n}@gmail.com"
4
+ end
5
+
6
+ factory :admin do
7
+ email { FactoryGirl.generate(:admin_email) }
8
+ password "password"
9
+ end
10
+ end