authenticate 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []