shuber-authentication 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +5 -0
- data/README.markdown +58 -30
- data/lib/huberry/authentication/controller_methods.rb +17 -16
- data/lib/huberry/authentication/model_methods.rb +19 -17
- data/test/_controller_test.rb +25 -10
- metadata +2 -2
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
2009-01-09 - Sean Huber (shuber@huberry.com)
|
|
2
|
+
* Add authentication_options cattr_accessor
|
|
3
|
+
* Update README
|
|
4
|
+
* Add authentication_options cattr_accessor to models as well
|
|
5
|
+
|
|
1
6
|
2009-01-08 - Sean Huber (shuber@huberry.com)
|
|
2
7
|
* Clean up CHANGELOG
|
|
3
8
|
* Convert to soft tabs
|
data/README.markdown
CHANGED
|
@@ -12,8 +12,12 @@ Installation
|
|
|
12
12
|
script/plugin install git://github.com/shuber/authentication.git
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
Usage
|
|
16
|
+
-----
|
|
17
|
+
|
|
18
|
+
### Model ###
|
|
19
|
+
|
|
20
|
+
Simply call `uses_authentication` in your model like so:
|
|
17
21
|
|
|
18
22
|
class User < ActiveRecord::Base
|
|
19
23
|
# Accepts an optional hash of options
|
|
@@ -23,59 +27,83 @@ Example
|
|
|
23
27
|
# :salt_field - (defaults to :salt)
|
|
24
28
|
uses_authentication :login_field => :username
|
|
25
29
|
end
|
|
30
|
+
|
|
31
|
+
A few helpful methods will now be available for your model:
|
|
32
|
+
|
|
33
|
+
# Class method that authenticates a user based on a login and password - returns a user instance or false
|
|
34
|
+
User.authenticate(login, password)
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# self.authentication_model = The model that uses authentication (defaults to 'User')
|
|
30
|
-
# self.unauthenticated_message = The error flash message to set when unauthenticated (defaults to 'Login to continue')
|
|
31
|
-
# self.unauthenticated_redirect_path = The path to redirect to when unauthenticated (can be a symbol of a method) (defaults to '/')
|
|
32
|
-
end
|
|
36
|
+
# Checks if the password passed to it matches the current user instance's password
|
|
37
|
+
authenticated?(password)
|
|
33
38
|
|
|
39
|
+
# Checks if the current user instance's password has just been changed
|
|
40
|
+
password_changed?
|
|
41
|
+
|
|
42
|
+
# Resets the password - will generate a new random password if one is not specified
|
|
43
|
+
reset_password(new_password = nil)
|
|
44
|
+
|
|
45
|
+
# Resets the password and saves - will generate a new random password if one is not specified
|
|
46
|
+
reset_password!(new_password = nil)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Controller ###
|
|
50
|
+
|
|
51
|
+
Simply add `before_filter :authentication_required` for any actions that require authentication. The `:model` will then be searched
|
|
52
|
+
for a record with the id found in the `session[options[:session_field]`. The result of that query is stored in a controller instance
|
|
53
|
+
method called `current_user`. If a record could not be found, the controller's `unauthenticated` instance method is called which simply
|
|
54
|
+
redirects with a flash message. You can overwrite this method to change this behavior.
|
|
55
|
+
|
|
34
56
|
class UsersController < ApplicationController
|
|
35
57
|
before_filter :authentication_required, :only => [:index]
|
|
36
|
-
|
|
58
|
+
|
|
37
59
|
def index
|
|
38
|
-
render :text =>
|
|
60
|
+
render :text => current_user.id.to_s
|
|
39
61
|
end
|
|
40
|
-
end
|
|
41
|
-
|
|
62
|
+
end
|
|
42
63
|
|
|
43
|
-
|
|
44
|
-
------------------
|
|
64
|
+
A few helpful instance methods are available for your controller:
|
|
45
65
|
|
|
46
66
|
# Returns the current user or nil if a user is not logged in
|
|
47
67
|
current_user
|
|
48
68
|
|
|
49
|
-
# Checks if the current user is authenticated
|
|
69
|
+
# Checks if the current user is authenticated
|
|
50
70
|
logged_in?
|
|
51
71
|
|
|
52
72
|
# Login a user
|
|
53
73
|
login(user)
|
|
54
74
|
|
|
55
|
-
# A before filter to require authentication - redirects to the controller class's authentication_redirect_path if unauthenticated
|
|
56
|
-
authentication_required
|
|
57
|
-
|
|
58
75
|
# Logout the current user
|
|
59
76
|
logout
|
|
60
77
|
|
|
78
|
+
`current_user` and `logged_in?` are also helper methods so you can use them in your views.
|
|
61
79
|
|
|
62
|
-
Model Methods
|
|
63
|
-
-------------
|
|
64
80
|
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
### Controller options ###
|
|
82
|
+
|
|
83
|
+
Your controller has a class method called `authentication_options` which contains a hash with default options. You can change
|
|
84
|
+
these like so:
|
|
85
|
+
|
|
86
|
+
class ApplicationController < ActionController::Base
|
|
87
|
+
self.authentication_options.merge!{ :message => 'You are not authenticated', :redirect_to => :new_session_path }
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
The default controller authentication\_options are:
|
|
91
|
+
|
|
92
|
+
# The type of flash message to use when authentication fails. Defaults to :error.
|
|
93
|
+
:flash_type
|
|
67
94
|
|
|
68
|
-
#
|
|
69
|
-
|
|
95
|
+
# The flash message to use when authentication fails. If set to false, no flash is set. Defaults to 'Login to continue'.
|
|
96
|
+
:message
|
|
70
97
|
|
|
71
|
-
#
|
|
72
|
-
|
|
98
|
+
# The model to authenticate with. Defaults to 'User'
|
|
99
|
+
:model
|
|
73
100
|
|
|
74
|
-
#
|
|
75
|
-
|
|
101
|
+
# The session field name to store the current_user's id. Defaults to "#{options[:model].to_s.underscore}_id".to_sym (e.g. :user_id)
|
|
102
|
+
:session_field
|
|
76
103
|
|
|
77
|
-
#
|
|
78
|
-
|
|
104
|
+
# The path to redirect to if authentication fails. Accepts a string or a symbol representing an instance method to call.
|
|
105
|
+
# Defaults to '/'
|
|
106
|
+
:redirect_to
|
|
79
107
|
|
|
80
108
|
|
|
81
109
|
Contact
|
|
@@ -5,10 +5,13 @@ module Huberry
|
|
|
5
5
|
base.class_eval do
|
|
6
6
|
include InstanceMethods
|
|
7
7
|
|
|
8
|
-
cattr_accessor :
|
|
9
|
-
self.
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
cattr_accessor :authentication_options
|
|
9
|
+
self.authentication_options = {
|
|
10
|
+
:flash_type => :error,
|
|
11
|
+
:model => 'User',
|
|
12
|
+
:message => 'Login to continue',
|
|
13
|
+
:redirect_to => '/'
|
|
14
|
+
}
|
|
12
15
|
|
|
13
16
|
attr_accessor :current_user
|
|
14
17
|
helper_method :current_user, :logged_in?
|
|
@@ -25,35 +28,33 @@ module Huberry
|
|
|
25
28
|
def find_current_user(force_query = false)
|
|
26
29
|
if @queried_for_current_user.nil? || force_query
|
|
27
30
|
@queried_for_current_user = true
|
|
28
|
-
self.current_user = self.class.
|
|
31
|
+
self.current_user = self.class.authentication_options[:model].to_s.constantize.find(session[authentication_session_field]) rescue nil
|
|
29
32
|
end
|
|
30
33
|
self.current_user
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
def logged_in?
|
|
34
|
-
find_current_user
|
|
35
|
-
if self.current_user.nil?
|
|
36
|
-
block_given? ? nil : false
|
|
37
|
-
else
|
|
38
|
-
block_given? ? yield : true
|
|
39
|
-
end
|
|
37
|
+
!find_current_user.nil?
|
|
40
38
|
end
|
|
41
39
|
|
|
42
40
|
def login(user)
|
|
43
41
|
self.current_user = user
|
|
44
|
-
session[
|
|
42
|
+
session[authentication_session_field] = user.id
|
|
45
43
|
end
|
|
46
44
|
|
|
47
45
|
def logout
|
|
48
46
|
self.current_user = nil
|
|
49
|
-
session[
|
|
47
|
+
session[authentication_session_field] = nil
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def authentication_session_field
|
|
51
|
+
self.class.authentication_options[:session_field] || "#{self.class.authentication_options[:model].to_s.underscore}_id".to_sym
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
def unauthenticated
|
|
53
55
|
session[:return_to] = request.request_uri
|
|
54
|
-
flash[:
|
|
55
|
-
redirect_to respond_to?(self.class.
|
|
56
|
-
false
|
|
56
|
+
flash[self.class.authentication_options[:flash_type]] = self.class.authentication_options[:message] if self.class.authentication_options[:message]
|
|
57
|
+
redirect_to respond_to?(self.class.authentication_options[:redirect_to]) ? send(self.class.authentication_options[:redirect_to]) : self.class.authentication_options[:redirect_to].to_s
|
|
57
58
|
end
|
|
58
59
|
end
|
|
59
60
|
end
|
|
@@ -7,17 +7,19 @@ module Huberry
|
|
|
7
7
|
extend ClassMethods
|
|
8
8
|
include InstanceMethods
|
|
9
9
|
|
|
10
|
-
cattr_accessor :
|
|
11
|
-
self.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
cattr_accessor :authentication_options
|
|
11
|
+
self.authentication_options = {
|
|
12
|
+
:hashed_password_field => :hashed_password,
|
|
13
|
+
:login_field => :email,
|
|
14
|
+
:password_field => :password,
|
|
15
|
+
:salt_field => :salt
|
|
16
|
+
}.merge(options)
|
|
15
17
|
|
|
16
|
-
attr_accessor self.password_field, "#{self.password_field}_confirmation".to_sym
|
|
18
|
+
attr_accessor self.authentication_options[:password_field], "#{self.authentication_options[:password_field]}_confirmation".to_sym
|
|
17
19
|
|
|
18
|
-
validates_presence_of self.login_field
|
|
19
|
-
validates_presence_of self.password_field, :if => :password_required?
|
|
20
|
-
validates_confirmation_of self.password_field, :if => :password_required?
|
|
20
|
+
validates_presence_of self.authentication_options[:login_field]
|
|
21
|
+
validates_presence_of self.authentication_options[:password_field], :if => :password_required?
|
|
22
|
+
validates_confirmation_of self.authentication_options[:password_field], :if => :password_required?
|
|
21
23
|
|
|
22
24
|
before_save :hash_password
|
|
23
25
|
|
|
@@ -27,7 +29,7 @@ module Huberry
|
|
|
27
29
|
|
|
28
30
|
module ClassMethods
|
|
29
31
|
def authenticate(login, password)
|
|
30
|
-
user = send("find_by_#{self.login_field}", login)
|
|
32
|
+
user = send("find_by_#{self.authentication_options[:login_field]}", login)
|
|
31
33
|
user && user.authenticated?(password) ? user : false
|
|
32
34
|
end
|
|
33
35
|
|
|
@@ -38,7 +40,7 @@ module Huberry
|
|
|
38
40
|
|
|
39
41
|
module InstanceMethods
|
|
40
42
|
def authenticated?(password)
|
|
41
|
-
send(self.class.hashed_password_field) == self.class.digest(password.to_s + send(self.class.salt_field).to_s)
|
|
43
|
+
send(self.class.authentication_options[:hashed_password_field]) == self.class.digest(password.to_s + send(self.class.authentication_options[:salt_field]).to_s)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
def password_changed?
|
|
@@ -52,9 +54,9 @@ module Huberry
|
|
|
52
54
|
|
|
53
55
|
def reset_password(new_password = nil)
|
|
54
56
|
new_password = generate_salt[0..7] if new_password.blank?
|
|
55
|
-
send("#{self.class.salt_field}=", nil)
|
|
56
|
-
send("#{self.class.password_field}=", new_password)
|
|
57
|
-
send("#{self.class.password_field}_confirmation=", new_password)
|
|
57
|
+
send("#{self.class.authentication_options[:salt_field]}=", nil)
|
|
58
|
+
send("#{self.class.authentication_options[:password_field]}=", new_password)
|
|
59
|
+
send("#{self.class.authentication_options[:password_field]}_confirmation=", new_password)
|
|
58
60
|
@password_changed = true
|
|
59
61
|
end
|
|
60
62
|
|
|
@@ -71,13 +73,13 @@ module Huberry
|
|
|
71
73
|
|
|
72
74
|
def hash_password
|
|
73
75
|
if password_changed? || password_required?
|
|
74
|
-
send("#{self.class.salt_field}=", generate_salt) if send("#{self.class.salt_field}").blank?
|
|
75
|
-
send("#{self.class.hashed_password_field}=", self.class.digest(send(self.class.password_field).to_s + send(self.class.salt_field).to_s))
|
|
76
|
+
send("#{self.class.authentication_options[:salt_field]}=", generate_salt) if send("#{self.class.authentication_options[:salt_field]}").blank?
|
|
77
|
+
send("#{self.class.authentication_options[:hashed_password_field]}=", self.class.digest(send(self.class.authentication_options[:password_field]).to_s + send(self.class.authentication_options[:salt_field]).to_s))
|
|
76
78
|
end
|
|
77
79
|
end
|
|
78
80
|
|
|
79
81
|
def password_required?
|
|
80
|
-
send(self.class.hashed_password_field).blank? || !send(self.class.password_field).blank?
|
|
82
|
+
send(self.class.authentication_options[:hashed_password_field]).blank? || !send(self.class.authentication_options[:password_field]).blank?
|
|
81
83
|
end
|
|
82
84
|
end
|
|
83
85
|
end
|
data/test/_controller_test.rb
CHANGED
|
@@ -31,7 +31,10 @@ class ControllerTest < Test::Unit::TestCase
|
|
|
31
31
|
@request = ActionController::TestRequest.new
|
|
32
32
|
@response = ActionController::TestResponse.new
|
|
33
33
|
|
|
34
|
+
@controller.instance_variable_set('@_request', @request)
|
|
34
35
|
@controller.instance_variable_set('@_session', @request.session)
|
|
36
|
+
|
|
37
|
+
@controller.class.authentication_options.merge!(:session_field => nil, :message => 'Login to continue')
|
|
35
38
|
end
|
|
36
39
|
|
|
37
40
|
def teardown
|
|
@@ -48,13 +51,6 @@ class ControllerTest < Test::Unit::TestCase
|
|
|
48
51
|
assert !@controller.send(:logged_in?)
|
|
49
52
|
end
|
|
50
53
|
|
|
51
|
-
def test_logged_in_yields_block_on_true
|
|
52
|
-
assert_equal nil, @controller.send(:logged_in?) { 'some_value' }
|
|
53
|
-
|
|
54
|
-
@controller.send :login, @user
|
|
55
|
-
assert_equal 'some_value', @controller.send(:logged_in?) { 'some_value' }
|
|
56
|
-
end
|
|
57
|
-
|
|
58
54
|
def test_current_user
|
|
59
55
|
assert_nil @controller.send(:current_user)
|
|
60
56
|
|
|
@@ -68,9 +64,6 @@ class ControllerTest < Test::Unit::TestCase
|
|
|
68
64
|
def test_should_require_login
|
|
69
65
|
get :login_required
|
|
70
66
|
assert_response :redirect
|
|
71
|
-
assert flash.has_key?(:error)
|
|
72
|
-
assert_equal @controller.unauthenticated_message, flash[:error]
|
|
73
|
-
assert_redirected_to '/'
|
|
74
67
|
end
|
|
75
68
|
|
|
76
69
|
def test_should_not_require_login
|
|
@@ -111,4 +104,26 @@ class ControllerTest < Test::Unit::TestCase
|
|
|
111
104
|
assert_equal @controller.session[:return_to], '/login_required'
|
|
112
105
|
end
|
|
113
106
|
|
|
107
|
+
def test_should_set_a_flash_message_when_unauthenticated
|
|
108
|
+
get :login_required
|
|
109
|
+
assert_equal @controller.class.authentication_options[:message], flash[@controller.class.authentication_options[:flash_type]]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_not_should_set_a_flash_message_when_unauthenticated_if_message_is_false
|
|
113
|
+
@controller.class.authentication_options[:message] = false
|
|
114
|
+
get :login_required
|
|
115
|
+
assert_nil flash[@controller.class.authentication_options[:flash_type]]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def test_should_redirect_when_unauthenticated
|
|
119
|
+
get :login_required
|
|
120
|
+
assert_redirected_to @controller.class.authentication_options[:redirect_to]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def test_should_use_custom_session_field
|
|
124
|
+
@controller.class.authentication_options[:session_field] = :test
|
|
125
|
+
@request.session[:test] = @user.id
|
|
126
|
+
assert @controller.send(:logged_in?)
|
|
127
|
+
end
|
|
128
|
+
|
|
114
129
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: shuber-authentication
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sean Huber
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
11
|
|
|
12
|
-
date: 2009-01-
|
|
12
|
+
date: 2009-01-09 00:00:00 -08:00
|
|
13
13
|
default_executable:
|
|
14
14
|
dependencies: []
|
|
15
15
|
|