clearance 0.16.3 → 1.0.0.rc1

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 (85) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +0 -2
  3. data/Appraisals +2 -2
  4. data/CONTRIBUTING.md +10 -19
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +81 -82
  7. data/NEWS.md +17 -4
  8. data/README.md +176 -113
  9. data/app/controllers/clearance/passwords_controller.rb +44 -31
  10. data/app/controllers/clearance/sessions_controller.rb +11 -10
  11. data/app/controllers/clearance/users_controller.rb +8 -12
  12. data/app/mailers/clearance_mailer.rb +4 -5
  13. data/app/views/clearance_mailer/change_password.html.erb +2 -4
  14. data/app/views/layouts/application.html.erb +7 -5
  15. data/app/views/passwords/edit.html.erb +8 -7
  16. data/app/views/passwords/new.html.erb +6 -5
  17. data/app/views/sessions/_form.html.erb +7 -5
  18. data/app/views/sessions/new.html.erb +3 -2
  19. data/app/views/users/_form.html.erb +4 -3
  20. data/clearance.gemspec +29 -27
  21. data/config/routes.rb +10 -13
  22. data/db/migrate/20110111224543_create_clearance_users.rb +18 -0
  23. data/db/schema.rb +4 -5
  24. data/features/engine/visitor_resets_password.feature +0 -7
  25. data/features/engine/visitor_signs_in.feature +7 -0
  26. data/features/engine/visitor_signs_up.feature +2 -2
  27. data/features/integration.feature +0 -1
  28. data/features/integration_with_test_unit.feature +43 -0
  29. data/features/step_definitions/configuration_steps.rb +8 -15
  30. data/features/step_definitions/engine/clearance_steps.rb +38 -38
  31. data/features/support/clearance.rb +1 -1
  32. data/features/support/env.rb +4 -21
  33. data/gemfiles/{3.0.12.gemfile → 3.0.15.gemfile} +1 -1
  34. data/gemfiles/{3.0.12.gemfile.lock → 3.0.15.gemfile.lock} +75 -76
  35. data/gemfiles/{3.2.3.gemfile → 3.1.6.gemfile} +1 -1
  36. data/gemfiles/{3.1.4.gemfile.lock → 3.1.6.gemfile.lock} +79 -80
  37. data/gemfiles/{3.1.4.gemfile → 3.2.6.gemfile} +1 -1
  38. data/gemfiles/{3.2.3.gemfile.lock → 3.2.6.gemfile.lock} +80 -81
  39. data/lib/clearance.rb +1 -0
  40. data/lib/clearance/authentication.rb +37 -69
  41. data/lib/clearance/configuration.rb +3 -18
  42. data/lib/clearance/constraints.rb +2 -0
  43. data/lib/clearance/constraints/signed_in.rb +28 -0
  44. data/lib/clearance/constraints/signed_out.rb +9 -0
  45. data/lib/clearance/engine.rb +4 -4
  46. data/lib/clearance/password_strategies.rb +5 -1
  47. data/lib/clearance/password_strategies/bcrypt.rb +27 -0
  48. data/lib/clearance/password_strategies/bcrypt_migration_from_sha1.rb +52 -0
  49. data/lib/clearance/password_strategies/blowfish.rb +11 -15
  50. data/lib/clearance/password_strategies/fake.rb +23 -0
  51. data/lib/clearance/password_strategies/sha1.rb +15 -21
  52. data/lib/clearance/session.rb +28 -20
  53. data/lib/clearance/testing.rb +8 -3
  54. data/lib/clearance/testing/assertion_error.rb +2 -7
  55. data/lib/clearance/testing/deny_access_matcher.rb +27 -32
  56. data/lib/clearance/testing/helpers.rb +7 -8
  57. data/lib/clearance/user.rb +26 -92
  58. data/lib/clearance/version.rb +1 -1
  59. data/lib/generators/clearance/install/templates/db/migrate/upgrade_clearance_to_diesel.rb +24 -26
  60. data/spec/clearance/constraints/signed_in_spec.rb +51 -0
  61. data/spec/clearance/constraints/signed_out_spec.rb +15 -0
  62. data/spec/clearance/rack_session_spec.rb +8 -7
  63. data/spec/clearance/session_spec.rb +28 -27
  64. data/spec/configuration_spec.rb +7 -6
  65. data/spec/controllers/denies_controller_spec.rb +11 -10
  66. data/spec/controllers/flashes_controller_spec.rb +5 -5
  67. data/spec/controllers/forgeries_controller_spec.rb +9 -9
  68. data/spec/controllers/passwords_controller_spec.rb +42 -55
  69. data/spec/controllers/sessions_controller_spec.rb +26 -33
  70. data/spec/controllers/users_controller_spec.rb +16 -14
  71. data/spec/factories.rb +1 -3
  72. data/spec/mailers/clearance_mailer_spec.rb +4 -4
  73. data/spec/models/bcrypt_migration_from_sha1_spec.rb +71 -0
  74. data/spec/models/bcrypt_spec.rb +40 -0
  75. data/spec/models/blowfish_spec.rb +14 -13
  76. data/spec/models/{clearance_user_spec.rb → password_strategies_spec.rb} +5 -5
  77. data/spec/models/sha1_spec.rb +18 -13
  78. data/spec/models/user_spec.rb +58 -73
  79. data/spec/spec_helper.rb +5 -6
  80. data/spec/support/clearance.rb +0 -4
  81. data/spec/support/cookies.rb +25 -27
  82. data/spec/support/request_with_remember_token.rb +19 -0
  83. metadata +95 -90
  84. data/db/migrate/20110111224543_create_diesel_clearance_users.rb +0 -19
  85. data/init.rb +0 -1
