clearance 1.0.0.rc7 → 1.0.0.rc8

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 (55) hide show
  1. checksums.yaml +6 -14
  2. data/.gitignore +3 -3
  3. data/.travis.yml +0 -17
  4. data/Appraisals +2 -6
  5. data/Gemfile +16 -1
  6. data/Gemfile.lock +116 -107
  7. data/NEWS.md +7 -3
  8. data/README.md +195 -129
  9. data/Rakefile +5 -9
  10. data/app/views/layouts/application.html.erb +1 -1
  11. data/clearance.gemspec +20 -17
  12. data/config/locales/clearance.en.yml +2 -1
  13. data/config/routes.rb +2 -2
  14. data/features/add_migrations_to_project.feature +7 -37
  15. data/features/integration_with_rspec.feature +5 -4
  16. data/features/integration_with_test_unit.feature +11 -38
  17. data/features/step_definitions/configuration_steps.rb +94 -8
  18. data/features/step_definitions/gem_file_steps.rb +8 -0
  19. data/features/support/env.rb +7 -0
  20. data/lib/clearance/authorization.rb +16 -3
  21. data/lib/clearance/configuration.rb +2 -0
  22. data/lib/clearance/engine.rb +1 -1
  23. data/lib/clearance/session.rb +12 -11
  24. data/lib/clearance/testing.rb +0 -4
  25. data/lib/clearance/testing/application.rb +23 -24
  26. data/lib/clearance/testing/helpers.rb +1 -1
  27. data/lib/clearance/user.rb +17 -10
  28. data/lib/clearance/version.rb +1 -1
  29. data/lib/generators/clearance/specs/templates/{integration → features}/clearance/user_signs_out_spec.rb +0 -0
  30. data/lib/generators/clearance/specs/templates/{integration → features}/clearance/visitor_resets_password_spec.rb +24 -0
  31. data/lib/generators/clearance/specs/templates/{integration → features}/clearance/visitor_signs_in_spec.rb +0 -0
  32. data/lib/generators/clearance/specs/templates/{integration → features}/clearance/visitor_signs_up_spec.rb +8 -0
  33. data/lib/generators/clearance/specs/templates/{integration → features}/clearance/visitor_updates_password_spec.rb +0 -0
  34. data/lib/generators/clearance/specs/templates/support/features.rb +5 -0
  35. data/lib/generators/clearance/specs/templates/support/{integration → features}/clearance_helpers.rb +1 -1
  36. data/spec/clearance/session_spec.rb +27 -0
  37. data/spec/controllers/apis_controller_spec.rb +36 -0
  38. data/spec/controllers/passwords_controller_spec.rb +13 -11
  39. data/spec/controllers/sessions_controller_spec.rb +13 -27
  40. data/spec/controllers/users_controller_spec.rb +6 -2
  41. data/spec/factories.rb +5 -0
  42. data/spec/models/user_spec.rb +13 -19
  43. data/spec/support/clearance.rb +9 -0
  44. metadata +36 -218
  45. data/gemfiles/3.0.20.gemfile +0 -7
  46. data/gemfiles/3.0.20.gemfile.lock +0 -173
  47. data/gemfiles/3.1.11.gemfile +0 -7
  48. data/gemfiles/3.1.11.gemfile.lock +0 -183
  49. data/gemfiles/3.2.12.gemfile +0 -7
  50. data/gemfiles/3.2.12.gemfile.lock +0 -182
  51. data/gemfiles/3.2.13.rc2.gemfile +0 -7
  52. data/gemfiles/3.2.13.rc2.gemfile.lock +0 -182
  53. data/lib/clearance/password_strategies/fake.rb +0 -23
  54. data/lib/generators/clearance/specs/templates/support/integration.rb +0 -6
  55. data/lib/generators/clearance/specs/templates/support/integration/action_mailer_helpers.rb +0 -19
@@ -2,6 +2,7 @@ module Clearance
2
2
  class Configuration
3
3
  attr_accessor \
4
4
  :cookie_expiration,
5
+ :httponly,
5
6
  :mailer_sender,
6
7
  :password_strategy,
7
8
  :redirect_url,
