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
data/lib/clearance.rb CHANGED
@@ -5,3 +5,4 @@ require 'clearance/authentication'
5
5
  require 'clearance/user'
6
6
  require 'clearance/engine'
7
7
  require 'clearance/password_strategies'
8
+ require 'clearance/constraints'
@@ -4,90 +4,58 @@ module Clearance
4
4
 
5
5
  included do
6
6
  helper_method :current_user, :signed_in?, :signed_out?
7
- hide_action :current_user, :current_user=,
8
- :signed_in?, :signed_out?,
9
- :sign_in, :sign_out,
10
- :authorize, :deny_access
7
+ hide_action :authorize, :current_user, :current_user=, :deny_access,
8
+ :sign_in, :sign_out, :signed_in?, :signed_out?
9
+ end
10
+
11
+ def authenticate(params)
12
+ Clearance.configuration.user_model.authenticate(
13
+ params[:session][:email], params[:session][:password]
14
+ )
15
+ end
16
+
17
+ def authorize
18
+ unless signed_in?
19
+ deny_access
20
+ end
11
21
  end
12
22
 
13
- # Finds the user from the rack clearance session
14
- #
15
- # @return [User, nil]
16
23
  def current_user
17
24
  clearance_session.current_user
18
25
  end
19
26
 
20
- # Set the current user
21
- #
22
- # @param [User]
23
27
  def current_user=(user)
24
28
  clearance_session.sign_in user
25
29
  end
26
30
 
27
- # Is the current user signed in?
28
- #
29
- # @return [true, false]
30
- def signed_in?
31
- clearance_session.signed_in?
32
- end
31
+ def deny_access(flash_message = nil)
32
+ store_location
33
33
 
34
- # Is the current user signed out?
35
- #
36
- # @return [true, false]
37
- def signed_out?
38
- !signed_in?
34
+ if flash_message
35
+ flash[:notice] = flash_message
36
+ end
37
+
38
+ if signed_in?
39
+ redirect_to url_after_denied_access_when_signed_in
40
+ else
41
+ redirect_to url_after_denied_access_when_signed_out
42
+ end
39
43
  end
40
44
 
41
- # Sign user in to cookie.
42
- #
43
- # @param [User]
44
- #
45
- # @example
46
- # sign_in(@user)
47
45
  def sign_in(user)
48
46
  clearance_session.sign_in user
49
47
  end
50
48
 
51
- # Sign user out of cookie.
52
- #
53
- # @example
54
- # sign_out
55
49
  def sign_out
56
50
  clearance_session.sign_out
57
51
  end
58
52
 
59
- # Find the user by the given params or return nil.
60
- # By default, uses email and password.
61
- # Redefine this method and User.authenticate for other mechanisms
62
- # such as username and password.
63
- #
64
- # @example
65
- # @user = authenticate(params)
66
- def authenticate(params)
67
- Clearance.configuration.user_model.authenticate(params[:session][:email],
68
- params[:session][:password])
69
- end
70
-
71
- # Deny the user access if they are signed out.
72
- #
73
- # @example
74
- # before_filter :authorize
75
- def authorize
76
- deny_access unless signed_in?
53
+ def signed_in?
54
+ clearance_session.signed_in?
77
55
  end
78
56
 
79
- # Store the current location and redirect to sign in.
80
- # Display a failure flash message if included.
81
- #
82
- # @param [String] optional flash message to display to denied user
83
- def deny_access(flash_message = nil)
84
- store_location
85
- flash[:notice] = flash_message if flash_message
86
- if signed_in?
87
- redirect_to(url_after_denied_access_when_signed_in)
88
- else
89
- redirect_to(url_after_denied_access_when_signed_out)
90
- end
57
+ def signed_out?
58
+ !signed_in?
91
59
  end
92
60
 
93
61
  # CSRF protection in Rails >= 3.0.4
@@ -99,6 +67,10 @@ module Clearance
99
67
 
100
68
  protected
101
69
 
70
+ def clear_return_to
71
+ session[:return_to] = nil
72
+ end
73
+
102
74
  def clearance_session
103
75
  request.env[:clearance]
104
76
  end
@@ -114,18 +86,14 @@ module Clearance
114
86
  clear_return_to
115
87
  end
116
88
 
117
- def return_to
118
- session[:return_to] || params[:return_to]
119
- end
120
-
121
- def clear_return_to
122
- session[:return_to] = nil
123
- end
124
-
125
89
  def redirect_to_root
126
90
  redirect_to('/')
127
91
  end
128
92
 
93
+ def return_to
94
+ session[:return_to] || params[:return_to]
95
+ end
96
+
129
97
  def url_after_denied_access_when_signed_in