@@ -1,18 +1,25 @@
1
1
  module Clearance
2
2
  class Session
3
- REMEMBER_TOKEN_COOKIE = "remember_token".freeze
3
+ REMEMBER_TOKEN_COOKIE = 'remember_token'.freeze
4
4
 
5
5
  def initialize(env)
6
6
  @env = env
7
7
  end
8
8
 
9
- def signed_in?
10
- current_user.present?
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
+ :path => '/'
16
+ )
17
+ end
11
18
  end
12
19
 
13
20
  def current_user
14
21
  @current_user ||= with_remember_token do |token|
15
- Clearance.configuration.user_model.find_by_remember_token(token)
22
+ Clearance.configuration.user_model.find_by_remember_token token
16
23
  end
17
24
  end
18
25
 
@@ -21,35 +28,36 @@ module Clearance
21
28
  end
22
29
 
23
30
  def sign_out
24
- current_user.reset_remember_token! if signed_in?
31
+ if signed_in?
32
+ current_user.reset_remember_token!
33
+ end
34
+
25
35
  @current_user = nil
26
- cookies.delete(REMEMBER_TOKEN_COOKIE)
36
+ cookies.delete REMEMBER_TOKEN_COOKIE
27
37
  end
28
38
 
29
- def add_cookie_to_headers(headers)
30
- if signed_in?
31
- Rack::Utils.set_cookie_header!(headers,
32
- REMEMBER_TOKEN_COOKIE,
33
- :value => current_user.remember_token,
34
- :expires => Clearance.configuration.cookie_expiration.call,
35
- :path => "/")
36
- end
39
+ def signed_in?
40
+ current_user.present?
41
+ end
42
+
43
+ def signed_out?
44
+ ! signed_in?
37
45
  end
38
46
 
39
47
  private
40
48
 
41
- def with_remember_token
42
- if token = remember_token
43
- yield token
44
- end
49
+ def cookies
50
+ @cookies ||= @env['action_dispatch.cookies'] || Rack::Request.new(@env).cookies
45
51
  end
46
52
 
47
53
  def remember_token
48
54
  cookies[REMEMBER_TOKEN_COOKIE]
49
55
  end
50
56
 
51
- def cookies
52
- @cookies ||= @env['action_dispatch.cookies'] || Rack::Request.new(@env).cookies
57
+ def with_remember_token
58
+ if token = remember_token
59
+ yield token
60
+ end
53
61
  end
54
62
  end
55
63
  end
@@ -2,9 +2,14 @@ require 'clearance/testing/assertion_error'
2
2
  require 'clearance/testing/deny_access_matcher'
3
3
  require 'clearance/testing/helpers'
4
4
 
5
- if defined?(Test::Unit::TestCase)
6
- Test::Unit::TestCase.extend Clearance::Testing::Matchers
7
- class Test::Unit::TestCase
5
+ Clearance.configure do |config|
6
+ config.password_strategy = Clearance::PasswordStrategies::Fake
7
+ end
8
+
9
+ if defined?(ActionController::TestCase)
10
+ ActionController::TestCase.extend Clearance::Testing::Matchers
11
+
12
+ class ActionController::TestCase
8
13
  include Clearance::Testing::Helpers
9
14
  end
10
15
  end
@@ -1,11 +1,6 @@
1
1
  module Clearance