@@ -10,6 +11,7 @@ module Clearance
10
11
 
11
12
  def initialize
12
13
  @cookie_expiration = lambda { 1.year.from_now.utc }
14
+ @httponly = false
13
15
  @mailer_sender = 'reply@example.com'
14
16
  @secure_cookie = false
15
17
  @redirect_url = '/'
@@ -7,6 +7,6 @@ module Clearance
7
7
  app.config.filter_parameters += [:password, :token]
8
8
  end
9
9
 
10
- config.app_middleware.insert_after ActionDispatch::Cookies, Clearance::RackSession
10
+ config.app_middleware.insert_after ActionDispatch::ParamsParser, Clearance::RackSession
11
11
  end
12
12
  end
@@ -7,15 +7,15 @@ module Clearance
7
7
  end
8
8
 
9
9
  def add_cookie_to_headers(headers)
10
- if signed_in?
11
- Rack::Utils.set_cookie_header!(
12
- headers, REMEMBER_TOKEN_COOKIE,
13
- :value => current_user.remember_token,
14
- :expires => Clearance.configuration.cookie_expiration.call,
15
- :secure => Clearance.configuration.secure_cookie,
16
- :path => '/'
17
- )
18
- end
10
+ Rack::Utils.set_cookie_header!(
11
+ headers,
12
+ REMEMBER_TOKEN_COOKIE,
13
+ :value => remember_token,
14
+ :expires => Clearance.configuration.cookie_expiration.call,
15
+ :secure => Clearance.configuration.secure_cookie,
16
+ :httponly => Clearance.configuration.httponly,
17
+ :path => '/'
18
+ )
19
19
  end
20
20
 
21
21
  def current_user
@@ -26,6 +26,7 @@ module Clearance
26
26
 
27
27
  def sign_in(user)
28
28
  @current_user = user
29
+ cookies[REMEMBER_TOKEN_COOKIE] = @current_user.remember_token
29
30
  end
30
31
 
31
32
  def sign_out
@@ -56,8 +57,8 @@ module Clearance
56
57
  end
57
58
 
58
59
  def with_remember_token
59
- if token = remember_token
60
- yield token
60
+ if remember_token
61
+ yield remember_token
61
62
  end
62
63
  end
63
64
  end
@@ -2,10 +2,6 @@ require 'clearance/testing/assertion_error'
2
2
  require 'clearance/testing/deny_access_matcher'
3
3
  require 'clearance/testing/helpers'
4
4
 
5
- Clearance.configure do |config|
6
- config.password_strategy = Clearance::PasswordStrategies::Fake
7
- end
8
-
9
5
  if defined?(ActionController::TestCase)
10
6
  ActionController::TestCase.extend Clearance::Testing::Matchers
11
7
 
@@ -4,41 +4,40 @@ module Clearance
4
4
  module Testing
5
5
  APP_ROOT = File.expand_path('..', __FILE__).freeze
6
6
 
7
- class Application < Rails::Application
8
- config.encoding = "utf-8"
9
- config.action_mailer.default_url_options = { :host => 'localhost' }
10
-
11
- if Rails::VERSION::MAJOR >= 3 && Rails::VERSION::MINOR >= 1
12
- config.paths['config/database'] = "#{APP_ROOT}/config/database.yml"
13
- config.paths['config/routes'] << "#{APP_ROOT}/config/routes.rb"
14
- config.paths['app/controllers'] << "#{APP_ROOT}/app/controllers"
15
- config.paths['app/views'] << "#{APP_ROOT}/app/views"
16
- config.paths['log'] = "tmp/log/development.log"
17
- config.assets.enabled = true
18
- else
19
- config.paths.config.database = "#{APP_ROOT}/config/database.yml"
20
- config.paths.config.routes << "#{APP_ROOT}/config/routes.rb"
21
- config.paths.app.controllers << "#{APP_ROOT}/app/controllers"
22
- config.paths.app.views << "#{APP_ROOT}/app/views"
23
- config.paths.log = "tmp/log"
24
- end
7
+ def self.rails4?
8
+ Rails::VERSION::MAJOR == 4
9
+ end
25
10
 
