devise-radius-authenticatable 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +21 -0
- data/README.md +83 -0
- data/Rakefile +16 -0
- data/devise-radius-authenticatable.gemspec +32 -0
- data/lib/devise/models/radius_authenticatable.rb +131 -0
- data/lib/devise/radius_authenticatable/version.rb +5 -0
- data/lib/devise/radius_authenticatable.rb +32 -0
- data/lib/devise/strategies/radius_authenticatable.rb +27 -0
- data/lib/devise-radius-authenticatable.rb +2 -0
- data/lib/generators/devise_radius_authenticatable/install_generator.rb +78 -0
- data/spec/devise/models/radius_authenticatable_spec.rb +136 -0
- data/spec/factories/admins.rb +10 -0
- data/spec/fixtures/devise.rb +238 -0
- data/spec/generators/install_generator_spec.rb +46 -0
- data/spec/integration/radius_authenticatable_spec.rb +101 -0
- data/spec/rails_app/.gitignore +15 -0
- data/spec/rails_app/Gemfile +4 -0
- data/spec/rails_app/Rakefile +7 -0
- data/spec/rails_app/app/assets/images/rails.png +0 -0
- data/spec/rails_app/app/assets/javascripts/application.js +15 -0
- data/spec/rails_app/app/assets/stylesheets/application.css +13 -0
- data/spec/rails_app/app/controllers/admins_controller.rb +83 -0
- data/spec/rails_app/app/controllers/application_controller.rb +11 -0
- data/spec/rails_app/app/helpers/application_helper.rb +2 -0
- data/spec/rails_app/app/mailers/.gitkeep +0 -0
- data/spec/rails_app/app/models/.gitkeep +0 -0
- data/spec/rails_app/app/models/admin.rb +13 -0
- data/spec/rails_app/app/views/admins/_form.html.erb +17 -0
- data/spec/rails_app/app/views/admins/edit.html.erb +6 -0
- data/spec/rails_app/app/views/admins/index.html.erb +21 -0
- data/spec/rails_app/app/views/admins/new.html.erb +5 -0
- data/spec/rails_app/app/views/admins/show.html.erb +5 -0
- data/spec/rails_app/app/views/devise/confirmations/new.html.erb +12 -0
- data/spec/rails_app/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
- data/spec/rails_app/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
- data/spec/rails_app/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
- data/spec/rails_app/app/views/devise/passwords/edit.html.erb +16 -0
- data/spec/rails_app/app/views/devise/passwords/new.html.erb +12 -0
- data/spec/rails_app/app/views/devise/registrations/edit.html.erb +25 -0
- data/spec/rails_app/app/views/devise/registrations/new.html.erb +18 -0
- data/spec/rails_app/app/views/devise/sessions/new.html.erb +17 -0
- data/spec/rails_app/app/views/devise/shared/_links.erb +25 -0
- data/spec/rails_app/app/views/devise/unlocks/new.html.erb +12 -0
- data/spec/rails_app/app/views/layouts/application.html.erb +25 -0
- data/spec/rails_app/config/application.rb +62 -0
- data/spec/rails_app/config/boot.rb +6 -0
- data/spec/rails_app/config/database.yml +25 -0
- data/spec/rails_app/config/environment.rb +5 -0
- data/spec/rails_app/config/environments/development.rb +37 -0
- data/spec/rails_app/config/environments/production.rb +67 -0
- data/spec/rails_app/config/environments/test.rb +37 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/devise.rb +278 -0
- data/spec/rails_app/config/initializers/inflections.rb +15 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/initializers/secret_token.rb +7 -0
- data/spec/rails_app/config/initializers/session_store.rb +8 -0
- data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_app/config/locales/devise.en.yml +58 -0
- data/spec/rails_app/config/locales/en.yml +5 -0
- data/spec/rails_app/config/routes.rb +66 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/db/migrate/20120627042556_devise_create_admins.rb +48 -0
- data/spec/rails_app/db/schema.rb +37 -0
- data/spec/rails_app/db/seeds.rb +7 -0
- data/spec/rails_app/lib/assets/.gitkeep +0 -0
- data/spec/rails_app/lib/tasks/.gitkeep +0 -0
- data/spec/rails_app/public/404.html +26 -0
- data/spec/rails_app/public/422.html +26 -0
- data/spec/rails_app/public/500.html +25 -0
- data/spec/rails_app/public/favicon.ico +0 -0
- data/spec/rails_app/public/robots.txt +5 -0
- data/spec/rails_app/script/rails +6 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/devise_helpers.rb +18 -0
- data/spec/support/generator_helpers.rb +19 -0
- data/spec/support/radius_helpers.rb +84 -0
- metadata +389 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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,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,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
|