authenticate 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +17 -0
  4. data/CHANGELOG.md +14 -2
  5. data/Gemfile +2 -2
  6. data/Gemfile.lock +2 -3
  7. data/README.md +103 -30
  8. data/app/controllers/authenticate/authenticate_controller.rb +2 -0
  9. data/app/controllers/authenticate/passwords_controller.rb +1 -1
  10. data/app/controllers/authenticate/sessions_controller.rb +1 -1
  11. data/app/controllers/authenticate/users_controller.rb +1 -1
  12. data/app/views/passwords/new.html.erb +4 -0
  13. data/authenticate.gemspec +8 -10
  14. data/config/locales/authenticate.en.yml +9 -1
  15. data/gemfiles/rails42.gemfile +12 -0
  16. data/lib/authenticate/callbacks/authenticatable.rb +4 -1
  17. data/lib/authenticate/callbacks/brute_force.rb +2 -1
  18. data/lib/authenticate/callbacks/lifetimed.rb +2 -2
  19. data/lib/authenticate/callbacks/timeoutable.rb +1 -1
  20. data/lib/authenticate/callbacks/trackable.rb +1 -1
  21. data/lib/authenticate/controller.rb +12 -3
  22. data/lib/authenticate/debug.rb +8 -3
  23. data/lib/authenticate/engine.rb +3 -0
  24. data/lib/authenticate/lifecycle.rb +25 -16
  25. data/lib/authenticate/model/brute_force.rb +7 -3
  26. data/lib/authenticate/model/db_password.rb +12 -14
  27. data/lib/authenticate/model/email.rb +1 -1
  28. data/lib/authenticate/model/lifetimed.rb +7 -8
  29. data/lib/authenticate/model/password_reset.rb +12 -5
  30. data/lib/authenticate/model/timeoutable.rb +9 -12
  31. data/lib/authenticate/model/trackable.rb +5 -2
  32. data/lib/authenticate/model/username.rb +0 -8
  33. data/lib/authenticate/modules.rb +3 -2
  34. data/lib/authenticate/session.rb +7 -7
  35. data/lib/authenticate/version.rb +1 -1
  36. data/spec/dummy/config/initializers/authenticate.rb +3 -2
  37. data/spec/factories/users.rb +11 -1
  38. data/spec/model/db_password_spec.rb +33 -0
  39. data/spec/model/email_spec.rb +25 -0
  40. data/spec/model/lifetimed_spec.rb +35 -0
  41. data/spec/model/password_reset_spec.rb +81 -0
  42. data/spec/model/session_spec.rb +0 -6
  43. data/spec/model/timeoutable_spec.rb +20 -0
  44. data/spec/model/trackable_spec.rb +56 -0
  45. data/spec/spec_helper.rb +6 -0
  46. metadata +18 -13
@@ -42,16 +42,8 @@ module Authenticate
42
42
  username = credentials[0]
43
43
  find_by_username username
44
44
  end
45
-
46
- # def normalize_username(username)
47
- # username.to_s.downcase.gsub(/\s+/, '')
48
- # end
49
45
  end
50
46
 
51
- # def normalize_username
52
- # self.username = self.class.normalize_username(username)
53
- # end
54
-
55
47
  end
56
48
 
57
49
  end
@@ -1,6 +1,7 @@
1
1
  module Authenticate
2
2
  module Modules
3
3
  extend ActiveSupport::Concern
4
+ include Authenticate::Debug
4
5
 
5
6
  # Module to help Authenticate's user model load Authenticate modules.
6
7
  #
@@ -26,7 +27,7 @@ module Authenticate
26
27
  def load_modules
27
28
  constants = []
28
29
  Authenticate.configuration.modules.each do |mod|
29
- puts "load_modules about to load #{mod.to_s}"
30
+ # puts "load_modules about to load #{mod.to_s}"
30
31
  require "authenticate/model/#{mod.to_s}" if mod.is_a?(Symbol)
31
32
  mod = load_constant(mod) if mod.is_a?(Symbol)
32
33
  constants << mod
@@ -70,7 +71,7 @@ module Authenticate
70
71
  end
71
72
 
72
73
  def message
73
- "The following attribute(s) is (are) missing on your model: #{@attributes.join(", ")}"
74
+ "The following attribute(s) is (are) missing on your user model: #{@attributes.join(", ")}"
74
75
  end