2
2
  module Testing
3
- if RUBY_VERSION > "1.9"
4
- require 'minitest/unit'
5
- AssertionError = MiniTest::Assertion
6
- else
7
- require 'test/unit/assertionfailederror'
8
- AssertionError = Test::Unit::AssertionFailedError
9
- end
3
+ require 'minitest/unit'
4
+ AssertionError = MiniTest::Assertion
10
5
  end
11
6
  end
@@ -1,12 +1,6 @@
1
1
  module Clearance
2
2
  module Testing
3
3
  module Matchers
4
- # Ensures a controller denied access.
5
- #
6
- # @example
7
- # it { should deny_access }
8
- # it { should deny_access(:flash => "Denied access.") }
9
- # it { should deny_access(:redirect => sign_in_url) }
10
4
  def deny_access(opts = {})
11
5
  DenyAccessMatcher.new(self, opts)
12
6
  end
@@ -16,11 +10,15 @@ module Clearance
16
10
 
17
11
  def initialize(context, opts)
18
12
  @context = context
19
- @flash = opts[:flash]
20
- @url = opts[:redirect]
13
+ @flash = opts[:flash]
14
+ @url = opts[:redirect]
21
15
 
22
- @failure_message = ""
23
- @negative_failure_message = ""
16
+ @failure_message = ''
17
+ @negative_failure_message = ''
18
+ end
19
+
20
+ def description
21
+ 'deny access'
24
22
  end
25
23
 
26
24
  def matches?(controller)
@@ -28,26 +26,20 @@ module Clearance
28
26
  sets_the_flash? && redirects_to_url?
29
27
  end
30
28
 
31
- def description
32
- "deny access"
33
- end
34
-
35
29
  private
36
30
 
37
- def sets_the_flash?
38
- if @flash.blank?
39
- true
31
+ def denied_access_url
32
+ if @controller.signed_in?
33
+ '/'
40
34
  else
41
- if flash_notice_value == @flash
42
- @negative_failure_message << "Didn't expect to set the flash to #{@flash}"
43
- true
44
- else
45
- @failure_message << "Expected the flash to be set to #{@flash} but was #{flash_notice_value}"
46
- false
47
- end
35
+ @controller.sign_in_url
48
36
  end
49
37
  end
50
38
 
39
+ def flash_notice
40
+ @controller.flash[:notice]
41
+ end
42
+
51
43
  def flash_notice_value
52
44
  if flash_notice.respond_to?(:values)
53
45
  flash_notice.values.first
@@ -56,12 +48,9 @@ module Clearance
56
48
  end
57
49
  end
58
50
 
59
- def flash_notice
60
- @controller.flash[:notice]
61
- end
62
-
63
51
  def redirects_to_url?
64
52
  @url ||= denied_access_url
53
+
65
54
  begin
66
55
  @context.send(:assert_redirected_to, @url)
67
56
  @negative_failure_message << "Didn't expect to redirect to #{@url}."
@@ -72,11 +61,17 @@ module Clearance
72
61
  end
73
62
  end
74
63
 
75
- def denied_access_url
76
- if @controller.signed_in?
77
- '/'
64
+ def sets_the_flash?
65
+ if @flash.blank?
66
+ true
78
67
  else
79
- @controller.sign_in_url
68
+ if flash_notice_value == @flash
69
+ @negative_failure_message << "Didn't expect to set the flash to #{@flash}"
70
+ true
71
+ else
72
+ @failure_message << "Expected the flash to be set to #{@flash} but was #{flash_notice_value}"
73
+ false
74
+ end
80
75
  end
81
76
  end
82
77
  end
@@ -1,22 +1,21 @@
1
1
  module Clearance
2
2
  module Testing
3
3
  module Helpers
4
- def sign_in_as(user)
5
- @controller.current_user = user
6
- return user
4
+ def setup_controller_request_and_response
5
+ super
6
+ @request.env[:clearance] = Clearance::Session.new(@request.env)
7
7
  end
8
8
 
9
9
  def sign_in
10
10
  sign_in_as FactoryGirl.create(:user)
11
11
  end
12
12
 
13
- def sign_out
14
- @controller.current_user = nil
13
+ def sign_in_as(user)
14
+ @controller.current_user = user
15
15
  end
16
16
 
17
- def setup_controller_request_and_response
18
- super
19
- @request.env[:clearance] = Clearance::Session.new(@request.env)
17
+ def sign_out
18
+ @controller.current_user = nil
20
19
  end
21
20
  end
22
21
  end