130
98
  '/'
131
99
  end
@@ -1,10 +1,10 @@
1
1
  module Clearance
2
2
  class Configuration
3
- attr_accessor :mailer_sender, :cookie_expiration, :password_strategy, :user_model
3
+ attr_accessor :cookie_expiration, :mailer_sender, :password_strategy, :user_model
4
4
 
5
5
  def initialize
6
- @mailer_sender = 'reply@example.com'
7
6
  @cookie_expiration = lambda { 1.year.from_now.utc }
7
+ @mailer_sender = 'reply@example.com'
8
8
  end
9
9
 
10
10
  def user_model
@@ -16,23 +16,8 @@ module Clearance
16
16
  attr_accessor :configuration
17
17
  end
18
18
 
19
- # Configure Clearance someplace sensible,
20
- # like config/initializers/clearance.rb
21
- #
22
- # If you want users to only be signed in during the current session
23
- # instead of being remembered, do this:
24
- #
25
- # config.cookie_expiration = lambda { }
26
- #
27
- # @example
28
- # Clearance.configure do |config|
29
- # config.mailer_sender = 'me@example.com'
30
- # config.cookie_expiration = lambda { 2.weeks.from_now.utc }
31
- # config.password_strategy = MyPasswordStrategy
32
- # config.user_model = MyNamespace::MyUser
33
- # end
34
19
  def self.configure
35
20
  self.configuration ||= Configuration.new
36
- yield(configuration)
21
+ yield configuration
37
22
  end
38
23
  end
@@ -0,0 +1,2 @@
1
+ require 'clearance/constraints/signed_in'
2
+ require 'clearance/constraints/signed_out'
@@ -0,0 +1,28 @@
1
+ module Clearance
2
+ module Constraints
3
+ class SignedIn
4
+ def initialize(&block)
5
+ @block = block || lambda { |user| true }
6
+ end
7
+
8
+ def matches?(request)
9
+ @request = request
10
+ signed_in? && current_user_fulfills_additional_requirements?
11
+ end
12
+
13
+ private
14
+
15
+ def current_user
16
+ @request.env[:clearance].current_user
17
+ end
18
+
19
+ def current_user_fulfills_additional_requirements?
20
+ @block.call current_user
21
+ end
22
+
23
+ def signed_in?
24
+ @request.env[:clearance].signed_in?
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ module Clearance
2
+ module Constraints
3
+ class SignedOut
4
+ def matches?(request)
5
+ request.env[:clearance].signed_out?
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,10 +1,10 @@
1
- require "clearance"
2
- require "rails"
1
+ require 'clearance'
2
+ require 'rails'
3
3
 
4
4
  module Clearance
5
5
  class Engine < Rails::Engine
6
- initializer "clearance.filter" do |app|
7
- app.config.filter_parameters += [:token, :password]
6
+ initializer 'clearance.filter' do |app|
7
+ app.config.filter_parameters += [:password, :token]
8
8
  end
9
9
 
10
10
  config.app_middleware.insert_after ActionDispatch::Cookies, Clearance::RackSession
@@ -1,6 +1,10 @@
1
1
  module Clearance
2
2
  module PasswordStrategies
3
- autoload :SHA1, 'clearance/password_strategies/sha1'
3
+ autoload :BCrypt, 'clearance/password_strategies/bcrypt'
4
+ autoload :BCryptMigrationFromSHA1,
5
+ 'clearance/password_strategies/bcrypt_migration_from_sha1'
4
6
  autoload :Blowfish, 'clearance/password_strategies/blowfish'
7
+ autoload :Fake, 'clearance/password_strategies/fake'
8
+ autoload :SHA1, 'clearance/password_strategies/sha1'
5
9
  end
6
10
  end
