clearance 1.0.0.rc4 → 1.0.0.rc6
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of clearance might be problematic. Click here for more details.
- checksums.yaml +15 -0
- data/.travis.yml +14 -3
- data/Appraisals +7 -1
- data/Gemfile.lock +33 -26
- data/LICENSE +1 -1
- data/NEWS.md +13 -10
- data/README.md +44 -37
- data/Rakefile +3 -0
- data/app/controllers/clearance/passwords_controller.rb +6 -2
- data/app/views/clearance_mailer/change_password.html.erb +2 -2
- data/app/views/passwords/create.html.erb +3 -1
- data/app/views/passwords/edit.html.erb +15 -13
- data/app/views/passwords/new.html.erb +13 -11
- data/app/views/sessions/_form.html.erb +8 -3
- data/app/views/sessions/new.html.erb +4 -11
- data/app/views/users/_form.html.erb +2 -2
- data/app/views/users/new.html.erb +14 -5
- data/clearance.gemspec +5 -3
- data/config/locales/clearance.en.yml +53 -23
- data/config/routes.rb +3 -3
- data/gemfiles/{3.0.17.gemfile → 3.0.20.gemfile} +1 -1
- data/gemfiles/{3.0.17.gemfile.lock → 3.0.20.gemfile.lock} +62 -57
- data/gemfiles/{3.2.8.gemfile → 3.1.11.gemfile} +1 -1
- data/gemfiles/{3.1.8.gemfile.lock → 3.1.11.gemfile.lock} +70 -65
- data/gemfiles/{3.1.8.gemfile → 3.2.12.gemfile} +1 -1
- data/gemfiles/{3.2.8.gemfile.lock → 3.2.12.gemfile.lock} +74 -68
- data/gemfiles/3.2.13.rc2.gemfile +7 -0
- data/gemfiles/3.2.13.rc2.gemfile.lock +182 -0
- data/lib/clearance.rb +2 -1
- data/lib/clearance/authentication.rb +8 -53
- data/lib/clearance/authorization.rb +62 -0
- data/lib/clearance/back_door.rb +42 -0
- data/lib/clearance/controller.rb +11 -0
- data/lib/clearance/password_strategies/bcrypt.rb +13 -1
- data/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb +1 -0
- data/lib/clearance/password_strategies/blowfish.rb +5 -1
- data/lib/clearance/password_strategies/sha1.rb +5 -1
- data/lib/clearance/testing.rb +1 -1
- data/lib/clearance/testing/app/controllers/application_controller.rb +1 -1
- data/lib/clearance/user.rb +23 -10
- data/lib/clearance/version.rb +1 -1
- data/lib/generators/clearance/install/install_generator.rb +1 -1
- data/lib/generators/clearance/specs/templates/support/integration.rb +2 -0
- data/spec/clearance/back_door_spec.rb +39 -0
- data/spec/controllers/denies_controller_spec.rb +3 -2
- data/spec/controllers/flashes_controller_spec.rb +3 -3
- data/spec/controllers/forgeries_controller_spec.rb +3 -2
- data/spec/controllers/passwords_controller_spec.rb +14 -0
- data/spec/mailers/clearance_mailer_spec.rb +9 -1
- data/spec/models/bcrypt_migration_from_sha1_spec.rb +10 -9
- data/spec/models/bcrypt_spec.rb +21 -7
- data/spec/models/blowfish_spec.rb +1 -6
- data/spec/models/password_strategies_spec.rb +9 -3
- data/spec/models/sha1_spec.rb +1 -6
- data/spec/models/user_spec.rb +19 -9
- data/spec/support/clearance.rb +1 -1
- data/spec/support/fake_model_with_password_strategy.rb +14 -0
- metadata +54 -47
data/lib/clearance/testing.rb
CHANGED
@@ -17,6 +17,6 @@ end
|
|
17
17
|
if defined?(RSpec) && RSpec.respond_to?(:configure)
|
18
18
|
RSpec.configure do |config|
|
19
19
|
config.include Clearance::Testing::Matchers
|
20
|
-
config.include Clearance::Testing::Helpers
|
20
|
+
config.include Clearance::Testing::Helpers, :type => :controller
|
21
21
|
end
|
22
22
|
end
|
data/lib/clearance/user.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'digest/sha1'
|
2
|
+
require 'email_validator'
|
2
3
|
|
3
4
|
module Clearance
|
4
5
|
module User
|
@@ -10,29 +11,41 @@ module Clearance
|
|
10
11
|
|
11
12
|
include Validations
|
12
13
|
include Callbacks
|
13
|
-
include
|
14
|
-
Clearance
|
14
|
+
include(
|
15
|
+
Clearance.configuration.password_strategy ||
|
16
|
+
Clearance::PasswordStrategies::BCrypt
|
17
|
+
)
|
15
18
|
end
|
16
19
|
|
17
20
|
module ClassMethods
|
18
21
|
def authenticate(email, password)
|
19
|
-
if user =
|
22
|
+
if user = find_by_normalized_email(email)
|
20
23
|
if user.authenticated? password
|
21
24
|
return user
|
22
25
|
end
|
23
26
|
end
|
24
27
|
end
|
28
|
+
|
29
|
+
def find_by_normalized_email(email)
|
30
|
+
find_by_email normalize_email(email)
|
31
|
+
end
|
32
|
+
|
33
|
+
def normalize_email(email)
|
34
|
+
email.to_s.downcase.gsub(/\s+/, "")
|
35
|
+
end
|
25
36
|
end
|
26
37
|
|
27
38
|
module Validations
|
28
39
|
extend ActiveSupport::Concern
|
29
40
|
|
30
41
|
included do
|
31
|
-
|
32
|
-
|
33
|
-
|
42
|
+
validates :email,
|
43
|
+
email: true,
|
44
|
+
presence: true,
|
45
|
+
uniqueness: { allow_blank: true },
|
46
|
+
unless: :email_optional?
|
34
47
|
|
35
|
-
|
48
|
+
validates :password, presence: true, unless: :password_optional?
|
36
49
|
end
|
37
50
|
end
|
38
51
|
|
@@ -40,7 +53,7 @@ module Clearance
|
|
40
53
|
extend ActiveSupport::Concern
|
41
54
|
|
42
55
|
included do
|
43
|
-
before_validation :
|
56
|
+
before_validation :normalize_email
|
44
57
|
before_create :generate_remember_token
|
45
58
|
end
|
46
59
|
end
|
@@ -69,8 +82,8 @@ module Clearance
|
|
69
82
|
|
70
83
|
private
|
71
84
|
|
72
|
-
def
|
73
|
-
self.email =
|
85
|
+
def normalize_email
|
86
|
+
self.email = self.class.normalize_email(email)
|
74
87
|
end
|
75
88
|
|
76
89
|
def email_optional?
|
data/lib/clearance/version.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Clearance::BackDoor do
|
4
|
+
it 'signs in as a given user' do
|
5
|
+
user_id = '123'
|
6
|
+
user = stub('user')
|
7
|
+
User.stubs(:find).with(user_id).returns(user)
|
8
|
+
env = env_for_user_id(user_id)
|
9
|
+
back_door = Clearance::BackDoor.new(mock_app)
|
10
|
+
|
11
|
+
result = back_door.call(env)
|
12
|
+
|
13
|
+
env[:clearance].should have_received(:sign_in).with(user)
|
14
|
+
result.should eq mock_app.call(env)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'delegates directly without a user' do
|
18
|
+
env = env_without_user_id
|
19
|
+
back_door = Clearance::BackDoor.new(mock_app)
|
20
|
+
|
21
|
+
result = back_door.call(env)
|
22
|
+
|
23
|
+
env[:clearance].should have_received(:sign_in).never
|
24
|
+
result.should eq mock_app.call(env)
|
25
|
+
end
|
26
|
+
|
27
|
+
def env_without_user_id
|
28
|
+
env_for_user_id('')
|
29
|
+
end
|
30
|
+
|
31
|
+
def env_for_user_id(user_id)
|
32
|
+
clearance = stub('clearance', sign_in: true)
|
33
|
+
Rack::MockRequest.env_for("/?as=#{user_id}").merge(clearance: clearance)
|
34
|
+
end
|
35
|
+
|
36
|
+
def mock_app
|
37
|
+
lambda { |env| [200, {}, ['okay']] }
|
38
|
+
end
|
39
|
+
end
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
class DeniesController < ActionController::Base
|
4
|
-
include Clearance::
|
4
|
+
include Clearance::Controller
|
5
|
+
|
5
6
|
before_filter :authorize, :only => :show
|
6
7
|
|
7
8
|
def new
|
@@ -23,7 +24,7 @@ describe DeniesController do
|
|
23
24
|
before do
|
24
25
|
Rails.application.routes.draw do
|
25
26
|
resource :deny, :only => [:new, :show]
|
26
|
-
|
27
|
+
get '/sign_in' => 'clearance/sessions#new', :as => 'sign_in'
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
class FlashesController < ActionController::Base
|
4
|
-
include Clearance::
|
4
|
+
include Clearance::Controller
|
5
5
|
|
6
6
|
def set_flash
|
7
7
|
flash[:notice] = params[:message]
|
@@ -16,8 +16,8 @@ end
|
|
16
16
|
describe FlashesController do
|
17
17
|
before do
|
18
18
|
Rails.application.routes.draw do
|
19
|
-
|
20
|
-
|
19
|
+
get '/set_flash' => 'flashes#set_flash'
|
20
|
+
get '/view_flash' => 'flashes#view_flash'
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
class ForgeriesController < ActionController::Base
|
4
|
-
include Clearance::
|
4
|
+
include Clearance::Controller
|
5
|
+
|
5
6
|
protect_from_forgery
|
6
7
|
before_filter :authorize
|
7
8
|
|
@@ -18,7 +19,7 @@ describe ForgeriesController do
|
|
18
19
|
before do
|
19
20
|
Rails.application.routes.draw do
|
20
21
|
resources :forgeries
|
21
|
-
|
22
|
+
get '/sign_in' => 'clearance/sessions#new', :as => 'sign_in'
|
22
23
|
end
|
23
24
|
|
24
25
|
@user = create(:user)
|
@@ -35,6 +35,20 @@ describe Clearance::PasswordsController do
|
|
35
35
|
it { should respond_with(:success) }
|
36
36
|
end
|
37
37
|
|
38
|
+
describe 'with correct email address capitalized differently' do
|
39
|
+
before do
|
40
|
+
ActionMailer::Base.deliveries.clear
|
41
|
+
post :create, :password => { :email => @user.email.upcase }
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should generate a token for the change your password email' do
|
45
|
+
@user.reload.confirmation_token.should_not be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it { should have_sent_email.with_subject(/change your password/i) }
|
49
|
+
it { should respond_with(:success) }
|
50
|
+
end
|
51
|
+
|
38
52
|
describe 'with incorrect email address' do
|
39
53
|
before do
|
40
54
|
email = 'user1@example.com'
|
@@ -21,7 +21,15 @@ describe ClearanceMailer do
|
|
21
21
|
@email.body.to_s.should =~ regexp
|
22
22
|
end
|
23
23
|
|
24
|
-
it '
|
24
|
+
it 'sets its subject' do
|
25
25
|
@email.subject.should =~ /Change your password/
|
26
26
|
end
|
27
|
+
|
28
|
+
it 'contains opening text in the body' do
|
29
|
+
@email.body.should =~ /a link to change your password/
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'contains closing text in the body' do
|
33
|
+
@email.body.should =~ /Your password has not been changed/
|
34
|
+
end
|
27
35
|
end
|
@@ -2,12 +2,9 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Clearance::PasswordStrategies::BCryptMigrationFromSHA1 do
|
4
4
|
subject do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
attr_accessor :salt
|
9
|
-
include Clearance::PasswordStrategies::BCryptMigrationFromSHA1
|
10
|
-
end.new
|
5
|
+
fake_model_with_password_strategy(
|
6
|
+
Clearance::PasswordStrategies::BCryptMigrationFromSHA1
|
7
|
+
)
|
11
8
|
end
|
12
9
|
|
13
10
|
describe '#password=' do
|
@@ -27,13 +24,12 @@ describe Clearance::PasswordStrategies::BCryptMigrationFromSHA1 do
|
|
27
24
|
end
|
28
25
|
|
29
26
|
it 'encrypts with BCrypt' do
|
30
|
-
BCrypt::Password.should have_received(:create).with(password)
|
27
|
+
BCrypt::Password.should have_received(:create).with(password, anything)
|
31
28
|
end
|
32
29
|
|
33
30
|
it 'sets the pasword on the subject' do
|
34
31
|
subject.password.should be_present
|
35
32
|
end
|
36
|
-
|
37
33
|
end
|
38
34
|
|
39
35
|
describe '#authenticated?' do
|
@@ -45,6 +41,7 @@ describe Clearance::PasswordStrategies::BCryptMigrationFromSHA1 do
|
|
45
41
|
before do
|
46
42
|
subject.salt = salt
|
47
43
|
subject.encrypted_password = sha1_hash
|
44
|
+
subject.stubs :save => true
|
48
45
|
end
|
49
46
|
|
50
47
|
it 'is authenticated' do
|
@@ -61,6 +58,11 @@ describe Clearance::PasswordStrategies::BCryptMigrationFromSHA1 do
|
|
61
58
|
subject.authenticated? 'bad' + password
|
62
59
|
}.should_not raise_error(BCrypt::Errors::InvalidHash)
|
63
60
|
end
|
61
|
+
|
62
|
+
it 'saves the subject to database' do
|
63
|
+
subject.authenticated? password
|
64
|
+
subject.should have_received(:save)
|
65
|
+
end
|
64
66
|
end
|
65
67
|
|
66
68
|
context 'with a BCrypt-encrypted password' do
|
@@ -80,5 +82,4 @@ describe Clearance::PasswordStrategies::BCryptMigrationFromSHA1 do
|
|
80
82
|
end
|
81
83
|
end
|
82
84
|
end
|
83
|
-
|
84
85
|
end
|
data/spec/models/bcrypt_spec.rb
CHANGED
@@ -2,10 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Clearance::PasswordStrategies::BCrypt do
|
4
4
|
subject do
|
5
|
-
|
6
|
-
attr_accessor :encrypted_password
|
7
|
-
include Clearance::PasswordStrategies::BCrypt
|
8
|
-
end.new
|
5
|
+
fake_model_with_password_strategy(Clearance::PasswordStrategies::BCrypt)
|
9
6
|
end
|
10
7
|
|
11
8
|
describe '#password=' do
|
@@ -14,15 +11,32 @@ describe Clearance::PasswordStrategies::BCrypt do
|
|
14
11
|
|
15
12
|
before do
|
16
13
|
BCrypt::Password.stubs :create => encrypted_password
|
17
|
-
subject.password = password
|
18
14
|
end
|
19
15
|
|
20
16
|
it 'encrypts the password into encrypted_password' do
|
17
|
+
subject.password = password
|
18
|
+
|
21
19
|
subject.encrypted_password.should == encrypted_password
|
22
20
|
end
|
23
21
|
|
24
|
-
it 'encrypts with BCrypt' do
|
25
|
-
|
22
|
+
it 'encrypts with BCrypt using default cost in non test environments' do
|
23
|
+
Rails.stubs :env => ActiveSupport::StringInquirer.new("production")
|
24
|
+
|
25
|
+
subject.password = password
|
26
|
+
|
27
|
+
BCrypt::Password.should have_received(:create).with(
|
28
|
+
password,
|
29
|
+
:cost => ::BCrypt::Engine::DEFAULT_COST
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'encrypts with BCrypt using minimum cost in test environment' do
|
34
|
+
subject.password = password
|
35
|
+
|
36
|
+
BCrypt::Password.should have_received(:create).with(
|
37
|
+
password,
|
38
|
+
:cost => ::BCrypt::Engine::MIN_COST
|
39
|
+
)
|
26
40
|
end
|
27
41
|
end
|
28
42
|
|
@@ -2,12 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Clearance::PasswordStrategies::Blowfish do
|
4
4
|
subject do
|
5
|
-
|
6
|
-
attr_accessor :salt, :encrypted_password
|
7
|
-
include Clearance::PasswordStrategies::Blowfish
|
8
|
-
|
9
|
-
def generate_random_code; 'code'; end
|
10
|
-
end.new
|
5
|
+
fake_model_with_password_strategy(Clearance::PasswordStrategies::Blowfish)
|
11
6
|
end
|
12
7
|
|
13
8
|
describe '#password=' do
|
@@ -2,10 +2,16 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Clearance::User do
|
4
4
|
subject do
|
5
|
+
class UniquenessValidator < ActiveModel::Validator
|
6
|
+
def validate(record)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
5
10
|
Class.new do
|
6
|
-
|
7
|
-
|
8
|
-
|
11
|
+
include ActiveModel::Validations
|
12
|
+
|
13
|
+
validates_with UniquenessValidator
|
14
|
+
|
9
15
|
def self.before_validation(*args); end
|
10
16
|
def self.before_create(*args); end
|
11
17
|
|
data/spec/models/sha1_spec.rb
CHANGED
@@ -2,12 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Clearance::PasswordStrategies::SHA1 do
|
4
4
|
subject do
|
5
|
-
|
6
|
-
attr_accessor :salt, :encrypted_password
|
7
|
-
include Clearance::PasswordStrategies::SHA1
|
8
|
-
|
9
|
-
def generate_random_code; "code"; end
|
10
|
-
end.new
|
5
|
+
fake_model_with_password_strategy(Clearance::PasswordStrategies::SHA1)
|
11
6
|
end
|
12
7
|
|
13
8
|
describe '#password=' do
|
data/spec/models/user_spec.rb
CHANGED
@@ -15,8 +15,8 @@ describe User do
|
|
15
15
|
it { should_not allow_value('foo').for(:email) }
|
16
16
|
it { should_not allow_value('example.com').for(:email) }
|
17
17
|
|
18
|
-
it 'stores email in down case' do
|
19
|
-
user = create(:user, :email => '
|
18
|
+
it 'stores email in down case and removes whitespace' do
|
19
|
+
user = create(:user, :email => 'Jo hn.Do e @exa mp le.c om')
|
20
20
|
user.email.should == 'john.doe@example.com'
|
21
21
|
end
|
22
22
|
end
|
@@ -33,27 +33,28 @@ describe User do
|
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'is authenticated with correct email and password' do
|
36
|
-
|
37
|
-
should be
|
36
|
+
User.authenticate(@user.email, @password).should eq(@user)
|
38
37
|
@user.should be_authenticated(@password)
|
39
38
|
end
|
40
39
|
|
41
40
|
it 'is authenticated with correct uppercased email and correct password' do
|
42
|
-
|
43
|
-
should be
|
41
|
+
User.authenticate(@user.email.upcase, @password).should eq(@user)
|
44
42
|
@user.should be_authenticated(@password)
|
45
43
|
end
|
46
44
|
|
47
45
|
it 'is authenticated with incorrect credentials' do
|
48
|
-
|
49
|
-
should_not be
|
46
|
+
User.authenticate(@user.email, 'bad_password').should be_nil
|
50
47
|
@user.should_not be_authenticated('bad password')
|
51
48
|
end
|
49
|
+
|
50
|
+
it 'is retrieved via a case-insensitive search' do
|
51
|
+
User.find_by_normalized_email(@user.email.upcase).should eq(@user)
|
52
|
+
end
|
52
53
|
end
|
53
54
|
|
54
55
|
describe 'when resetting authentication with reset_remember_token!' do
|
55
56
|
before do
|
56
|
-
@user
|
57
|
+
@user = create(:user)
|
57
58
|
@user.remember_token = 'old-token'
|
58
59
|
@user.reset_remember_token!
|
59
60
|
end
|
@@ -139,6 +140,7 @@ describe User do
|
|
139
140
|
describe 'a user with an optional email' do
|
140
141
|
before do
|
141
142
|
@user = User.new
|
143
|
+
|
142
144
|
class << @user
|
143
145
|
def email_optional?
|
144
146
|
true
|
@@ -175,6 +177,14 @@ describe User do
|
|
175
177
|
end
|
176
178
|
end
|
177
179
|
|
180
|
+
describe 'email address normalization' do
|
181
|
+
let(:email) { 'Jo hn.Do e @exa mp le.c om' }
|
182
|
+
|
183
|
+
it 'downcases the address and strips spaces' do
|
184
|
+
User.normalize_email(email).should eq 'john.doe@example.com'
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
178
188
|
describe 'the password setter on a User' do
|
179
189
|
let(:password) { 'a-password' }
|
180
190
|
before { subject.send(:password=, password) }
|