devise-multi-radius-authenticatable 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +21 -0
- data/README.md +85 -0
- data/Rakefile +16 -0
- data/devise-multi-radius-authenticatable.gemspec +32 -0
- data/lib/devise-radius-authenticatable.rb +8 -0
- data/lib/devise/models/radius_authenticatable.rb +187 -0
- data/lib/devise/radius_authenticatable.rb +45 -0
- data/lib/devise/radius_authenticatable/test_helpers.rb +126 -0
- data/lib/devise/radius_authenticatable/version.rb +5 -0
- data/lib/devise/strategies/radius_authenticatable.rb +30 -0
- data/lib/generators/devise_radius_authenticatable/install_generator.rb +93 -0
- data/spec/devise/models/radius_authenticatable_spec.rb +170 -0
- data/spec/factories/admins.rb +10 -0
- data/spec/fixtures/devise.rb +238 -0
- data/spec/generators/install_generator_spec.rb +66 -0
- data/spec/integration/radius_authenticatable_spec.rb +115 -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 +13 -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 +11 -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 +29 -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/bin/bundle +3 -0
- data/spec/rails_app/bin/rails +4 -0
- data/spec/rails_app/bin/rake +4 -0
- data/spec/rails_app/config.ru +4 -0
- data/spec/rails_app/config/application.rb +23 -0
- data/spec/rails_app/config/boot.rb +4 -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 +29 -0
- data/spec/rails_app/config/environments/production.rb +80 -0
- data/spec/rails_app/config/environments/test.rb +36 -0
- data/spec/rails_app/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/rails_app/config/initializers/devise.rb +308 -0
- data/spec/rails_app/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/rails_app/config/initializers/inflections.rb +16 -0
- data/spec/rails_app/config/initializers/mime_types.rb +5 -0
- data/spec/rails_app/config/initializers/secret_token.rb +12 -0
- data/spec/rails_app/config/initializers/session_store.rb +3 -0
- data/spec/rails_app/config/initializers/wrap_parameters.rb +14 -0
- data/spec/rails_app/config/locales/devise.en.yml +59 -0
- data/spec/rails_app/config/locales/en.yml +23 -0
- data/spec/rails_app/config/routes.rb +63 -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 +28 -0
- data/spec/support/devise_helpers.rb +18 -0
- data/spec/support/generator_helpers.rb +16 -0
- metadata +359 -0
checksums.yaml
ADDED
@@ -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
|
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,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
|
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-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,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
|