26
- config.cache_classes = true
27
- config.whiny_nils = true
28
- config.consider_all_requests_local = true
11
+ class Application < Rails::Application
12
+ config.action_controller.allow_forgery_protection = false
29
13
  config.action_controller.perform_caching = false
30
14
  config.action_dispatch.show_exceptions = false
31
- config.action_controller.allow_forgery_protection = false
15
+ config.action_mailer.default_url_options = { :host => 'localhost' }
32
16
  config.action_mailer.delivery_method = :test
33
17
  config.active_support.deprecation = :stderr
34
- config.secret_token = "SECRET_TOKEN_IS_MIN_30_CHARS_LONG"
18
+ config.assets.enabled = true
19
+ config.cache_classes = true
20
+ config.consider_all_requests_local = true
21
+ config.eager_load = false
22
+ config.encoding = 'utf-8'
23
+ config.paths['app/controllers'] << "#{APP_ROOT}/app/controllers"
24
+ config.paths['app/views'] << "#{APP_ROOT}/app/views"
25
+ config.paths['config/database'] = "#{APP_ROOT}/config/database.yml"
26
+ config.paths['log'] = 'tmp/log/development.log'
27
+ config.secret_token = 'SECRET_TOKEN_IS_MIN_30_CHARS_LONG'
28
+
29
+ if Clearance::Testing.rails4?
30
+ config.paths.add 'config/routes.rb', with: "#{APP_ROOT}/config/routes.rb"
31
+ else
32
+ config.paths.add 'config/routes', with: "#{APP_ROOT}/config/routes.rb"
33
+ end
35
34
 
36
35
  def require_environment!
37
36
  initialize!
38
37
  end
39
38
 
40
39
  def initialize!
41
- FileUtils.mkdir_p(Rails.root.join("db").to_s)
40
+ FileUtils.mkdir_p(Rails.root.join('db').to_s)
42
41
  super unless @initialized
43
42
  end
44
43
  end
@@ -15,7 +15,7 @@ module Clearance
15
15
  end
16
16
 
17
17
  def sign_out
18
- @controller.current_user = nil
18
+ @controller.sign_out
19
19
  end
20
20
  end
21
21
  end
@@ -11,16 +11,13 @@ module Clearance
11
11
 
12
12
  include Validations
13
13
  include Callbacks
14
- include(
15
- Clearance.configuration.password_strategy ||
16
- Clearance::PasswordStrategies::BCrypt
17
- )
14
+ include password_strategy
18
15
  end
19
16
 
20
17
  module ClassMethods
21
18
  def authenticate(email, password)
22
19
  if user = find_by_normalized_email(email)
23
- if user.authenticated? password
20
+ if password.present? && user.authenticated?(password)
24
21
  return user
25
22
  end
26
23
  end
@@ -33,6 +30,12 @@ module Clearance
33
30
  def normalize_email(email)
34
31
  email.to_s.downcase.gsub(/\s+/, "")
35
32
  end
33
+
34
+ private
35
+
36
+ def password_strategy
37
+ Clearance.configuration.password_strategy || PasswordStrategies::BCrypt
38
+ end
36
39
  end
37
40
 
38
41
  module Validations
@@ -45,7 +48,7 @@ module Clearance
45
48
  uniqueness: { allow_blank: true },
46
49
  unless: :email_optional?
47
50
 
48
- validates :password, presence: true, unless: :password_optional?
51
+ validates :password, presence: true, unless: :skip_password_validation?
49
52
  end
50
53
  end
51
54
 
@@ -90,6 +93,14 @@ module Clearance
90
93
  false
91
94
  end
92
95
 
96
+ def password_optional?
97
+ false
98
+ end
99
+
100
+ def skip_password_validation?
101
+ password_optional? || (encrypted_password.present? && !password_changing)
102
+ end
103
+
93
104
  def generate_confirmation_token
94
105
  self.confirmation_token = SecureRandom.hex(20).encode('UTF-8')
95
106
  end
@@ -97,9 +108,5 @@ module Clearance
97
108
  def generate_remember_token
98
109
  self.remember_token = SecureRandom.hex(20).encode('UTF-8')
99
110
  end