@@ -4,155 +4,89 @@ module Clearance
4
4
  module User
5
5
  extend ActiveSupport::Concern
6
6
 
7
- # Hook for all Clearance::User modules.
8
- #
9
- # If you need to override parts of Clearance::User,
10
- # extend and include à la carte.
11
- #
12
- # @example
13
- # include Clearance::User::Callbacks
14
- #
15
- # @see Validations
16
- # @see Callbacks
17
7
  included do
18
8
  attr_accessor :password_changing
19
9
  attr_reader :password
20
10
 
21
11
  include Validations
22
12
  include Callbacks
23
-
24
- include (Clearance.configuration.password_strategy || Clearance::PasswordStrategies::SHA1)
13
+ include (Clearance.configuration.password_strategy ||
14
+ Clearance::PasswordStrategies::BCrypt)
25
15
  end
26
16
 
27
17
  module ClassMethods
28
- # Authenticate with email and password.
29
- #
30
- # @param [String, String] email and password
31
- # @return [User, nil] authenticated user or nil
32
- # @example
33
- # User.authenticate("email@example.com", "password")
34
18
  def authenticate(email, password)
35
- return nil unless user = find_by_email(email.to_s.downcase)
36
- return user if user.authenticated?(password)
19
+ if user = find_by_email(email.to_s.downcase)
20
+ if user.authenticated? password
21
+ return user
22
+ end
23
+ end
37
24
  end
38
25
  end
39
26
 
40
27
  module Validations
41
28
  extend ActiveSupport::Concern
42
29
 
43
- # Hook for validations.
44
- #
45
- # :email must be present, unique, formatted
46
- #
47
- # If password is required,
48
- # :password must be present, confirmed
49
30
  included do