75
76
  end
76
77
 
@@ -11,7 +11,7 @@ module Authenticate
11
11
  @request = request # trackable module accesses request
12
12
  @cookies = cookies
13
13
  @session_token = @cookies[cookie_name]
14
- d 'SESSION initialize: @session_token: ' + @session_token.inspect
14
+ debug 'SESSION initialize: @session_token: ' + @session_token.inspect
15
15
  end
16
16
 
17
17
  # consecutive_failed_logins_limit
@@ -28,9 +28,9 @@ module Authenticate
28
28
  #
29
29
  # @return [User]
30
30
  def login(user, &block)
31
- d 'session.login()'
31
+ debug 'session.login()'
32
32
  @current_user = user
33
- d "session.login @current_user: #{@current_user.inspect}"
33
+ debug "session.login @current_user: #{@current_user.inspect}"
34
34
  # todo extract token gen to two different strategies
35
35
  @current_user.generate_session_token if user.present?
36
36
 
@@ -39,7 +39,7 @@ module Authenticate
39
39
  Authenticate.lifecycle.run_callbacks(:after_authentication, @current_user, self, { event: :authentication })
40
40
  end
41
41
 
42
- d "session.login after lifecycle callbacks, message: #{message}"
42
+ debug "session.login after lifecycle callbacks, message: #{message}"
43
43
  status = message.present? ? Failure.new(message) : Success.new
44
44
  if status.success?
45
45
  @current_user.save
@@ -58,7 +58,7 @@ module Authenticate
58
58
  #
59
59
  # @return [User]
60
60
  def current_user
61
- d 'session.current_user'
61
+ debug 'session.current_user'
62
62
  if @session_token.present?
63
63
  @current_user ||= load_user
64
64
  end
@@ -69,7 +69,7 @@ module Authenticate
69
69
  #
70
70
  # @return [Boolean]
71
71
  def authenticated?
72
- d 'session.authenticated?'
72
+ debug 'session.authenticated?'
73
73
  current_user.present?
74
74
  end
75
75
 
@@ -109,7 +109,7 @@ module Authenticate
109
109
  cookie_hash[:domain] = Authenticate.configuration.cookie_domain if Authenticate.configuration.cookie_domain
110
110
  # @cookies.signed[cookie_name] = cookie_hash
111
111
  @cookies[cookie_name] = cookie_hash
112
- d 'session.write_cookie WROTE COOKIE I HOPE. Cookie guts:' + @cookies[cookie_name].inspect
112
+ debug 'session.write_cookie WROTE COOKIE I HOPE. Cookie guts:' + @cookies[cookie_name].inspect
113
113
  end
114
114
 
115
115
  def cookie_name
@@ -1,3 +1,3 @@
1
1
  module Authenticate
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
@@ -6,9 +6,10 @@ Authenticate.configure do |config|
6
6
  # config.cookie_path = '/'
7
7
  # config.secure_cookie = false # set to true in production https environments
8
8
  # config.http_only = false # set to true if you can
9
- # config.timeout_in = 45.minutes
10
- # config.max_session_lifetime = 8.hours
9
+ config.timeout_in = 45.minutes
10
+ config.max_session_lifetime = 5.minutes
11
11
  config.max_consecutive_bad_logins_allowed = 1
12
12
  config.bad_login_lockout_period = 2.minutes
13
+ config.reset_password_within = 5.minutes
13
14
  # config.authentication_strategy = :email
14
15
  end
@@ -7,7 +7,17 @@ FactoryGirl.define do
7
7
 
8
8
  factory :user do
9
9
  email
10
- encrypted_password 'password'
10
+ # encrypted_password 'password'
11
+ password 'password'
12
+
13
+ trait :without_email do
14
+ email nil
15
+ end
16
+
17
+ trait :without_password do
18
+ password nil
19
+ encrypted_password nil
20
+ end
11
21
 
12
22
  trait :with_session_token do