100
-
101
- def password_optional?
102
- encrypted_password.present? && password.blank? && password_changing.blank?
103
- end
104
111
  end
105
112
  end
@@ -1,3 +1,3 @@
1
1
  module Clearance
2
- VERSION = '1.0.0.rc7'
2
+ VERSION = '1.0.0.rc8'
3
3
  end
@@ -1,6 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  feature 'Visitor resets password' do
4
+ scenario 'by navigating to the page' do
5
+ visit sign_in_path
6
+
7
+ click_link I18n.t('sessions.form.forgot_password')
8
+
9
+ current_path.should eq new_password_path
10
+ end
11
+
4
12
  scenario 'with valid email' do
5
13
  user = user_with_reset_password
6
14
 
@@ -25,4 +33,20 @@ feature 'Visitor resets password' do
25
33
  def page_should_display_change_password_message
26
34
  page.should have_content I18n.t('passwords.create.description')
27
35
  end
36
+
37
+ def mailer_should_have_delivery(recipient, subject, body)
38
+ ActionMailer::Base.deliveries.should_not be_empty
39
+
40
+ message = ActionMailer::Base.deliveries.any? do |email|
41
+ email.to == [recipient] &&
42
+ email.subject =~ /#{subject}/i &&
43
+ email.body =~ /#{body}/
44
+ end
45
+
46
+ message.should be
47
+ end
48
+
49
+ def mailer_should_have_no_deliveries
50
+ ActionMailer::Base.deliveries.should be_empty
51
+ end
28
52
  end
@@ -1,6 +1,14 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  feature 'Visitor signs up' do
4
+ scenario 'by navigating to the page' do
5
+ visit sign_in_path
6
+
7
+ click_link I18n.t('sessions.form.sign_up')
8
+
9
+ current_path.should eq sign_up_path
10
+ end
11
+
4
12
  scenario 'with valid email and password' do
5
13
  sign_up_with 'valid@example.com', 'password'
6
14
 
@@ -0,0 +1,5 @@
1
+ Dir[Rails.root.join('spec/support/features/*.rb')].each { |f| require f }
2
+
3
+ RSpec.configure do |config|
4
+ config.include Features::ClearanceHelpers, :type => :feature
5
+ end
@@ -1,4 +1,4 @@
1
- module Integration
1
+ module Features
2
2
  module ClearanceHelpers
3
3
  def sign_up_with(email, password)
4
4
  visit sign_up_path
@@ -38,6 +38,33 @@ describe Clearance::Session do
38
38
  session.current_user.should == user
39
39
  end
40
40
 
41
+ context 'if httponly is set' do
42
+ before do
43
+ Clearance.configuration.httponly = true
44
+ session.sign_in(user)
45
+ end
46
+
47
+ it 'sets a httponly cookie' do
48
+ session.add_cookie_to_headers(headers)
49
+
50
+ headers['Set-Cookie'].should =~ /remember_token=.+; HttpOnly/
51
+ end
52
+
53
+ after { restore_default_config }
54
+ end
55
+
56
+ context 'if httponly is not set' do
57
+ before do
58
+ session.sign_in(user)
59
+ end
60
+
61
+ it 'sets a standard cookie' do
62
+ session.add_cookie_to_headers(headers)
63
+
64
+ headers['Set-Cookie'].should_not =~ /remember_token=.+; HttpOnly/
65
+ end
66
+ end
67
+
41
68
  it 'sets a remember token cookie with a default expiration of 1 year from now' do
42
69
  user = create(:user)
43
70
  headers = {}
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ class ApisController < ActionController::Base
4
+ include Clearance::Controller
5
+
6
+ before_filter :authorize
7
+
8
+ respond_to :js
9
+
10
+ def show
11
+ render text: 'response'
12
+ end
13
+
14
+ protected
15
+
16
+ def authorize
17
+ deny_access 'Access denied.'
18
+ end
19
+ end
20
+
21
+ describe ApisController do
22
+ before do
23
+ Rails.application.routes.draw do
24
+ resource :api, only: [:show]
25
+ end
26
+ end
27
+
28
+ after do
29
+ Rails.application.reload_routes!
30
+ end
31
+
32
+ it 'responds with HTTP status code 401 when denied' do
33
+ get :show, format: :js
34
+ subject.should respond_with(:unauthorized)
35
+ end
36
+ end
@@ -1,13 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Clearance::PasswordsController do
4
- include Shoulda::Matchers::ActionMailer
5
-
6
- it {
7
- should route(:get, '/users/1/password/edit').
8
- to(:controller => 'clearance/passwords', :action => 'edit', :user_id => '1')
9
- }
10
-
11
4
  describe 'a signed up user' do
