josevalim-auth_helpers 0.1.2 → 0.2.0
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 +2 -7
- data/README +54 -72
- data/lib/auth_helpers.rb +8 -20
- data/lib/auth_helpers/model/associatable.rb +5 -0
- data/lib/auth_helpers/model/confirmable.rb +32 -42
- data/lib/auth_helpers/model/recoverable.rb +27 -38
- data/lib/auth_helpers/model/updatable.rb +39 -0
- data/lib/auth_helpers/notifier.rb +20 -19
- data/lib/auth_helpers/spec/confirmable.rb +24 -30
- data/lib/auth_helpers/spec/notifier.rb +10 -11
- data/lib/auth_helpers/spec/recoverable.rb +25 -32
- data/lib/auth_helpers/spec/updatable.rb +37 -0
- data/views/auth_helpers/notifier/create_confirmation.erb +1 -0
- data/views/auth_helpers/notifier/resend_confirmation.erb +1 -0
- data/views/auth_helpers/notifier/reset_password.erb +1 -1
- data/views/auth_helpers/notifier/update_confirmation.erb +1 -0
- metadata +10 -16
- data/lib/auth_helpers/migration.rb +0 -56
- data/lib/auth_helpers/model/authenticable.rb +0 -105
- data/lib/auth_helpers/model/rememberable.rb +0 -71
- data/lib/auth_helpers/model/validatable.rb +0 -43
- data/lib/auth_helpers/spec/associatable.rb +0 -34
- data/lib/auth_helpers/spec/authenticable.rb +0 -87
- data/lib/auth_helpers/spec/rememberable.rb +0 -83
- data/lib/auth_helpers/spec/validatable.rb +0 -29
- data/views/auth_helpers/notifier/confirmation_code.erb +0 -1
- data/views/auth_helpers/notifier/email_changed.erb +0 -1
- data/views/auth_helpers/notifier/new_account.erb +0 -1
data/CHANGELOG
CHANGED
@@ -1,17 +1,12 @@
|
|
1
|
-
# Version 0.
|
1
|
+
# Version 0.2
|
2
2
|
|
3
3
|
* First version with the following modules:
|
4
4
|
|
5
5
|
AuthHelpers::Model::Associatable
|
6
|
-
AuthHelpers::Model::Authenticable
|
7
6
|
AuthHelpers::Model::Confirmable
|
8
7
|
AuthHelpers::Model::Recoverable
|
9
|
-
AuthHelpers::Model::
|
10
|
-
AuthHelpers::Model::Validatable
|
8
|
+
AuthHelpers::Model::Updatable
|
11
9
|
|
12
10
|
Plus:
|
13
11
|
|
14
12
|
AuthHelpers::Notifier
|
15
|
-
AuthHelpers::Migration
|
16
|
-
|
17
|
-
The first to deal with Notifications and the second with migrations.
|
data/README
CHANGED
@@ -9,20 +9,9 @@ You can also read this README in pretty html at the GitHub project Wiki page:
|
|
9
9
|
Description
|
10
10
|
-----------
|
11
11
|
|
12
|
-
AuthHelpers is a collection of modules to
|
13
|
-
|
14
|
-
|
15
|
-
Why? Authentication is something that you need to do right since the beginning,
|
16
|
-
otherwise it will haunt you until the end of the project.
|
17
|
-
|
18
|
-
Gladly, Rails community has two awesome gems for this: Clearance and AuthLogic.
|
19
|
-
While the first gives you a simple but full, ready-to-go engine, the second
|
20
|
-
is a complex, fully featured authencation library.
|
21
|
-
|
22
|
-
While working in different projects, requisites change and sometimes you need
|
23
|
-
something that works between something simple and something fully featured. This
|
24
|
-
is the scope of AuthHelpers: you have modules and you include them where you
|
25
|
-
want.
|
12
|
+
AuthHelpers is a collection of modules to improve your Authlogic models. It
|
13
|
+
mainly exposes some convenience methods to help on confirmation and passwords
|
14
|
+
management, it also deals automatically with associations and notifications.
|
26
15
|
|
27
16
|
Installation
|
28
17
|
------------
|
@@ -41,14 +30,15 @@ Modules
|
|
41
30
|
-------
|
42
31
|
|
43
32
|
class Account < ActiveRecord::Base
|
44
|
-
|
33
|
+
acts_as_authentic do |a|
|
34
|
+
a.validations_scope = :accountable_type
|
35
|
+
a.require_password_confirmation = false
|
36
|
+
end
|
45
37
|
|
46
38
|
include AuthHelpers::Model::Associatable
|
47
|
-
include AuthHelpers::Model::Authenticable
|
48
39
|
include AuthHelpers::Model::Confirmable
|
49
40
|
include AuthHelpers::Model::Recoverable
|
50
|
-
include AuthHelpers::Model::
|
51
|
-
include AuthHelpers::Model::Validatable
|
41
|
+
include AuthHelpers::Model::Updatable
|
52
42
|
end
|
53
43
|
|
54
44
|
== Associatable
|
@@ -58,53 +48,45 @@ in the table. This module exists because, whenever it's possible, I follow the
|
|
58
48
|
pattern of having an account model and then all accountable objects uses this
|
59
49
|
model (it autodetects polymorphic associations too).
|
60
50
|
|
61
|
-
== Authenticable
|
62
|
-
|
63
|
-
It adds password, salt and encrypt behavior. Adds find_and_authenticate class
|
64
|
-
method and authenticate?(password) as instance method. It requires a constant
|
65
|
-
named SALT set in your model.
|
66
|
-
|
67
51
|
== Confirmable
|
68
52
|
|
69
|
-
Adds the
|
70
|
-
creation, and adds find_and_confirm,
|
71
|
-
methods
|
53
|
+
Adds the confirmation handling. It sends an e-mail to the user on account
|
54
|
+
creation, and adds find_and_confirm, find_and_resend_confirmation_instructions
|
55
|
+
as class methods plus confirmed? and confirm! as instance methods.
|
72
56
|
|
73
|
-
When used with
|
74
|
-
whenever the user changes his e-mail address.
|
57
|
+
When used with Updatable, also sends an e-mail the user changes his e-mail address.
|
75
58
|
|
76
59
|
== Recoverable
|
77
60
|
|
78
|
-
Adds the
|
79
|
-
and find_and_reset_password class methods.
|
80
|
-
|
81
|
-
== Rememberable
|
61
|
+
Adds the reset password code handling. Adds find_and_resend_confirmation_instructions
|
62
|
+
and find_and_reset_password class methods plus reset_password instance method.
|
82
63
|
|
83
|
-
|
84
|
-
only returns a record if the token hasn't expired yet), remember_me! and forget_me!
|
85
|
-
methods.
|
64
|
+
== Updatable
|
86
65
|
|
87
|
-
|
88
|
-
|
66
|
+
It adds email and password confirmations and hack into update_attributes to
|
67
|
+
ensure the another confirmation instruction is sent when the user changes the
|
68
|
+
e-mail.
|
89
69
|
|
90
|
-
|
70
|
+
Authlogic already supports password confirmation, but it's coupled with the
|
71
|
+
validates_length_of :password_confirmation, which is unecessary. So in order
|
72
|
+
to use this module, is advisable to disable require_password_confirmation from
|
73
|
+
Authlogic.
|
91
74
|
|
92
|
-
|
93
|
-
|
75
|
+
acts_as_authentic do |a|
|
76
|
+
a.require_password_confirmation = false
|
77
|
+
end
|
94
78
|
|
95
79
|
Specs
|
96
80
|
-----
|
97
81
|
|
98
|
-
All
|
82
|
+
All modules come with specs, that's why the library does not have tests per se.
|
99
83
|
So if you want to test the Account model declared above, just do:
|
100
84
|
|
101
85
|
describe Account do
|
102
86
|
include AuthHelpers::Spec::Associatable
|
103
|
-
include AuthHelpers::Spec::Authenticable
|
104
87
|
include AuthHelpers::Spec::Confirmable
|
105
88
|
include AuthHelpers::Spec::Recoverable
|
106
|
-
include AuthHelpers::Spec::
|
107
|
-
include AuthHelpers::Spec::Validatable
|
89
|
+
include AuthHelpers::Spec::Updatable
|
108
90
|
|
109
91
|
before(:each) do
|
110
92
|
@valid_attributes = {
|
@@ -120,28 +102,8 @@ So if you want to test the Account model declared above, just do:
|
|
120
102
|
end
|
121
103
|
end
|
122
104
|
|
123
|
-
The only
|
124
|
-
variable set with a hash of valid attributes.
|
125
|
-
those tests.
|
126
|
-
|
127
|
-
Migrations
|
128
|
-
----------
|
129
|
-
|
130
|
-
While AuthHelpers gives you the flexibity to choose which model you want to add
|
131
|
-
your validations, it takes from you the freedom to choose what are the column
|
132
|
-
names. However it makes easier to create your migrations. This is a migration up
|
133
|
-
example for the Account model above:
|
134
|
-
|
135
|
-
create_table :accounts do |t|
|
136
|
-
t.references :accountable, :polymorphic => true
|
137
|
-
t.extend AuthHelpers::Migration
|
138
|
-
|
139
|
-
t.authenticable
|
140
|
-
t.confirmable
|
141
|
-
t.recoverable
|
142
|
-
t.rememberable
|
143
|
-
t.timestamps
|
144
|
-
end
|
105
|
+
The only requirment you have for the tests is to have a @valid_attributes
|
106
|
+
instance variable set with a hash of valid attributes.
|
145
107
|
|
146
108
|
Notifications
|
147
109
|
-------------
|
@@ -155,12 +117,32 @@ want to prettify your notification views, so you just need to do:
|
|
155
117
|
Then make a copy of the plugin views folder to your app/views and start to work
|
156
118
|
on them.
|
157
119
|
|
158
|
-
|
159
|
-
|
120
|
+
Notification has some basic specs which fail if you don't include the perishable
|
121
|
+
token in your emails. Just put a file in spec/models/auth_helpers/notifier.rb
|
122
|
+
with the following contents:
|
123
|
+
|
124
|
+
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
|
125
|
+
|
126
|
+
module AuthHelpers
|
127
|
+
describe Notifier do
|
128
|
+
include AuthHelpers::Spec::Notifier
|
129
|
+
end end
|
130
|
+
|
131
|
+
I18n
|
132
|
+
----
|
133
|
+
|
134
|
+
Those helpers rely on I18n. Assuming the account model below, you need to
|
135
|
+
configure the following messages:
|
160
136
|
|
161
|
-
|
162
|
-
|
163
|
-
|
137
|
+
activerecord:
|
138
|
+
errors:
|
139
|
+
models:
|
140
|
+
account:
|
141
|
+
already_confirmed: was already confirmed
|
142
|
+
not_found: could not be found. Are you sure you gave the right e-mail address?
|
143
|
+
perishable_token:
|
144
|
+
invalid_confirmation: is invalid. Are you sure you copied the right link from your e-mail?
|
145
|
+
invalid_reset_password: is invalid. Are you sure you copied the right link from your e-mail?
|
164
146
|
|
165
147
|
Example app
|
166
148
|
-----------
|
data/lib/auth_helpers.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module AuthHelpers
|
2
|
-
# Helper that find or initialize an object by attribute only if the given value
|
3
|
-
# If it's blank, create a new object using :new.
|
2
|
+
# Helper that find or initialize an object by attribute only if the given value
|
3
|
+
# is not blank. If it's blank, create a new object using :new.
|
4
4
|
#
|
5
5
|
def self.find_or_initialize_by_unless_blank(klass, attr, value)
|
6
6
|
if value.blank?
|
@@ -10,25 +10,13 @@ module AuthHelpers
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
-
#
|
14
|
-
# the database if the code already exists.
|
13
|
+
# Creates a new record, assigning the perishable token and an error message.
|
15
14
|
#
|
16
|
-
def self.
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
value
|
15
|
+
def self.new_with_perishable_token_error(klass, message=:invalid, options={})
|
16
|
+
record = klass.new(options)
|
17
|
+
record.perishable_token = options[:perishable_token]
|
18
|
+
record.errors.add(:perishable_token, message, :default => :invalid)
|
19
|
+
record
|
22
20
|
end
|
23
21
|
|
24
|
-
# Create a random string with the given length using letters and numbers.
|
25
|
-
#
|
26
|
-
def self.random_string(length)
|
27
|
-
chars = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
|
28
|
-
|
29
|
-
newpass = ''
|
30
|
-
1.upto(length) { |i| newpass << chars.rand }
|
31
|
-
|
32
|
-
return newpass
|
33
|
-
end
|
34
22
|
end
|
@@ -11,6 +11,11 @@ module AuthHelpers
|
|
11
11
|
# Finally, if the *_id in the table has also *_type. It considers a polymorphic
|
12
12
|
# association.
|
13
13
|
#
|
14
|
+
# Whenever using this method with polymorphic association, don't forget to
|
15
|
+
# set the validation scope in AuthLogic.
|
16
|
+
#
|
17
|
+
# a.validations_scope = :accountable_type
|
18
|
+
#
|
14
19
|
module Associatable
|
15
20
|
def self.included(base)
|
16
21
|
column = base.columns.detect{|c| c.name =~ /_id$/ }
|
@@ -8,76 +8,66 @@ module AuthHelpers
|
|
8
8
|
module Confirmable
|
9
9
|
def self.included(base)
|
10
10
|
base.extend ClassMethods
|
11
|
-
base.send :
|
12
|
-
base.send :after_create, :send_new_account_notification
|
11
|
+
base.send :after_create, :send_confirmation_instructions
|
13
12
|
end
|
14
13
|
|
15
|
-
# Returns true if is not a new record and
|
14
|
+
# Returns true if is not a new record and confirmed_at is not blank.
|
16
15
|
#
|
17
16
|
def confirmed?
|
18
|
-
!self.new_record?
|
17
|
+
!(self.new_record? || self.confirmed_at.nil?)
|
19
18
|
end
|
20
19
|
|
21
|
-
# Confirms
|
22
|
-
# confirmed_at field.
|
20
|
+
# Confirms the record by setting the confirmed at.
|
23
21
|
#
|
24
22
|
def confirm!
|
25
|
-
|
26
|
-
self.confirmed_at = Time.now.utc
|
27
|
-
return self.save(false)
|
23
|
+
update_attribute(:confirmed_at, Time.now.utc)
|
28
24
|
end
|
29
25
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
# Send a notification to new account. Used as filter.
|
42
|
-
#
|
43
|
-
def send_new_account_notification
|
44
|
-
AuthHelpers::Notifier.deliver_new_account(self)
|
45
|
-
end
|
26
|
+
# Send confirmation isntructions in different scenarios. It resets the
|
27
|
+
# perishable token, confirmed_at date and set the confirmation_sent_at
|
28
|
+
# datetime.
|
29
|
+
#
|
30
|
+
def send_confirmation_instructions(on=:create)
|
31
|
+
self.reset_perishable_token
|
32
|
+
self.confirmed_at = nil
|
33
|
+
self.confirmation_sent_at = Time.now.utc
|
34
|
+
self.save(false)
|
35
|
+
AuthHelpers::Notifier.send(:"deliver_#{on}_confirmation", self)
|
36
|
+
end
|
46
37
|
|
47
38
|
module ClassMethods
|
48
39
|
|
49
|
-
# Receives
|
50
|
-
# If
|
40
|
+
# Receives the perishable token and try to find a record to confirm the
|
41
|
+
# account. If it can't find the record, returns a new record with an
|
42
|
+
# error set on the perishable token.
|
51
43
|
#
|
52
|
-
def find_and_confirm(
|
53
|
-
confirmable =
|
54
|
-
|
55
|
-
if confirmable.new_record?
|
56
|
-
confirmable.errors.add :confirmation_code, :invalid
|
57
|
-
else
|
44
|
+
def find_and_confirm(options={})
|
45
|
+
if confirmable = self.find_using_perishable_token(options[:perishable_token])
|
58
46
|
confirmable.confirm!
|
47
|
+
confirmable
|
48
|
+
else
|
49
|
+
AuthHelpers.new_with_perishable_token_error(self, :invalid_confirmation, options)
|
59
50
|
end
|
60
|
-
|
61
|
-
return confirmable
|
62
51
|
end
|
63
52
|
|
64
|
-
# Receives a hash with email and tries to find
|
65
|
-
# If the
|
66
|
-
#
|
53
|
+
# Receives a hash with email and tries to find a record to resend the
|
54
|
+
# confirmation instructions. If the record can't be found or it's already
|
55
|
+
# confirmed, set the appropriate error messages and return the object.
|
67
56
|
#
|
68
|
-
def
|
57
|
+
def find_and_resend_confirmation_instructions(options = {})
|
69
58
|
confirmable = AuthHelpers.find_or_initialize_by_unless_blank(self, :email, options[:email])
|
70
59
|
|
71
60
|
if confirmable.new_record?
|
72
|
-
confirmable.errors.add
|
61
|
+
confirmable.errors.add(:email, :not_found)
|
73
62
|
elsif confirmable.confirmed?
|
74
|
-
confirmable.errors.add
|
63
|
+
confirmable.errors.add(:email, :already_confirmed)
|
75
64
|
else
|
76
|
-
|
65
|
+
confirmable.send_confirmation_instructions(:resend)
|
77
66
|
end
|
78
67
|
|
79
68
|
return confirmable
|
80
69
|
end
|
70
|
+
|
81
71
|
end
|
82
72
|
|
83
73
|
end
|
@@ -3,72 +3,61 @@ require File.join(File.dirname(__FILE__), '..', 'notifier')
|
|
3
3
|
module AuthHelpers
|
4
4
|
module Model
|
5
5
|
|
6
|
-
# Adds a module that deals with forgot your password.
|
6
|
+
# Adds a module that deals with forgot your password. It overwrites the
|
7
|
+
# reset password method from authlogic for one that accepts a password.
|
7
8
|
#
|
8
9
|
module Recoverable
|
9
10
|
def self.included(base)
|
10
|
-
base.send(:attr_accessible, :reset_password_code)
|
11
11
|
base.extend ClassMethods
|
12
12
|
end
|
13
13
|
|
14
|
+
def reset_password(new_password, new_password_confirmation)
|
15
|
+
self.password = new_password || ""
|
16
|
+
self.password_confirmation = new_password_confirmation || "" if self.respond_to?(:password_confirmation)
|
17
|
+
end
|
18
|
+
|
14
19
|
# Reset the password with the new_password is equals its confirmation and
|
15
20
|
# set reset password code to nil.
|
16
21
|
#
|
17
22
|
def reset_password!(new_password, new_password_confirmation)
|
18
|
-
|
19
|
-
self.
|
20
|
-
|
21
|
-
if self.valid?
|
22
|
-
self.reset_password_code = nil
|
23
|
-
return self.save
|
24
|
-
end
|
25
|
-
|
26
|
-
false
|
27
|
-
end
|
28
|
-
|
29
|
-
# Set a reset password code in the database and send it through e-mail
|
30
|
-
#
|
31
|
-
def send_reset_password_code
|
32
|
-
new_code = AuthHelpers.generate_unique_string_for(self.class, :reset_password_code, 40)
|
33
|
-
self.update_attribute(:reset_password_code, new_code)
|
34
|
-
|
35
|
-
AuthHelpers::Notifier.deliver_reset_password(self)
|
36
|
-
return true
|
23
|
+
reset_password(new_password, new_password_confirmation)
|
24
|
+
self.save
|
37
25
|
end
|
38
26
|
|
39
27
|
module ClassMethods
|
40
28
|
|
41
|
-
# Receives a hash with
|
42
|
-
#
|
43
|
-
#
|
29
|
+
# Receives a hash with email and tries to find a record to resend reset
|
30
|
+
# password instructions. If the record can't be found, it sets the
|
31
|
+
# appropriate error messages and return the object.
|
44
32
|
#
|
45
|
-
def
|
46
|
-
recoverable = AuthHelpers.find_or_initialize_by_unless_blank(self, :
|
33
|
+
def find_and_send_reset_password_instructions(options={})
|
34
|
+
recoverable = AuthHelpers.find_or_initialize_by_unless_blank(self, :email, options[:email])
|
47
35
|
|
48
36
|
if recoverable.new_record?
|
49
|
-
recoverable.errors.add :
|
37
|
+
recoverable.errors.add(:email, :not_found, options)
|
50
38
|
else
|
51
|
-
recoverable.
|
39
|
+
recoverable.reset_perishable_token!
|
40
|
+
AuthHelpers::Notifier.deliver_reset_password(recoverable)
|
52
41
|
end
|
53
42
|
|
54
43
|
return recoverable
|
55
44
|
end
|
56
45
|
|
57
|
-
# Receives a hash with
|
58
|
-
# If the
|
46
|
+
# Receives a hash with perishable_token, password and password confirmation.
|
47
|
+
# If the password cannot be reset (confirmation fails, for example), it
|
48
|
+
# returns an object with errors.
|
59
49
|
#
|
60
|
-
def
|
61
|
-
recoverable =
|
62
|
-
|
63
|
-
|
64
|
-
recoverable.errors.add :email, :not_found, options
|
50
|
+
def find_and_reset_password(options={})
|
51
|
+
if recoverable = self.find_using_perishable_token(options[:perishable_token])
|
52
|
+
recoverable.reset_password!(options[:password], options[:password_confirmation])
|
53
|
+
recoverable
|
65
54
|
else
|
66
|
-
|
55
|
+
AuthHelpers.new_with_perishable_token_error(self, :invalid_reset_password, options)
|
67
56
|
end
|
68
|
-
|
69
|
-
return recoverable
|
70
57
|
end
|
58
|
+
|
71
59
|
end
|
60
|
+
|
72
61
|
end
|
73
62
|
end
|
74
63
|
end
|