13
23
  session_token 'this_is_a_big_fake_long_token'
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'authenticate/model/db_password'
3
+
4
+
5
+ describe Authenticate::Model::DbPassword do
6
+
7
+ it 'validates password' do
8
+ user = build(:user, :without_password)
9
+ user.save
10
+ expect(user.errors.count).to be(1)
11
+ expect(user.errors.messages[:password]).to eq(["can't be blank"])
12
+ end
13
+
14
+ it 'matches a password' do
15
+ user = create(:user)
16
+ expect(user.password_match? 'password').to be_truthy
17
+ end
18
+
19
+ it 'fails to match a bad password' do
20
+ user = create(:user)
21
+ expect(user.password_match? 'bad password').to be_falsey
22
+ end
23
+
24
+ it 'sets a password' do
25
+ user = create(:user)
26
+ user.password = 'new_password'
27
+ user.save!
28
+
29
+ user = User.find(user.id)
30
+ expect(user.password_match? 'new_password').to be_truthy
31
+ end
32
+
33
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+ require 'authenticate/model/email'
3
+
4
+
5
+ describe Authenticate::Model::Email do
6
+
7
+ it 'validates email' do
8
+ user = build(:user, :without_email)
9
+ user.save
10
+ expect(user.errors.count).to be(2)
11
+ expect(user.errors.messages[:email]).to include('is invalid')
12
+ expect(user.errors.messages[:email]).to include("can't be blank")
13
+ end
14
+
15
+ it 'extracts credentials from params' do
16
+ params = {session:{email:'foo', password:'bar'}}
17
+ expect(User.credentials(params)).to match_array(['foo', 'bar'])
18
+ end
19
+
20
+ it 'authenticates from credentials' do
21
+ user = create(:user)
22
+ expect(User.authenticate([user.email, user.password])).to eq(user)
23
+ end
24
+
25
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'authenticate/model/lifetimed'
3
+
4
+
5
+ describe Authenticate::Model::Lifetimed do
6
+
7
+ context '#max_session_lifetime_exceeded?' do
8
+ before {
9
+ Authenticate.configure do |config|
10
+ config.max_session_lifetime = 10.minutes
11
+ end
12
+ }
13
+
14
+ it 'passes fresh sessions' do
15
+ user = create(:user, current_sign_in_at: 1.minute.ago.utc)
16
+ expect(user).to_not be_max_session_lifetime_exceeded
17
+ end
18
+
19
+ it 'detects timed out sessions' do
20
+ user = create(:user, current_sign_in_at: 5.hours.ago.utc)
21
+ expect(user).to be_max_session_lifetime_exceeded
22
+ end
23
+
24
+ describe 'max_session_lifetime param not set' do
25
+ it 'does not time out' do
26
+ user = create(:user, current_sign_in_at: 5.hours.ago.utc)
27
+ Authenticate.configure do |config|
28
+ config.max_session_lifetime = nil
29
+ end
30
+ expect(user).to_not be_max_session_lifetime_exceeded
31
+ end
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+ require 'authenticate/model/password_reset'
3
+
4
+
5
+ describe Authenticate::Model::PasswordReset do
6
+ before(:all) {
7
+ Authenticate.configuration.reset_password_within = 5.minutes
8
+ }
9
+ context 'forgot_password!' do
10
+ subject { create(:user) }
11
+ before { subject.forgot_password! }
12
+
13
+ it 'generates a password reset token' do
14
+ expect(subject.password_reset_token).to_not be_nil
15
+ end
16
+
17
+ it 'sets password reset sent at' do
18
+ expect(subject.password_reset_sent_at).to_not be_nil
19
+ end
20
+
21
+ end
22
+
23
+ context '#reset_password_period_valid?' do
24
+ subject { create(:user) }
25
+ before(:each) {
26
+ Authenticate.configuration.reset_password_within = 5.minutes
27
+ }
28
+
29
+ it 'always true if reset_password_within config param is nil' do
30
+ subject.password_reset_sent_at = 10.days.ago
31
+ Authenticate.configuration.reset_password_within = nil
32
+ expect(subject.reset_password_period_valid?).to be_truthy
33
+ end
34
+
35
+ it 'false if time exceeded' do
36
+ subject.password_reset_sent_at = 10.minutes.ago
37
+ expect(subject.reset_password_period_valid?).to be_falsey
38
+ end
39
+
40
+ it 'true if time within limit' do
41
+ subject.password_reset_sent_at = 1.minutes.ago
42
+ expect(subject.reset_password_period_valid?).to be_truthy
43
+ end
44
+ end
45
+
46
+ context '#update_password' do
47
+ subject { create(:user) }
48
+ before(:each) {
49
+ Authenticate.configuration.reset_password_within = 5.minutes
50
+ }
51
+
52
+ context 'within time time' do
53
+ before(:each) {
54
+ subject.password_reset_sent_at = 1.minutes.ago
55
+ }
56
+
57
+ it 'allows password update within time limit' do
58
+ expect(subject.update_password 'chongo').to be_truthy
59
+ end
60
+
61
+ it 'clears password reset token' do
62
+ subject.update_password 'chongo'
63
+ expect(subject.password_reset_token).to be_nil
64
+ end
65
+
66
+ it 'generates a new session token' do
67
+ token = subject.session_token
68
+ subject.update_password 'chongo'
69
+ expect(subject.session_token).to_not eq(token)
70
+ end
71
+
72
+ end
73
+
74
+ it 'stops password update after time limit' do
75
+ subject.password_reset_sent_at = 6.minutes.ago
76
+ expect(subject.update_password 'chongo').to be_falsey
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -81,10 +81,4 @@ describe Authenticate::Session do
81
81
  end
