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.

Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -3
  3. data/Gemfile.lock +4 -28
  4. data/NEWS.md +12 -0
  5. data/README.md +8 -1
  6. data/Rakefile +11 -8
  7. data/app/controllers/clearance/passwords_controller.rb +1 -0
  8. data/app/controllers/clearance/sessions_controller.rb +14 -2
  9. data/app/controllers/clearance/users_controller.rb +15 -4
  10. data/bin/appraisal +16 -0
  11. data/bin/rake +16 -0
  12. data/bin/rspec +16 -0
  13. data/clearance.gemspec +1 -1
  14. data/gemfiles/rails3.2.gemfile +1 -3
  15. data/gemfiles/rails4.0.gemfile +1 -3
  16. data/gemfiles/rails4.1.gemfile +1 -3
  17. data/gemfiles/rails4.2.gemfile +1 -3
  18. data/lib/clearance/configuration.rb +2 -0
  19. data/lib/clearance/session.rb +12 -6
  20. data/lib/clearance/version.rb +1 -1
  21. data/spec/acceptance/clearance_installation_spec.rb +75 -0
  22. data/spec/{support/app_templates → app_templates}/app/controllers/application_controller.rb +0 -0
  23. data/spec/{support/app_templates → app_templates}/app/models/user.rb +0 -0
  24. data/spec/{support/app_templates → app_templates}/config/routes.rb +0 -0
  25. data/spec/app_templates/testapp/Gemfile +7 -0
  26. data/spec/app_templates/testapp/app/controllers/home_controller.rb +5 -0
  27. data/spec/app_templates/testapp/config/initializers/action_mailer.rb +3 -0
  28. data/spec/app_templates/testapp/config/routes.rb +3 -0
  29. data/spec/clearance/session_spec.rb +13 -0
  30. data/spec/controllers/passwords_controller_spec.rb +100 -131
  31. data/spec/controllers/sessions_controller_spec.rb +66 -52
  32. data/spec/controllers/users_controller_spec.rb +47 -60
  33. data/spec/dummy/app/models/user.rb +3 -0
  34. data/spec/dummy/app/models/user_with_optional_password.rb +7 -0
  35. data/spec/dummy/application.rb +2 -0
  36. data/spec/factories.rb +4 -0
  37. data/spec/{models → password_strategies}/bcrypt_migration_from_sha1_spec.rb +1 -0
  38. data/spec/password_strategies/bcrypt_spec.rb +81 -0
  39. data/spec/password_strategies/blowfish_spec.rb +55 -0
  40. data/spec/password_strategies/password_strategies_spec.rb +28 -0
  41. data/spec/password_strategies/sha1_spec.rb +53 -0
  42. data/spec/support/clearance.rb +0 -16
  43. data/spec/support/fake_model_with_password_strategy.rb +0 -4
  44. data/spec/support/fake_model_without_password_strategy.rb +19 -0
  45. data/spec/support/generator_spec_helpers.rb +1 -1
  46. data/spec/support/request_with_remember_token.rb +1 -1
  47. data/spec/user_spec.rb +186 -0
  48. metadata +23 -67
  49. data/cucumber.yml +0 -1
  50. data/features/integration_with_rspec.feature +0 -23
  51. data/features/integration_with_test_unit.feature +0 -16
  52. data/features/step_definitions/configuration_steps.rb +0 -153
  53. data/features/step_definitions/gem_file_steps.rb +0 -15
  54. data/features/support/aruba.rb +0 -3
  55. data/features/support/env.rb +0 -27
  56. data/spec/models/bcrypt_spec.rb +0 -66
  57. data/spec/models/blowfish_spec.rb +0 -42
  58. data/spec/models/password_strategies_spec.rb +0 -41
  59. data/spec/models/sha1_spec.rb +0 -43
  60. data/spec/models/user_spec.rb +0 -196
@@ -1,88 +1,75 @@
1
- require 'spec_helper'
1
+ require "spec_helper"
2
2
 
3
3
  describe Clearance::UsersController do
4
4
  it { should be_a Clearance::BaseController }
5
5
 
6
- describe 'when signed out' do
7
- before { sign_out }
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
- describe 'on GET to #new' do
10
- before { get :new }
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 'should set assigned user email' do
24
- expect(assigns(:user).email).to eq @email
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
- describe 'on POST to #create with valid attributes' do
29
- before do
30
- user_attributes = FactoryGirl.attributes_for(:user)
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
- it 'assigns a user' do
36
- expect(assigns(:user)).to be_present
37
- end
28
+ get :new
38
29
 
39
- it 'should create a new user' do
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
- describe 'on POST to #create with valid attributes and a session return url' do
47
- before do
48
- user_attributes = FactoryGirl.attributes_for(:user)
49
- @old_user_count = User.count
50
- @return_url = '/url_in_the_session'
51
- @request.session[:return_to] = @return_url
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
- it 'assigns a user' do
56
- expect(assigns(:user)).to be_present
57
- end
42
+ post :create, user: user_attributes
58
43
 
59
- it 'should create a new user' do
60
- expect(User.count).to eq @old_user_count + 1
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
- it { should redirect_to(@return_url) }
64
- end
65
- end
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
- describe 'A signed-in user' do
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
- it 'redirects to the home page' do
77
- should redirect_to(Clearance.configuration.redirect_url)
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
- describe 'POST to create' do
82
- before { post :create, user: {} }
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
- it 'redirects to the home page' do
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
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ include Clearance::User
3
+ end
@@ -0,0 +1,7 @@
1
+ class UserWithOptionalPassword < User
2
+ private
3
+
4
+ def password_optional?
5
+ true
6
+ end
7
+ end
@@ -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 ''
@@ -1,4 +1,5 @@
1
1
  require "spec_helper"
2
+ include FakeModelWithPasswordStrategy
2
3
 
3
4
  describe Clearance::PasswordStrategies::BCryptMigrationFromSHA1 do
4
5
  describe "#password=" do
@@ -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
@@ -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
@@ -8,7 +8,3 @@ module FakeModelWithPasswordStrategy
8
8
  end.new
9
9
  end
10
10
  end
11
-
12
- RSpec.configure do |config|
13
- config.include FakeModelWithPasswordStrategy
14
- end
@@ -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