12
5
  before do
13
6
  @user = create(:user)
@@ -31,7 +24,11 @@ describe Clearance::PasswordsController do
31
24
  @user.reload.confirmation_token.should_not be_nil
32
25
  end
33
26
 
34
- it { should have_sent_email.with_subject(/change your password/i) }
27
+ it 'sends an email with relevant subject' do
28
+ email = ActionMailer::Base.deliveries.last
29
+ email.subject.should match(/change your password/i)
30
+ end
31
+
35
32
  it { should respond_with(:success) }
36
33
  end
37
34
 
@@ -45,7 +42,11 @@ describe Clearance::PasswordsController do
45
42
  @user.reload.confirmation_token.should_not be_nil
46
43
  end
47
44
 
48
- it { should have_sent_email.with_subject(/change your password/i) }
45
+ it 'sends an email with relevant subject' do
46
+ email = ActionMailer::Base.deliveries.last
47
+ email.subject.should match(/change your password/i)
48
+ end
49
+
49
50
  it { should respond_with(:success) }
50
51
  end
51
52
 
@@ -113,14 +114,15 @@ describe Clearance::PasswordsController do
113
114
  describe 'on PUT to #update with password' do
114
115
  before do
115
116
  @new_password = 'new_password'
116
- @user.encrypted_password.should_not == @new_password
117
+ @old_encrypted_password = @user.encrypted_password
118
+
117
119
  put :update, :user_id => @user, :token => @user.confirmation_token,
118
120
  :password_reset => { :password => @new_password }
119
121
  @user.reload
120
122
  end
121
123
 
122
124
  it 'should update password' do
123
- @user.encrypted_password.should == @new_password
125
+ @user.encrypted_password.to_s.should_not eq @old_encrypted_password
124
126
  end
125
127
 
126
128
  it 'should clear confirmation token' do
@@ -9,6 +9,19 @@ describe Clearance::SessionsController do
9
9
  it { should_not set_the_flash }
10
10
  end
11
11
 
12
+ context 'when password is optional' do
13
+ describe 'POST create' do
14
+ it 'renders the page with error' do
15
+ user = create(:user_with_optional_password)
16
+
17
+ post :create, session: { email: user.email, password: user.password }
18
+
19
+ expect(response).to render_template(:new)
20
+ expect(flash[:notice]).to match /^Bad email or password/
21
+ end
22
+ end
23
+ end
24
+
12
25
  describe 'on POST to #create with good credentials' do
13
26
  before do
14
27
  @user = create(:user)
@@ -40,33 +53,6 @@ describe Clearance::SessionsController do
40
53
  end
41
54
  end
42
55
 
43
- describe 'on POST to #create with good credentials and a request return url' do
44
- before do
45
- @user = create(:user)
46
- @return_url = '/url_in_the_request'
47
- post :create, :session => { :email => @user.email, :password => @user.password },
48
- :return_to => @return_url
49
- end
50
-
51
- it 'redirects to the return URL' do
52
- should redirect_to(@return_url)
53
- end
54
- end
55
-
56
- describe 'on POST to #create with good credentials and a session return url and request return url' do
57
- before do
58
- @user = create(:user)
59
- @return_url = '/url_in_the_session'
60
- @request.session[:return_to] = @return_url
61
- post :create, :session => { :email => @user.email, :password => @user.password },
62
- :return_to => '/url_in_the_request'
63
- end
64
-
65
- it 'redirects to the return url' do
66
- should redirect_to(@return_url)
67
- end
68
- end
69
-
70
56
  describe 'on DELETE to #destroy given a signed out user' do
71
57
  before do
72
58
  sign_out