82
82
 
83
83
 
84
- def mock_request
85
- req = double("request")
86
- allow(req).to receive(:params)
87
- allow(req).to receive(:remote_ip).and_return('111.111.111.111')
88
- return req
89
- end
90
84
  end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'authenticate/model/timeoutable'
3
+
4
+
5
+ describe Authenticate::Model::Timeoutable do
6
+ before(:all) {
7
+ Authenticate.configuration.timeout_in = 45.minutes
8
+ }
9
+ subject { create(:user) }
10
+
11
+ it 'does not timeout while last_access_at is valid' do
12
+ subject.last_access_at = 10.minutes.ago
13
+ expect(subject.timedout?).to be_falsey
14
+ end
15
+
16
+ it 'does timeout when last_access_at is stale' do
17
+ subject.last_access_at = 46.minutes.ago
18
+ expect(subject.timedout?).to be_truthy
19
+ end
20
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'authenticate/model/trackable'
3
+
4
+
5
+ describe Authenticate::Model::Trackable do
6
+ subject {create(:user)}
7
+
8
+ context '#last_sign_in_at' do
9
+ it 'sets to old current_sign_in_at if it is not nil' do
10
+ old_sign_in = 2.days.ago.utc
11
+ subject.current_sign_in_at = old_sign_in
12
+ subject.update_tracked_fields mock_request
13
+ expect(subject.last_sign_in_at).to eq(old_sign_in)
14
+ end
15
+
16
+ it 'sets to current time if old current_sign_in_at is nil' do
17
+ subject.current_sign_in_at = nil
18
+ subject.update_tracked_fields mock_request
19
+ expect(subject.last_sign_in_at).to be_within(5.seconds).of(Time.now.utc)
20
+ end
21
+ end
22
+
23
+ context '#last_sign_in_ip' do
24
+ it 'sets to old current_sign_in_ip if it is not nil' do
25
+ old_ip = '222.222.222.222'
26
+ subject.current_sign_in_ip = old_ip
27
+ subject.update_tracked_fields mock_request
28
+ expect(subject.last_sign_in_ip).to eq(old_ip)
29
+ end
30
+
31
+ it 'sets to current ip if old current_sign_in_ip is nil' do
32
+ subject.current_sign_in_ip = nil
33
+ subject.update_tracked_fields mock_request
34
+ expect(subject.last_sign_in_ip).to_not be_nil
35
+ end
36
+ end
37
+
38
+ it 'sets current_sign_in_at to now' do
39
+ subject.current_sign_in_at = nil
40
+ subject.update_tracked_fields mock_request
41
+ expect(subject.current_sign_in_at).to be_within(5.seconds).of(Time.now.utc)
42
+ end
43
+
44
+ context '#sign_in_count' do
45
+ it 'initializes a nil count' do
46
+ subject.sign_in_count = nil
47
+ subject.update_tracked_fields mock_request
48
+ expect(subject.sign_in_count).to eq(1)
49
+ end
50
+ it 'increments existing count' do
51
+ subject.sign_in_count = 4
52
+ subject.update_tracked_fields mock_request
53
+ expect(subject.sign_in_count).to eq(5)
54
+ end
55
+ end
56
+ end
data/spec/spec_helper.rb CHANGED
@@ -53,6 +53,12 @@ def restore_default_configuration
53
53
  Authenticate.configure {}
