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,88 +1,75 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
describe Clearance::UsersController do
|
4
4
|
it { should be_a Clearance::BaseController }
|
5
5
|
|
6
|
-
describe
|
7
|
-
|
6
|
+
describe "on GET to #new" do
|
7
|
+
context "when signed out" do
|
8
|
+
it "renders a form for a new user" do
|
9
|
+
get :new
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
it { is_expected.to respond_with(:success) }
|
13
|
-
it { is_expected.to render_template(:new) }
|
14
|
-
it { is_expected.not_to set_the_flash }
|
15
|
-
end
|
16
|
-
|
17
|
-
describe 'on GET to #new with email' do
|
18
|
-
before do
|
19
|
-
@email = 'a@example.com'
|
20
|
-
get :new, user: { email: @email }
|
11
|
+
expect(response).to be_success
|
12
|
+
expect(response).to render_template(:new)
|
21
13
|
end
|
22
14
|
|
23
|
-
it
|
24
|
-
|
15
|
+
it "defaults email field to the value provided in the query string" do
|
16
|
+
get :new, user: { email: "a@example.com" }
|
17
|
+
|
18
|
+
expect(assigns(:user).email).to eq "a@example.com"
|
19
|
+
expect(response).to be_success
|
20
|
+
expect(response).to render_template(:new)
|
25
21
|
end
|
26
22
|
end
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
@old_user_count = User.count
|
32
|
-
post :create, user: user_attributes
|
33
|
-
end
|
24
|
+
context "when signed in" do
|
25
|
+
it "redirects to the configured clearance redirect url" do
|
26
|
+
sign_in
|
34
27
|
|
35
|
-
|
36
|
-
expect(assigns(:user)).to be_present
|
37
|
-
end
|
28
|
+
get :new
|
38
29
|
|
39
|
-
|
40
|
-
expect(User.count).to eq @old_user_count + 1
|
30
|
+
expect(response).to redirect_to(Clearance.configuration.redirect_url)
|
41
31
|
end
|
42
|
-
|
43
|
-
it { should redirect_to_url_after_create }
|
44
32
|
end
|
33
|
+
end
|
45
34
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
post :create, user: user_attributes
|
53
|
-
end
|
35
|
+
describe "on POST to #create" do
|
36
|
+
context "when signed out" do
|
37
|
+
context "with valid attributes" do
|
38
|
+
it "assigns and creates a user then redirects to the redirect_url" do
|
39
|
+
user_attributes = FactoryGirl.attributes_for(:user)
|
40
|
+
old_user_count = User.count
|
54
41
|
|
55
|
-
|
56
|
-
expect(assigns(:user)).to be_present
|
57
|
-
end
|
42
|
+
post :create, user: user_attributes
|
58
43
|
|
59
|
-
|
60
|
-
|
44
|
+
expect(assigns(:user)).to be_present
|
45
|
+
expect(User.count).to eq old_user_count + 1
|
46
|
+
expect(response).to redirect_to(Clearance.configuration.redirect_url)
|
47
|
+
end
|
61
48
|
end
|
62
49
|
|
63
|
-
|
64
|
-
|
65
|
-
|
50
|
+
context "with valid attributes and a session return url" do
|
51
|
+
it "assigns and creates a user then redirects to the return_url" do
|
52
|
+
user_attributes = FactoryGirl.attributes_for(:user)
|
53
|
+
old_user_count = User.count
|
54
|
+
return_url = "/url_in_the_session"
|
55
|
+
@request.session[:return_to] = return_url
|
66
56
|
|
67
|
-
|
68
|
-
before do
|
69
|
-
@user = create(:user)
|
70
|
-
sign_in_as @user
|
71
|
-
end
|
72
|
-
|
73
|
-
describe 'GET to new' do
|
74
|
-
before { get :new }
|
57
|
+
post :create, user: user_attributes
|
75
58
|
|
76
|
-
|
77
|
-
|
59
|
+
expect(assigns(:user)).to be_present
|
60
|
+
expect(User.count).to eq old_user_count + 1
|
61
|
+
expect(response).to redirect_to(return_url)
|
62
|
+
end
|
78
63
|
end
|
79
64
|
end
|
80
65
|
|
81
|
-
|
82
|
-
|
66
|
+
context "when signed in" do
|
67
|
+
it "redirects to the configured clearance redirect url" do
|
68
|
+
sign_in
|
69
|
+
|
70
|
+
post :create, user: {}
|
83
71
|
|
84
|
-
|
85
|
-
should redirect_to(Clearance.configuration.redirect_url)
|
72
|
+
expect(response).to redirect_to(Clearance.configuration.redirect_url)
|
86
73
|
end
|
87
74
|
end
|
88
75
|
end
|
data/spec/dummy/application.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "rails/all"
|
2
|
+
require "clearance"
|
2
3
|
|
3
4
|
module Dummy
|
4
5
|
APP_ROOT = File.expand_path("..", __FILE__).freeze
|
@@ -23,6 +24,7 @@ module Dummy
|
|
23
24
|
config.eager_load = false
|
24
25
|
config.encoding = "utf-8"
|
25
26
|
config.paths["app/controllers"] << "#{APP_ROOT}/app/controllers"
|
27
|
+
config.paths["app/models"] << "#{APP_ROOT}/app/models"
|
26
28
|
config.paths["app/views"] << "#{APP_ROOT}/app/views"
|
27
29
|
config.paths["config/database"] = "#{APP_ROOT}/config/database.yml"
|
28
30
|
config.paths["log"] = "tmp/log/development.log"
|
data/spec/factories.rb
CHANGED
@@ -7,6 +7,10 @@ FactoryGirl.define do
|
|
7
7
|
email
|
8
8
|
password 'password'
|
9
9
|
|
10
|
+
trait :with_forgotten_password do
|
11
|
+
confirmation_token Clearance::Token.new
|
12
|
+
end
|
13
|
+
|
10
14
|
factory :user_with_optional_password, class: 'UserWithOptionalPassword' do
|
11
15
|
password nil
|
12
16
|
encrypted_password ''
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
include FakeModelWithPasswordStrategy
|
3
|
+
|
4
|
+
describe Clearance::PasswordStrategies::BCrypt do
|
5
|
+
describe "#password=" do
|
6
|
+
it "encrypts the password into encrypted_password" do
|
7
|
+
stub_bcrypt_password
|
8
|
+
model_instance = fake_model_with_bcrypt_strategy
|
9
|
+
|
10
|
+
model_instance.password = password
|
11
|
+
|
12
|
+
expect(model_instance.encrypted_password).to eq encrypted_password
|
13
|
+
end
|
14
|
+
|
15
|
+
it "encrypts with BCrypt using default cost in non test environments" do
|
16
|
+
stub_bcrypt_password
|
17
|
+
model_instance = fake_model_with_bcrypt_strategy
|
18
|
+
allow(Rails).to receive(:env).
|
19
|
+
and_return(ActiveSupport::StringInquirer.new("production"))
|
20
|
+
|
21
|
+
model_instance.password = password
|
22
|
+
|
23
|
+
expect(BCrypt::Password).to have_received(:create).with(
|
24
|
+
password,
|
25
|
+
cost: ::BCrypt::Engine::DEFAULT_COST
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "encrypts with BCrypt using minimum cost in test environment" do
|
30
|
+
stub_bcrypt_password
|
31
|
+
model_instance = fake_model_with_bcrypt_strategy
|
32
|
+
|
33
|
+
model_instance.password = password
|
34
|
+
|
35
|
+
expect(BCrypt::Password).to have_received(:create).with(
|
36
|
+
password,
|
37
|
+
cost: ::BCrypt::Engine::MIN_COST
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def stub_bcrypt_password
|
42
|
+
allow(BCrypt::Password).to receive(:create).and_return(encrypted_password)
|
43
|
+
end
|
44
|
+
|
45
|
+
def encrypted_password
|
46
|
+
@encrypted_password ||= double("encrypted password")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#authenticated?" do
|
51
|
+
context "given a password" do
|
52
|
+
it "is authenticated with BCrypt" do
|
53
|
+
model_instance = fake_model_with_bcrypt_strategy
|
54
|
+
|
55
|
+
model_instance.password = password
|
56
|
+
|
57
|
+
expect(model_instance).to be_authenticated(password)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "given no password" do
|
62
|
+
it "is not authenticated" do
|
63
|
+
model_instance = fake_model_with_bcrypt_strategy
|
64
|
+
|
65
|
+
password = nil
|
66
|
+
|
67
|
+
expect(model_instance).not_to be_authenticated(password)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def fake_model_with_bcrypt_strategy
|
73
|
+
@model_instance ||= fake_model_with_password_strategy(
|
74
|
+
Clearance::PasswordStrategies::BCrypt
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def password
|
79
|
+
"password"
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
include FakeModelWithPasswordStrategy
|
3
|
+
|
4
|
+
describe Clearance::PasswordStrategies::Blowfish do
|
5
|
+
describe "#password=" do
|
6
|
+
context "when the password is set" do
|
7
|
+
it "does not initialize the salt" do
|
8
|
+
model_instance = fake_model_with_blowfish_strategy
|
9
|
+
model_instance.salt = salt
|
10
|
+
model_instance.password = password
|
11
|
+
|
12
|
+
expect(model_instance.salt).to eq salt
|
13
|
+
end
|
14
|
+
|
15
|
+
it "encrypts the password using Blowfish and the existing salt" do
|
16
|
+
model_instance = fake_model_with_blowfish_strategy
|
17
|
+
model_instance.salt = salt
|
18
|
+
model_instance.salt = salt
|
19
|
+
model_instance.password = password
|
20
|
+
cipher = OpenSSL::Cipher::Cipher.new("bf-cbc").encrypt
|
21
|
+
cipher.key = Digest::SHA256.digest(salt)
|
22
|
+
expected = cipher.update("--#{salt}--#{password}--") << cipher.final
|
23
|
+
|
24
|
+
encrypted_password = Base64.decode64(model_instance.encrypted_password)
|
25
|
+
|
26
|
+
expect(encrypted_password).to eq expected
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "when the salt is not set" do
|
31
|
+
it "should initialize the salt" do
|
32
|
+
model_instance = fake_model_with_blowfish_strategy
|
33
|
+
model_instance.salt = salt
|
34
|
+
model_instance.salt = nil
|
35
|
+
model_instance.password = password
|
36
|
+
|
37
|
+
expect(model_instance.salt).not_to be_nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def fake_model_with_blowfish_strategy
|
43
|
+
@model_instance ||= fake_model_with_password_strategy(
|
44
|
+
Clearance::PasswordStrategies::Blowfish
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def salt
|
49
|
+
"salt"
|
50
|
+
end
|
51
|
+
|
52
|
+
def password
|
53
|
+
"password"
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
include FakeModelWithoutPasswordStrategy
|
3
|
+
|
4
|
+
describe "Password strategy configuration" do
|
5
|
+
describe "when Clearance.configuration.password_strategy is set" do
|
6
|
+
it "includes the value it is set to" do
|
7
|
+
mock_password_strategy = Module.new
|
8
|
+
|
9
|
+
Clearance.configuration.password_strategy = mock_password_strategy
|
10
|
+
|
11
|
+
expect(model_instance).to be_kind_of(mock_password_strategy)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "when Clearance.configuration.password_strategy is not set" do
|
16
|
+
it "includes Clearance::PasswordStrategies::BCrypt" do
|
17
|
+
Clearance.configuration.password_strategy = nil
|
18
|
+
|
19
|
+
expect(model_instance).to be_kind_of(
|
20
|
+
Clearance::PasswordStrategies::BCrypt
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def model_instance
|
26
|
+
fake_model_without_password_strategy
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
include FakeModelWithPasswordStrategy
|
3
|
+
|
4
|
+
describe Clearance::PasswordStrategies::SHA1 do
|
5
|
+
describe "#password=" do
|
6
|
+
context "when the salt is set" do
|
7
|
+
it "does not initialize the salt when assigned" do
|
8
|
+
model_instance = fake_model_with_sha1_strategy
|
9
|
+
|
10
|
+
model_instance.salt = salt
|
11
|
+
|
12
|
+
expect(model_instance.salt).to eq salt
|
13
|
+
end
|
14
|
+
|
15
|
+
it "encrypts the password using SHA1 and the existing salt" do
|
16
|
+
model_instance = fake_model_with_sha1_strategy
|
17
|
+
model_instance.salt = salt
|
18
|
+
model_instance.password = password
|
19
|
+
|
20
|
+
expected = Digest::SHA1.hexdigest("--#{salt}--#{password}--")
|
21
|
+
|
22
|
+
expect(model_instance.encrypted_password).to eq expected
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when the salt is set" do
|
27
|
+
it "generates the salt" do
|
28
|
+
model_instance = fake_model_with_sha1_strategy
|
29
|
+
model_instance.password = ""
|
30
|
+
|
31
|
+
expect(model_instance.salt).not_to be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "doesn't encrypt the password" do
|
35
|
+
model_instance = fake_model_with_sha1_strategy
|
36
|
+
|
37
|
+
expect(model_instance.encrypted_password).to be_nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def fake_model_with_sha1_strategy
|
43
|
+
fake_model_with_password_strategy(Clearance::PasswordStrategies::SHA1)
|
44
|
+
end
|
45
|
+
|
46
|
+
def salt
|
47
|
+
"salt"
|
48
|
+
end
|
49
|
+
|
50
|
+
def password
|
51
|
+
"password"
|
52
|
+
end
|
53
|
+
end
|
data/spec/support/clearance.rb
CHANGED
@@ -4,22 +4,6 @@ Clearance.configure do |config|
|
|
4
4
|
# need an empty block to initialize the configuration object
|
5
5
|
end
|
6
6
|
|
7
|
-
class ApplicationController < ActionController::Base
|
8
|
-
include Clearance::Controller
|
9
|
-
end
|
10
|
-
|
11
|
-
class User < ActiveRecord::Base
|
12
|
-
include Clearance::User
|
13
|
-
end
|
14
|
-
|
15
|
-
class UserWithOptionalPassword < User
|
16
|
-
private
|
17
|
-
|
18
|
-
def password_optional?
|
19
|
-
true
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
7
|
module Clearance
|
24
8
|
module Test
|
25
9
|
module Redirects
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module FakeModelWithoutPasswordStrategy
|
2
|
+
def fake_model_without_password_strategy
|
3
|
+
Class.new do
|
4
|
+
include ActiveModel::Validations
|
5
|
+
|
6
|
+
validates_with UniquenessValidator
|
7
|
+
|
8
|
+
def self.before_validation(*); end
|
9
|
+
def self.before_create(*); end
|
10
|
+
|
11
|
+
include Clearance::User
|
12
|
+
end.new
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class UniquenessValidator < ActiveModel::Validator
|
17
|
+
def validate(_record)
|
18
|
+
end
|
19
|
+
end
|