50
- validates_presence_of :email, :unless => :email_optional?
51
- validates_uniqueness_of :email, :allow_blank => true
52
- validates_format_of :email, :with => %r{^[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$}i, :allow_blank => true
31
+ validates_presence_of :email, :unless => :email_optional?
32
+ validates_uniqueness_of :email, :allow_blank => true
33
+ validates_format_of :email, :with => %r{^[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$}i, :allow_blank => true
53
34
 
54
- validates_presence_of :password, :unless => :password_optional?
35
+ validates_presence_of :password, :unless => :password_optional?
55
36
  end
56
37
  end
57
38
 
58
39
  module Callbacks
59
40
  extend ActiveSupport::Concern
60
41
 
61
- # Hook for callbacks.
62
- #
63
- # salt, token, password encryption are handled before_save.
64
42
  included do
65
43
  before_validation :downcase_email
66
44
  before_create :generate_remember_token
67
45
  end
68
46
  end
69
47
 
70
- # Set the remember token.
71
- #
72
- # @deprecated Use {#reset_remember_token!} instead
73
- def remember_me!
74
- warn "[DEPRECATION] remember_me!: use reset_remember_token! instead"
75
- reset_remember_token!
48
+ def forgot_password!
49
+ generate_confirmation_token
50
+ save :validate => false
76
51
  end
77
52
 
78
- # Reset the remember token.
79
- #
80
- # @example
81
- # user.reset_remember_token!
82
53
  def reset_remember_token!
83
54
  generate_remember_token
84
- save(:validate => false)
85
- end
86
-
87
- # Mark my account as forgotten password.
88
- #
89
- # @example
90
- # user.forgot_password!
91
- def forgot_password!
92
- generate_confirmation_token
93
- save(:validate => false)
55
+ save :validate => false
94
56
  end
95
57
 
96
- # Update my password.
97
- #
98
- # @return [true, false] password was updated or not
99
- # @example
100
- # user.update_password('new-password')
101
58
  def update_password(new_password)
102
59
  self.password_changing = true
103
- self.password = new_password
60
+ self.password = new_password
61
+
104
62
  if valid?
105
63
  self.confirmation_token = nil
106
64
  generate_remember_token
107
65
  end
108
- save
109
- end
110
66
 
111
- def password=(unencrypted_password)
112
- @password = unencrypted_password
113
- encrypt_password
67
+ save
114
68
  end
115
69
 
116
- protected
70
+ private
117
71
 
118
- def generate_random_code(length = 20)
119
- if RUBY_VERSION >= '1.9'
120
- SecureRandom.hex(length).encode('UTF-8')
121
- else
122
- SecureRandom.hex(length)
123
- end
72
+ def downcase_email
73
+ self.email = email.to_s.downcase
124
74
  end
125
75
 
126
- def generate_remember_token
127
- self.remember_token = generate_random_code
76
+ def email_optional?
77
+ false
128
78
  end
129
79
 
130
80
  def generate_confirmation_token
131
- self.confirmation_token = generate_random_code
81
+ self.confirmation_token = SecureRandom.hex(20).encode('UTF-8')
132
82
  end
133
83
 
134
- # Always false. Override to allow other forms of authentication
135
- # (username, facebook, etc).
136
- # @return [Boolean] true if the email field be left blank for this user
137
- def email_optional?
138
- false
84
+ def generate_remember_token
85
+ self.remember_token = SecureRandom.hex(20).encode('UTF-8')
139
86
  end
140
87
 
141
- # True if the password has been set and the password is not being
142
- # updated and we are not updating the password. Override to allow
143
- # other forms of authentication (username, facebook, etc).
144
- # @return [Boolean] true if the password field can be left blank for this user
145
88
  def password_optional?
146
89
  encrypted_password.present? && password.blank? && password_changing.blank?
147
90
  end
148
-
149
- def password_required?
150
- # warn "[DEPRECATION] password_required?: use !password_optional? instead"
151
- !password_optional?
152
- end
153
-
154
- def downcase_email
155
- self.email = email.to_s.downcase
156
- end
157
91
  end
158
92
  end
@@ -1,3 +1,3 @@
1
1
  module Clearance
2
- VERSION = '0.16.3'
2
+ VERSION = '1.0.0.rc1'
3
3
  end
@@ -1,38 +1,36 @@
1
- class UpgradeClearanceToDiesel < ActiveRecord::Migration
2
- def self.up
3
1
  <%
4
- existing_columns = ActiveRecord::Base.connection.columns(:users).collect { |each| each.name }
5
- columns = [
6
- [:email, 't.string :email'],
7
- [:encrypted_password, 't.string :encrypted_password, :limit => 128'],
8
- [:salt, 't.string :salt, :limit => 128'],
9
- [:confirmation_token, 't.string :confirmation_token, :limit => 128'],
10
- [:remember_token, 't.string :remember_token, :limit => 128']
11
- ].delete_if {|c| existing_columns.include?(c.first.to_s)}
2
+ existing_columns = ActiveRecord::Base.connection.columns(:users).map(&:name)
3
+ new_columns = {
4
+ :email => 't.string :email',
5
+ :encrypted_password => 't.string :encrypted_password, :limit => 128',
6
+ :confirmation_token => 't.string :confirmation_token, :limit => 128',
7
+ :remember_token => 't.string :remember_token, :limit => 128'
8
+ }.reject { |column| existing_columns.include?(column.to_s) }
9
+ -%>
10
+ <%
11
+ existing_indexes = ActiveRecord::Base.connection.indexes(:users).map(&:name)
12
+ new_indexes = {
13
+ :index_users_on_email => 'add_index :users, :email',
14
+ :index_users_on_remember_token => 'add_index :users, :remember_token'
15
+ }.reject { |index| existing_indexes.include?(index.to_s) }
12
16
  -%>
13
- change_table(:users) do |t|
14
- <% columns.each do |c| -%>
15
- <%= c.last %>
17
+ class UpgradeClearanceToDiesel < ActiveRecord::Migration
18
+ def self.up
19
+ change_table :users do |t|
20
+ <% new_columns.values.each do |column| -%>
21
+ <%= column %>
16
22
  <% end -%>
17
23
  end
18
24
 
19
- <%
20
- existing_indexes = ActiveRecord::Base.connection.indexes(:users)
21
- index_names = existing_indexes.collect { |each| each.name }
22
- new_indexes = [
23
- [:index_users_on_email, 'add_index :users, :email'],
24
- [:index_users_on_remember_token, 'add_index :users, :remember_token']
25
- ].delete_if { |each| index_names.include?(each.first.to_s) }
26
- -%>
27
- <% new_indexes.each do |each| -%>
28
- <%= each.last %>
25
+ <% new_indexes.values.each do |index| -%>
26
+ <%= index %>
29
27
  <% end -%>
30
28
  end
31
29
 
32
30
  def self.down
33
- change_table(:users) do |t|
34
- <% unless columns.empty? -%>
35
- t.remove <%= columns.collect { |each| ":#{each.first}" }.join(',') %>
31
+ change_table :users do |t|
32
+ <% if new_columns.any? -%>
33
+ t.remove <%= new_columns.keys.map { |column| ":#{column}" }.join(',') %>
36
34
  <% end -%>
37
35
  end
38
36
  end