54
54
  end
55
55
 
56
+ def mock_request(params = {})
57
+ req = double("request")
58
+ allow(req).to receive(:params).and_return(params)
59
+ allow(req).to receive(:remote_ip).and_return('111.111.111.111')
60
+ req
61
+ end
56
62
 
57
63
 
58
64
  # # This file was generated by the `rails generate rspec:install` command. Conventionally, all
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authenticate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Tomich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-03 00:00:00.000000000 Z
11
+ date: 2016-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt
@@ -59,7 +59,7 @@ dependencies:
59
59
  - !ruby/object:Gem::Version
60
60
  version: '5.1'
61
61
  - !ruby/object:Gem::Dependency
62
- name: sqlite3
62
+ name: factory_girl_rails
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - ">="
@@ -87,7 +87,7 @@ dependencies:
87
87
  - !ruby/object:Gem::Version
88
88
  version: '0'
89
89
  - !ruby/object:Gem::Dependency
90
- name: factory_girl_rails
90
+ name: pry
91
91
  requirement: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - ">="
@@ -101,7 +101,7 @@ dependencies:
101
101
  - !ruby/object:Gem::Version
102
102
  version: '0'
103
103
  - !ruby/object:Gem::Dependency
104
- name: pry
104
+ name: sqlite3
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - ">="
@@ -114,7 +114,7 @@ dependencies:
114
114
  - - ">="
115
115
  - !ruby/object:Gem::Version
116
116
  version: '0'
117
- description: Rails authentication with email & password
117
+ description: Authentication for Rails applications
118
118
  email:
119
119
  - justin@tomich.org
120
120
  executables: []
@@ -122,10 +122,12 @@ extensions: []
122
122
  extra_rdoc_files:
123
123
  - LICENSE
124
124
  - README.md
125
+ - CHANGELOG.md
125
126
  files:
126
127
  - ".gitignore"
127
128
  - ".rspec"
128
129
  - ".ruby-version"
130
+ - ".travis.yml"
129
131
  - CHANGELOG.md
130
132
  - Gemfile
131
133
  - Gemfile.lock
@@ -136,6 +138,7 @@ files:
136
138
  - app/assets/images/authenticate/.keep
137
139
  - app/assets/javascripts/authenticate/.keep
138
140
  - app/assets/stylesheets/authenticate/.keep
141
+ - app/controllers/authenticate/authenticate_controller.rb
139
142
  - app/controllers/authenticate/passwords_controller.rb
140
143
  - app/controllers/authenticate/sessions_controller.rb
141
144
  - app/controllers/authenticate/users_controller.rb
@@ -153,6 +156,7 @@ files:
153
156
  - bin/rails
154
157
  - config/locales/authenticate.en.yml
155
158
  - config/routes.rb
159
+ - gemfiles/rails42.gemfile
156
160
  - lib/authenticate.rb
157
161
  - lib/authenticate/callbacks/authenticatable.rb
158
162
  - lib/authenticate/callbacks/brute_force.rb
@@ -249,8 +253,14 @@ files:
249
253
  - spec/dummy/public/favicon.ico
250
254
  - spec/factories/users.rb
251
255
  - spec/model/brute_force_spec.rb
256
+ - spec/model/db_password_spec.rb
257
+ - spec/model/email_spec.rb
258
+ - spec/model/lifetimed_spec.rb
259
+ - spec/model/password_reset_spec.rb
252
260
  - spec/model/session_spec.rb
261
+ - spec/model/timeoutable_spec.rb
253
262
  - spec/model/token_spec.rb
263
+ - spec/model/trackable_spec.rb
254
264
  - spec/model/user_spec.rb
255
265
  - spec/orm/active_record.rb
256
266
  - spec/spec_helper.rb
@@ -278,10 +288,5 @@ rubyforge_project:
278
288
  rubygems_version: 2.5.1
279
289
  signing_key:
280
290
  specification_version: 4
281
- summary: Rails authentication with email & password
282
- test_files:
283
- - spec/configuration_spec.rb
284
- - spec/model/brute_force_spec.rb
285
- - spec/model/session_spec.rb
286
- - spec/model/token_spec.rb
287
- - spec/model/user_spec.rb
291
+ summary: Authentication for Rails applications
292
+ test_files: []