clearance 1.7.0 → 1.8.0
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 +4 -4
- data/Gemfile +1 -3
- data/Gemfile.lock +4 -28
- data/NEWS.md +12 -0
- data/README.md +8 -1
- data/Rakefile +11 -8
- data/app/controllers/clearance/passwords_controller.rb +1 -0
- data/app/controllers/clearance/sessions_controller.rb +14 -2
- data/app/controllers/clearance/users_controller.rb +15 -4
- data/bin/appraisal +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/clearance.gemspec +1 -1
- data/gemfiles/rails3.2.gemfile +1 -3
- data/gemfiles/rails4.0.gemfile +1 -3
- data/gemfiles/rails4.1.gemfile +1 -3
- data/gemfiles/rails4.2.gemfile +1 -3
- data/lib/clearance/configuration.rb +2 -0
- data/lib/clearance/session.rb +12 -6
- data/lib/clearance/version.rb +1 -1
- data/spec/acceptance/clearance_installation_spec.rb +75 -0
- data/spec/{support/app_templates → app_templates}/app/controllers/application_controller.rb +0 -0
- data/spec/{support/app_templates → app_templates}/app/models/user.rb +0 -0
- data/spec/{support/app_templates → app_templates}/config/routes.rb +0 -0
- data/spec/app_templates/testapp/Gemfile +7 -0
- data/spec/app_templates/testapp/app/controllers/home_controller.rb +5 -0
- data/spec/app_templates/testapp/config/initializers/action_mailer.rb +3 -0
- data/spec/app_templates/testapp/config/routes.rb +3 -0
- data/spec/clearance/session_spec.rb +13 -0
- data/spec/controllers/passwords_controller_spec.rb +100 -131
- data/spec/controllers/sessions_controller_spec.rb +66 -52
- data/spec/controllers/users_controller_spec.rb +47 -60
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/models/user_with_optional_password.rb +7 -0
- data/spec/dummy/application.rb +2 -0
- data/spec/factories.rb +4 -0
- data/spec/{models → password_strategies}/bcrypt_migration_from_sha1_spec.rb +1 -0
- data/spec/password_strategies/bcrypt_spec.rb +81 -0
- data/spec/password_strategies/blowfish_spec.rb +55 -0
- data/spec/password_strategies/password_strategies_spec.rb +28 -0
- data/spec/password_strategies/sha1_spec.rb +53 -0
- data/spec/support/clearance.rb +0 -16
- data/spec/support/fake_model_with_password_strategy.rb +0 -4
- data/spec/support/fake_model_without_password_strategy.rb +19 -0
- data/spec/support/generator_spec_helpers.rb +1 -1
- data/spec/support/request_with_remember_token.rb +1 -1
- data/spec/user_spec.rb +186 -0
- metadata +23 -67
- data/cucumber.yml +0 -1
- data/features/integration_with_rspec.feature +0 -23
- data/features/integration_with_test_unit.feature +0 -16
- data/features/step_definitions/configuration_steps.rb +0 -153
- data/features/step_definitions/gem_file_steps.rb +0 -15
- data/features/support/aruba.rb +0 -3
- data/features/support/env.rb +0 -27
- data/spec/models/bcrypt_spec.rb +0 -66
- data/spec/models/blowfish_spec.rb +0 -42
- data/spec/models/password_strategies_spec.rb +0 -41
- data/spec/models/sha1_spec.rb +0 -43
- data/spec/models/user_spec.rb +0 -196
@@ -1,15 +0,0 @@
|
|
1
|
-
When /^I add the "([^"]*)" gem$/ do |gem_name|
|
2
|
-
append_to_file('Gemfile', %{\ngem "#{gem_name}"\n})
|
3
|
-
end
|
4
|
-
|
5
|
-
When /^I add the "([^"]*)" gem from this project$/ do |gem_name|
|
6
|
-
append_to_file('Gemfile', %{\ngem "#{gem_name}", path: "../../.."\n})
|
7
|
-
end
|
8
|
-
|
9
|
-
When /^I remove the "([^"]*)" gem from this project$/ do |gem_name|
|
10
|
-
in_current_dir do
|
11
|
-
content = File.read('Gemfile')
|
12
|
-
content.sub!(/^.*gem '#{gem_name}'.*\n/, "")
|
13
|
-
File.open('Gemfile', 'w') { |file| file.write(content) }
|
14
|
-
end
|
15
|
-
end
|
data/features/support/aruba.rb
DELETED
data/features/support/env.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
ENV["RAILS_ENV"] ||= "test"
|
2
|
-
|
3
|
-
require "bundler"
|
4
|
-
require "rails/all"
|
5
|
-
|
6
|
-
require "clearance"
|
7
|
-
require "aruba/cucumber"
|
8
|
-
require_relative "../../spec/dummy/application"
|
9
|
-
require "cucumber/rails/action_controller"
|
10
|
-
require "cucumber/rails/application"
|
11
|
-
require "cucumber/rails/capybara"
|
12
|
-
require "cucumber/rails/database"
|
13
|
-
require "cucumber/rails/hooks"
|
14
|
-
require "cucumber/rails/world"
|
15
|
-
|
16
|
-
ActionController::Base.allow_rescue = false
|
17
|
-
Capybara.default_selector = :css
|
18
|
-
Capybara.save_and_open_page_path = "tmp"
|
19
|
-
Dummy::Application.initialize!
|
20
|
-
|
21
|
-
DatabaseCleaner.strategy = :transaction
|
22
|
-
|
23
|
-
Around do |scenario, block|
|
24
|
-
Bundler.with_clean_env do
|
25
|
-
block.call
|
26
|
-
end
|
27
|
-
end
|
data/spec/models/bcrypt_spec.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Clearance::PasswordStrategies::BCrypt do
|
4
|
-
subject do
|
5
|
-
fake_model_with_password_strategy(Clearance::PasswordStrategies::BCrypt)
|
6
|
-
end
|
7
|
-
|
8
|
-
describe '#password=' do
|
9
|
-
let(:password) { 'password' }
|
10
|
-
let(:encrypted_password) { double("encrypted password") }
|
11
|
-
|
12
|
-
before do
|
13
|
-
allow(BCrypt::Password).to receive(:create).and_return(encrypted_password)
|
14
|
-
end
|
15
|
-
|
16
|
-
it 'encrypts the password into encrypted_password' do
|
17
|
-
subject.password = password
|
18
|
-
|
19
|
-
expect(subject.encrypted_password).to eq encrypted_password
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'encrypts with BCrypt using default cost in non test environments' do
|
23
|
-
allow(Rails).to receive(:env).
|
24
|
-
and_return(ActiveSupport::StringInquirer.new("production"))
|
25
|
-
|
26
|
-
subject.password = password
|
27
|
-
|
28
|
-
expect(BCrypt::Password).to have_received(:create).with(
|
29
|
-
password,
|
30
|
-
cost: ::BCrypt::Engine::DEFAULT_COST
|
31
|
-
)
|
32
|
-
end
|
33
|
-
|
34
|
-
it 'encrypts with BCrypt using minimum cost in test environment' do
|
35
|
-
subject.password = password
|
36
|
-
|
37
|
-
expect(BCrypt::Password).to have_received(:create).with(
|
38
|
-
password,
|
39
|
-
cost: ::BCrypt::Engine::MIN_COST
|
40
|
-
)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe '#authenticated?' do
|
45
|
-
|
46
|
-
before do
|
47
|
-
subject.password = password
|
48
|
-
end
|
49
|
-
|
50
|
-
context 'given a password' do
|
51
|
-
let(:password) { 'password' }
|
52
|
-
|
53
|
-
it 'is authenticated with BCrypt' do
|
54
|
-
expect(subject).to be_authenticated(password)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
context 'given no password' do
|
59
|
-
let(:password) { nil }
|
60
|
-
|
61
|
-
it 'is not authenticated' do
|
62
|
-
expect(subject).not_to be_authenticated(password)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Clearance::PasswordStrategies::Blowfish do
|
4
|
-
subject do
|
5
|
-
fake_model_with_password_strategy(Clearance::PasswordStrategies::Blowfish)
|
6
|
-
end
|
7
|
-
|
8
|
-
describe '#password=' do
|
9
|
-
context 'when the password is set' do
|
10
|
-
let(:salt) { 'salt' }
|
11
|
-
let(:password) { 'password' }
|
12
|
-
|
13
|
-
before do
|
14
|
-
subject.salt = salt
|
15
|
-
subject.password = password
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'does not initialize the salt' do
|
19
|
-
expect(subject.salt).to eq salt
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'encrypts the password using Blowfish and the existing salt' do
|
23
|
-
cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').encrypt
|
24
|
-
cipher.key = Digest::SHA256.digest(salt)
|
25
|
-
expected = cipher.update("--#{salt}--#{password}--") << cipher.final
|
26
|
-
encrypted_password = Base64.decode64(subject.encrypted_password)
|
27
|
-
expect(encrypted_password).to eq expected
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
context 'when the salt is not set' do
|
32
|
-
before do
|
33
|
-
subject.salt = nil
|
34
|
-
subject.password = 'whatever'
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'should initialize the salt' do
|
38
|
-
expect(subject.salt).not_to be_nil
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Clearance::User do
|
4
|
-
subject do
|
5
|
-
class UniquenessValidator < ActiveModel::Validator
|
6
|
-
undef validate
|
7
|
-
|
8
|
-
def validate(record)
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
Class.new do
|
13
|
-
include ActiveModel::Validations
|
14
|
-
|
15
|
-
validates_with UniquenessValidator
|
16
|
-
|
17
|
-
def self.before_validation(*args); end
|
18
|
-
def self.before_create(*args); end
|
19
|
-
|
20
|
-
include Clearance::User
|
21
|
-
end.new
|
22
|
-
end
|
23
|
-
|
24
|
-
describe 'when Clearance.configuration.password_strategy is set' do
|
25
|
-
let(:mock_password_strategy) { Module.new }
|
26
|
-
|
27
|
-
before { Clearance.configuration.password_strategy = mock_password_strategy }
|
28
|
-
|
29
|
-
it 'includes the value it is set to' do
|
30
|
-
expect(subject).to be_kind_of(mock_password_strategy)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
describe 'when Clearance.configuration.password_strategy is not set' do
|
35
|
-
before { Clearance.configuration.password_strategy = nil }
|
36
|
-
|
37
|
-
it 'includes Clearance::PasswordStrategies::BCrypt' do
|
38
|
-
expect(subject).to be_kind_of(Clearance::PasswordStrategies::BCrypt)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
data/spec/models/sha1_spec.rb
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Clearance::PasswordStrategies::SHA1 do
|
4
|
-
subject do
|
5
|
-
fake_model_with_password_strategy(Clearance::PasswordStrategies::SHA1)
|
6
|
-
end
|
7
|
-
|
8
|
-
describe '#password=' do
|
9
|
-
context 'when the password is set' do
|
10
|
-
let(:salt) { 'salt' }
|
11
|
-
let(:password) { 'password' }
|
12
|
-
|
13
|
-
before do
|
14
|
-
subject.salt = salt
|
15
|
-
subject.password = password
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'does not initialize the salt' do
|
19
|
-
expect(subject.salt).to eq salt
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'encrypts the password using SHA1 and the existing salt' do
|
23
|
-
expected = Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
24
|
-
expect(subject.encrypted_password).to eq expected
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
context "when the password is not set" do
|
29
|
-
before do
|
30
|
-
subject.salt = nil
|
31
|
-
subject.password = ""
|
32
|
-
end
|
33
|
-
|
34
|
-
it "initializes the salt" do
|
35
|
-
expect(subject.salt).not_to be_nil
|
36
|
-
end
|
37
|
-
|
38
|
-
it "doesn't encrpt the password" do
|
39
|
-
expect(subject.encrypted_password).to be_nil
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
data/spec/models/user_spec.rb
DELETED
@@ -1,196 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe User do
|
4
|
-
it { should have_db_index(:email) }
|
5
|
-
it { should have_db_index(:remember_token) }
|
6
|
-
|
7
|
-
describe 'when signing up' do
|
8
|
-
it { is_expected.to validate_presence_of(:email) }
|
9
|
-
it { is_expected.to validate_presence_of(:password) }
|
10
|
-
it { is_expected.to allow_value('foo@example.co.uk').for(:email) }
|
11
|
-
it { is_expected.to allow_value('foo@example.com').for(:email) }
|
12
|
-
it { is_expected.to allow_value('foo+bar@example.com').for(:email) }
|
13
|
-
it { is_expected.not_to allow_value('foo@').for(:email) }
|
14
|
-
it { is_expected.not_to allow_value('foo@example..com').for(:email) }
|
15
|
-
it { is_expected.not_to allow_value('foo@.example.com').for(:email) }
|
16
|
-
it { is_expected.not_to allow_value('foo').for(:email) }
|
17
|
-
it { is_expected.not_to allow_value('example.com').for(:email) }
|
18
|
-
it { is_expected.not_to allow_value('foo;@example.com').for(:email) }
|
19
|
-
|
20
|
-
it 'stores email in down case and removes whitespace' do
|
21
|
-
user = create(:user, email: 'Jo hn.Do e @exa mp le.c om')
|
22
|
-
expect(user.email).to eq 'john.doe@example.com'
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe 'when multiple users have signed up' do
|
27
|
-
before { create(:user) }
|
28
|
-
it { is_expected.to validate_uniqueness_of(:email) }
|
29
|
-
end
|
30
|
-
|
31
|
-
describe 'a user' do
|
32
|
-
before do
|
33
|
-
@user = create(:user)
|
34
|
-
@password = @user.password
|
35
|
-
end
|
36
|
-
|
37
|
-
it 'is authenticated with correct email and password' do
|
38
|
-
expect(User.authenticate(@user.email, @password)).to eq(@user)
|
39
|
-
expect(@user).to be_authenticated(@password)
|
40
|
-
end
|
41
|
-
|
42
|
-
it 'is authenticated with correct uppercased email and correct password' do
|
43
|
-
expect(User.authenticate(@user.email.upcase, @password)).to eq(@user)
|
44
|
-
expect(@user).to be_authenticated(@password)
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'is not authenticated with incorrect credentials' do
|
48
|
-
expect(User.authenticate(@user.email, 'bad_password')).to be_nil
|
49
|
-
expect(@user).not_to be_authenticated('bad password')
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'is retrieved via a case-insensitive search' do
|
53
|
-
expect(User.find_by_normalized_email(@user.email.upcase)).to eq(@user)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe 'when resetting authentication with reset_remember_token!' do
|
58
|
-
before do
|
59
|
-
@user = create(:user)
|
60
|
-
@user.remember_token = 'old-token'
|
61
|
-
@user.reset_remember_token!
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'changes the remember token' do
|
65
|
-
expect(@user.remember_token).not_to eq 'old-token'
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
describe 'an email confirmed user' do
|
70
|
-
before do
|
71
|
-
@user = create(:user)
|
72
|
-
@old_encrypted_password = @user.encrypted_password
|
73
|
-
end
|
74
|
-
|
75
|
-
describe 'who updates password' do
|
76
|
-
before do
|
77
|
-
@user.update_password('new_password')
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'changes encrypted password' do
|
81
|
-
expect(@user.encrypted_password).not_to eq @old_encrypted_password
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'does not generate same remember token for users with same password at same time' do
|
87
|
-
allow(Time).to receive(:now).and_return(Time.now)
|
88
|
-
password = 'secret'
|
89
|
-
first_user = create(:user, password: password)
|
90
|
-
second_user = create(:user, password: password)
|
91
|
-
expect(second_user.remember_token).not_to eq first_user.remember_token
|
92
|
-
end
|
93
|
-
|
94
|
-
describe 'a user' do
|
95
|
-
before do
|
96
|
-
@user = create(:user)
|
97
|
-
@old_encrypted_password = @user.encrypted_password
|
98
|
-
end
|
99
|
-
|
100
|
-
describe 'who requests password reminder' do
|
101
|
-
before do
|
102
|
-
expect(@user.confirmation_token).to be_nil
|
103
|
-
@user.forgot_password!
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'generates confirmation token' do
|
107
|
-
expect(@user.confirmation_token).not_to be_nil
|
108
|
-
end
|
109
|
-
|
110
|
-
describe 'and then updates password' do
|
111
|
-
describe 'with password' do
|
112
|
-
before do
|
113
|
-
@user.update_password 'new_password'
|
114
|
-
end
|
115
|
-
|
116
|
-
it 'changes encrypted password' do
|
117
|
-
expect(@user.encrypted_password).not_to eq @old_encrypted_password
|
118
|
-
end
|
119
|
-
|
120
|
-
it 'clears confirmation token' do
|
121
|
-
expect(@user.confirmation_token).to be_nil
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
describe 'with blank password' do
|
126
|
-
before do
|
127
|
-
@user.update_password ''
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'does not change encrypted password' do
|
131
|
-
expect(@user.encrypted_password.to_s).to eq @old_encrypted_password
|
132
|
-
end
|
133
|
-
|
134
|
-
it 'does not clear confirmation token' do
|
135
|
-
expect(@user.confirmation_token).to_not be_nil
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
describe 'a user with an optional email' do
|
143
|
-
before do
|
144
|
-
@user = User.new
|
145
|
-
|
146
|
-
class << @user
|
147
|
-
def email_optional?
|
148
|
-
true
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
subject { @user }
|
154
|
-
|
155
|
-
it { is_expected.to allow_value(nil).for(:email) }
|
156
|
-
it { is_expected.to allow_value('').for(:email) }
|
157
|
-
end
|
158
|
-
|
159
|
-
describe 'user factory' do
|
160
|
-
it 'should create a valid user with just an overridden password' do
|
161
|
-
expect(build(:user, password: 'test')).to be_valid
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
describe 'email address normalization' do
|
166
|
-
let(:email) { 'Jo hn.Do e @exa mp le.c om' }
|
167
|
-
|
168
|
-
it 'downcases the address and strips spaces' do
|
169
|
-
expect(User.normalize_email(email)).to eq 'john.doe@example.com'
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
describe 'the password setter on a User' do
|
174
|
-
let(:password) { 'a-password' }
|
175
|
-
before { subject.send(:password=, password) }
|
176
|
-
|
177
|
-
it 'sets password to the plain-text password' do
|
178
|
-
expect(subject.password).to eq password
|
179
|
-
end
|
180
|
-
|
181
|
-
it 'also sets encrypted_password' do
|
182
|
-
expect(subject.encrypted_password).to_not be_nil
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
describe UserWithOptionalPassword do
|
188
|
-
it { is_expected.to allow_value(nil).for(:password) }
|
189
|
-
it { is_expected.to allow_value('').for(:password) }
|
190
|
-
|
191
|
-
it 'cannot authenticate with blank password' do
|
192
|
-
user = create(:user_with_optional_password)
|
193
|
-
|
194
|
-
expect(UserWithOptionalPassword.authenticate(user.email, '')).to be_nil
|
195
|
-
end
|
196
|
-
end
|