@@ -0,0 +1,27 @@
1
+ module Clearance
2
+ module PasswordStrategies
3
+ module BCrypt
4
+ require 'bcrypt'
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ def authenticated?(password)
9
+ ::BCrypt::Password.new(encrypted_password) == password
10
+ end
11
+
12
+ def password=(new_password)
13
+ @password = new_password
14
+
15
+ if new_password.present?
16
+ self.encrypted_password = encrypt(new_password)
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def encrypt(password)
23
+ ::BCrypt::Password.create(password)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,52 @@
1
+ module Clearance
2
+ module PasswordStrategies
3
+ module BCryptMigrationFromSHA1
4
+ class BCryptUser
5
+ include Clearance::PasswordStrategies::BCrypt
6
+
7
+ def initialize(user)
8
+ @user = user
9
+ end
10
+
11
+ delegate :encrypted_password, :encrypted_password=, to: :@user
12
+ end
13
+
14
+ class SHA1User
15
+ include Clearance::PasswordStrategies::SHA1
16
+
17
+ def initialize(user)
18
+ @user = user
19
+ end
20
+
21
+ delegate :salt, :salt=, :encrypted_password, :encrypted_password=, to: :@user
22
+ end
23
+
24
+ def authenticated?(password)
25
+ authenticated_with_sha1?(password) || authenticated_with_bcrypt?(password)
26
+ end
27
+
28
+ def password=(new_password)
29
+ BCryptUser.new(self).password = new_password
30
+ end
31
+
32
+ private
33
+
34
+ def authenticated_with_bcrypt?(password)
35
+ BCryptUser.new(self).authenticated? password
36
+ end
37
+
38
+ def authenticated_with_sha1?(password)
39
+ if sha1_password?
40
+ if SHA1User.new(self).authenticated? password
41
+ self.password = password
42
+ true
43
+ end
44
+ end
45
+ end
46
+
47
+ def sha1_password?
48
+ self.encrypted_password =~ /^[a-f0-9]{40}$/
49
+ end
50
+ end
51
+ end
52
+ end
@@ -3,35 +3,31 @@ require 'openssl'
3
3
  module Clearance
4
4
  module PasswordStrategies
5
5
  module Blowfish
6
- # Am I authenticated with given password?
7
- #
8
- # @param [String] plain-text password
9
- # @return [true, false]
10
- # @example
11
- # user.authenticated?('password')
12
6
  def authenticated?(password)
13
7
  encrypted_password == encrypt(password)
14
8
  end
15
9
 
16
- protected
17
-
18
- def encrypt_password
10
+ def password=(new_password)
11
+ @password = new_password
19
12
  initialize_salt_if_necessary
20
- if password.present?
21
- self.encrypted_password = encrypt(password)
13
+
14
+ if new_password.present?
15
+ self.encrypted_password = encrypt(new_password)
22
16
  end
23
17
  end
24
18
 
19
+ protected
20
+
21
+ def encrypt(string)
22
+ generate_hash("--#{salt}--#{string}--")
23
+ end
24
+
25
25
  def generate_hash(string)
26
26
  cipher = OpenSSL::Cipher::Cipher.new('bf-cbc').encrypt
27
27
  cipher.key = Digest::SHA256.digest(salt)
28
28
  cipher.update(string) << cipher.final
29
29
  end
30
30
 
31
- def encrypt(string)
32
- generate_hash("--#{salt}--#{string}--")
33
- end
34
-
35
31
  def initialize_salt_if_necessary
36
32
  if salt.blank?
37
33
  self.salt = generate_random_code
@@ -0,0 +1,23 @@
1
+ module Clearance
2
+ module PasswordStrategies
3
+ module Fake
4
+ extend ActiveSupport::Concern
5
+
6
+ def authenticated?(password)
7
+ encrypted_password == password
8
+ end
9
+
10
+ def encrypt(password)
11
+ password
12
+ end
13
+
14
+ def password=(new_password)
15
+ @password = new_password
16
+
17
+ if new_password.present?
18
+ self.encrypted_password = encrypt(password)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,37 +1,31 @@
1
- require 'digest/sha1'
2
-
3
1
  module Clearance
4
2
  module PasswordStrategies
5
3
  module SHA1
6
- # Am I authenticated with given password?
7
- #
8
- # @param [String] plain-text password
9
- # @return [true, false]
10
- # @example
11
- # user.authenticated?('password')
4
+ require 'digest/sha1'
5
+
6
+ extend ActiveSupport::Concern
7
+
12
8
  def authenticated?(password)
13
9
  encrypted_password == encrypt(password)
14
10
  end
15
11
 
16
- protected
17
-
18
- def encrypt_password
12
+ def password=(new_password)
13
+ @password = new_password
19
14
  initialize_salt_if_necessary
20
- if password.present?
21
- self.encrypted_password = encrypt(password)
22
- end
23
- end
24
15
 
25
- def generate_hash(string)
26
- if RUBY_VERSION >= '1.9'
27
- Digest::SHA1.hexdigest(string).encode('UTF-8')
28
- else
29
- Digest::SHA1.hexdigest(string)
16
+ if new_password.present?
17
+ self.encrypted_password = encrypt(new_password)
30
18
  end
31
19
  end
32
20
 
21
+ private
22
+
33
23
  def encrypt(string)
34
- generate_hash("--#{salt}--#{string}--")
24
+ generate_hash "--#{salt}--#{string}--"
25
+ end
26
+
27
+ def generate_hash(string)
28
+ Digest::SHA1.hexdigest(string).encode 'UTF-8'
35
29
  end
36
30
 
37
31
  def initialize_salt_if_necessary