authenticate 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2888d491c7df1e767b2bcf58c4581012bb64f56a
4
- data.tar.gz: 30548171155bd56462c7326a70e4275d5dde2336
3
+ metadata.gz: dfcdc32766cdac8613010672395197b51dcbd54b
4
+ data.tar.gz: 32e9e1af1ef1cae8fd69e2aee5306f74837d51b7
5
5
  SHA512:
6
- metadata.gz: bd4d8474083ff62f1f677ef8760f13bdfa9c821c8c7eefcf9461ce691d829cca4af8687fd1d8b51a637b19b752eb25ff7eef2383598ddebf69da7f493f2210c8
7
- data.tar.gz: 95ef6dffaebefe2cf9e366e099812e625faa5eb0de27b6e549ccef7cd082aaaee876ae45828a4ddc9c49a0e3972e29984134f09ed095ac0c296f1357f225f2c4
6
+ metadata.gz: b63724b793d9f8c48d98151a1bd9e1722ccb6b1f5201de424e8998c94405835d143550f312d5778e66894e44317acae0b919d4bb2e1bb811651d22d0cc39a10e
7
+ data.tar.gz: 48ef63f7a896e3a00542908688b3e3dbb15056e6b7f8c3f5ecc4add8fb57abef5d4496919d1c91c36bb45452f5154b3b39b2959e454e4a071b22aa1f71ee5582
data/.travis.yml CHANGED
@@ -7,16 +7,22 @@ rvm:
7
7
  - 2.1.8
8
8
  - 2.2.4
9
9
  - 2.3.3
10
+ - 2.4.1
10
11
 
11
12
  gemfile:
12
13
  - gemfiles/4.2.gemfile
13
14
  - gemfiles/5.0.gemfile
15
+ - gemfiles/5.1.gemfile
14
16
 
15
17
 
16
18
  matrix:
17
19
  exclude:
18
20
  - rvm: 2.1.8
19
21
  gemfile: gemfiles/5.0.gemfile
22
+ - rvm: 2.1.8
23
+ gemfile: gemfiles/5.1.gemfile
24
+ - rvm: 2.4.1
25
+ gemfile: gemfiles/4.2.gemfile
20
26
 
21
27
  install:
22
28
  - "bin/setup"
data/Appraisals CHANGED
@@ -1,10 +1,16 @@
1
- appraise "4.2" do
2
- gem "rails", "~> 4.2.0"
1
+ if RUBY_VERSION < "2.4.0"
2
+ appraise "4.2" do
3
+ gem "rails", "~> 4.2.0"
4
+ end
3
5
  end
4
6
 
5
7
  if RUBY_VERSION >= "2.2.0"
6
8
  appraise "5.0" do
7
9
  gem "rails", "~> 5.0.0"
8
10
  end
11
+
12
+ appraise "5.1" do
13
+ gem "rails", "~> 5.1"
14
+ end
9
15
  end
10
16
 
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Authenticate Changelog
2
2
 
3
+
4
+ ## [0.7.0] - May 25, 2017
5
+
6
+ ### API Changes
7
+ - controller#require_authentication is deprecated, use controller#require_login
8
+ - controller#authenticated? is deprecated, use controller#logged_in?
9
+ - added controller#logged_out?
10
+ `authenticated?` and `required_authentication` will be removed in a future release.
11
+
12
+ ### Test support
13
+ - Added login_as via middleware for feature/integration/system tests.
14
+ - added rspec helpers for view and controller tests
15
+ - added test-unit helpers for controller/view tests
16
+
17
+ ### Internal changes - will not affect normal apps
18
+ - Session#initialize(request, cookies) is now Session#initialize(request)
19
+ - Session API changes, #authenticated? renamed #logged_in?
20
+
21
+ [0.7.0]: https://github.com/tomichj/authenticate/compare/v0.6.1...v0.7.0
22
+
23
+
3
24
  ## [0.6.1] - May 16, 2017
4
25
 
5
26
  ### Fixed
@@ -31,7 +52,7 @@
31
52
 
32
53
 
33
54
 
34
- ## [0.5.0] - March 26, 2017
55
+ ## [0.5.0] - March 26, 2017oh
35
56
 
36
57
  ### Support for rails 5.1.
37
58
 
data/README.md CHANGED
@@ -23,7 +23,6 @@ Please use [GitHub Issues] to report bugs. You can contact me directly on twitte
23
23
  * configuration driven - almost all configuration is performed in the initializer
24
24
 
25
25
 
26
-
27
26
  ## Implementation Overview
28
27
 
29
28
  Authenticate:
@@ -71,6 +70,19 @@ You'll need to run the migrations that Authenticate just generated:
71
70
  rake db:migrate
72
71
  ```
73
72
 
73
+ Finally, you need to secure any controllers that require authentication by adding
74
+ `before_action :require_login`. If your entire app requires authentication, add it to
75
+ `ApplicationController`:
76
+
77
+ ```ruby
78
+ # app/controllers/application_controller.rb
79
+ class ApplicationController < ActionController::Base
80
+ include Authenticate::Controller
81
+ before_action :require_login
82
+ protect_from_forgery with: :exception
83
+ end
84
+ ```
85
+
74
86
 
75
87
  ## Configure
76
88
 
@@ -103,17 +115,16 @@ end
103
115
  Configuration parameters are described in detail here: [Configuration](lib/authenticate/configuration.rb)
104
116
 
105
117
 
106
-
107
118
  ## Use
108
119
 
109
120
  ### Access Control
110
121
 
111
- Use the `require_authentication` filter to control access to controller actions. To control access to
122
+ Use the `require_login` filter to control access to controller actions. To control access to
112
123
  all controller actions, add the filter to your `ApplicationController`, e.g.:
113
124
 
114
125
  ```ruby
115
126
  class ApplicationController < ActionController::Base
116
- before_action :require_authentication
127
+ before_action :require_login
117
128
  end
118
129
  ```
119
130
 
@@ -133,12 +144,12 @@ end
133
144
 
134
145
  ### Helpers
135
146
 
136
- Use `current_user` and `authenticated?` in controllers, views, and helpers.
147
+ Use `current_user`, `logged_in?`, and `logged_out?` in controllers, views, and helpers.
137
148
 
138
149
  Example:
139
150
 
140
151
  ```erb
141
- <% if authenticated? %>
152
+ <% if logged_in? %>
142
153
  <%= current_user.email %>
143
154
  <%= link_to "Sign out", sign_out_path %>
144
155
  <% else %>
@@ -196,6 +207,7 @@ application for modification.
196
207
  To turn off Authenticate's built-in routes:
197
208
 
198
209
  ```ruby
210
+ # config/initializers/authenticate.rb
199
211
  Authenticate.configure do |config|
200
212
  config.routes = false
201
213
  end
@@ -222,12 +234,12 @@ For example, to customize `Authenticate::SessionController`:
222
234
  * subclass the controller:
223
235
 
224
236
  ```ruby
237
+ # app/controllers/sessions_controller.rb
225
238
  class SessionsController < Authenticate::SessionController
226
239
  # render sign in screen
227
240
  def new
228
241
  # ...
229
242
  end
230
- ...
231
243
  end
232
244
  ```
233
245
 
@@ -239,10 +251,11 @@ Start by dumping a copy of authenticate routes to your `config/routes.rb`:
239
251
  $ rails generate authenticate:routes
240
252
  ```
241
253
 
242
- Now update `config/routes.rb` to point to your new controller:
254
+ Now update your routes to point to your new controller:
255
+
243
256
  ```ruby
257
+ # config/routes.rb
244
258
  resource :sessions, controller: 'sessions', only: [:create, :new, :destroy]
245
- ...
246
259
  ```
247
260
 
248
261
  You can also use the Authenticate controller generator to copy the default controllers and mailer into
@@ -290,21 +303,24 @@ All flash messages and email lines are stored in i18n translations. You can over
290
303
  See [config/locales/authenticate.en.yml](/config/locales/authenticate.en.yml) for the default messages.
291
304
 
292
305
 
293
-
294
306
  ## Extending Authenticate
295
307
 
296
- Authenticate can be further extended with two mechanisms:
308
+ Authenticate can be extended via two mechanisms:
297
309
 
298
310
  * user modules: add behavior to the user model
299
- * callbacks: add behavior during various authentication events, such as login and subsequent hits
311
+ * callbacks: add rules or behavior during various authentication events, such as login and subsequent hits
312
+
313
+ Most of authenticate's behavior is implemented with a user module and a corresponding callback. User modules add
314
+ behavior to the user, and the callback uses the user model data to decide an authentication attempt is valid or
315
+ invalid.
300
316
 
301
317
 
302
318
  ### User Modules
303
319
 
304
- Add behavior to your User model for your callbacks to use. You can, of course, incldue behavrio yourself directly
320
+ Add behavior to your User model for your callbacks to use. You can include behavior yourself directly
305
321
  in your User class, but you can also use the Authenticate module loading system.
306
322
 
307
- To add a custom module to Authenticate, e.g. `MyUserModule`:
323
+ To add a custom module for Authenticate to load into your User model, e.g. `MyUserModule`:
308
324
 
309
325
  ```ruby
310
326
  Authenticate.configuration do |config|
@@ -315,14 +331,23 @@ end
315
331
 
316
332
  ### Callbacks
317
333
 
318
- Callbacks can be added to Authenticate. Use `Authenticate.lifecycle.after_set_user` or
319
- `Authenticate.lifecycle.after_authentication`. See [Lifecycle](lib/authenticate/lifecycle.rb) for full details.
334
+ Callbacks can be added to Authenticate. Callbacks available at these points of the authenticate lifecycle:
335
+
336
+ - `Authenticate.lifecycle.after_set_user`
337
+ Runs with every hit requiring authentication. This includes both the initial authentication process and
338
+ subsequent to any controller secured by Authenticate. These callbacks run immediately after the User is determined.
339
+
340
+ - `Authenticate.lifecycle.after_authentication`
341
+ These callbacks run only during the initial authentication process.
320
342
 
321
- Callbacks can `throw(:failure, message)` to signal an authentication/authorization failure. Callbacks can also perform
322
- actions on the user or session. Callbacks are passed a block at runtime of `|user, session, options|`.
343
+ See [Lifecycle](lib/authenticate/lifecycle.rb) for full details.
323
344
 
324
- Here's an example that counts logins for users. It consists of a module for User, and a callback that is
325
- set in the `included` block. The callback is then added to the User module via the Authenticate configuration.
345
+ Callbacks must `throw(:failure, message)` to signal an authentication/authorization failure. Callbacks can also perform
346
+ other actions on the user or session. Callbacks are invoked with `|user, session, options|`.
347
+
348
+ Here's a simple example that counts logins for users. It consists of a module for User implemented as an
349
+ `ActiveSupport::Concern`, with a callback that is defined in an `included` block. The module and callback
350
+ is added to the User module via the Authenticate configuration.
326
351
 
327
352
  ```ruby
328
353
  # app/models/concerns/login_count.rb
@@ -348,15 +373,150 @@ Authenticate.configuration do |config|
348
373
  end
349
374
  ```
350
375
 
376
+ More complex callbacks and modules can be implemented in a separate file(s); in that case,
377
+ the user module should `require` the callback file to inject it into Authenticate's callback lifecycle.
378
+
351
379
 
352
380
  ## Testing
353
381
 
354
- Authenticate has been tested with rails 4.2, other versions to follow.
382
+ ### Feature/Integration/System Tests
383
+
384
+ Authenticate includes middleware which allows tests to directly sign a test user in,
385
+ eliminating the need to visit and submit the sign on form. This can significantly speeds up tests.
386
+ Used by integration, system, feature, etc tests.
387
+
388
+ Configure your test environment to enable the middleware:
389
+ ```ruby
390
+ # config/environments/test.rb
391
+ MyRailsApp::Application.configure do
392
+ # ...
393
+ config.middleware.use Authenticate::Testing::IntegrationTestsSignOn
394
+ # ...
395
+ end
396
+ ```
397
+
398
+ Sign a test user in by passing as=USER_ID in a query parameter:
399
+ ```ruby
400
+ visit root_path(as: user)
401
+ ```
402
+
403
+ A feature spec using factory_girl and capybara with the integration sign on middleware might look like this:
404
+ ```ruby
405
+ require 'spec_helper'
406
+
407
+ feature 'dashboard' do
408
+ scenario 'logged in user has name on dashboard' do
409
+ user = create(:user)
410
+ visit dashboard_path(as: user)
411
+ expect(page).to have_content user.name
412
+ end
413
+ end
414
+ ```
415
+
416
+
417
+ ### Controller Tests
418
+
419
+ To test controller actions protected by authenticate with `before_action :require_login`, you can
420
+ use Authenticate's test helpers.
421
+
422
+ For `rspec`, add the following to your `spec/spec_helper.rb` or `spec/rails_helper.rb`:
423
+
424
+ ```ruby
425
+ require 'authenticate/testing/rspec'
426
+ ```
427
+
428
+ For `test-unit`, add the following to your `test/test_helper.rb`.
429
+
430
+ ```ruby
431
+ require 'authenticate/testing/test_unit'
432
+ ```
433
+
434
+ This will give you helper methods:
435
+
436
+ ```ruby
437
+ login_as(user)
438
+ logout
439
+ ```
440
+
441
+ Once you `login_as(user)`, you will satisfy the `require_login` filter. The other `Authenticate::Controller`
442
+ methods will then work: `current_user`, `logged_in?`, `logged_out?`
443
+
444
+ A controller spec using `factory_girl` and authenticate's controller helpers might look like this:
445
+ ```ruby
446
+ require 'spec_helper'
447
+ describe DashboardsController do
448
+ describe '#show' do
449
+ it 'shows view' do
450
+ user = create(:user)
451
+ login_as(user)
452
+ get :show
453
+ expect(response).to be_success
454
+ expect(response).to render_template 'dashboards/show'
455
+ end
456
+ end
457
+ end
458
+ ```
459
+
460
+ Rails 5 built-in test suite's controller tests now extend `ActionDispatch::IntegrationTest`. Use the middleware
461
+ `IntegrationTestsSignOn` to support sign on. For example:
462
+ ```ruby
463
+ require 'test_helper'
464
+ class DashboardsControllerTest < ActionDispatch::IntegrationTest
465
+ test 'logged in user can GET a dashboard' do
466
+ user = create(:user)
467
+ get dashboards_show_path(as: user)
468
+ assert_response :success
469
+ end
470
+ end
471
+ ```
472
+
473
+
474
+ ### View Tests
475
+
476
+ For `rspec`, require `authenticate/testing/rspec` to include view helpers:
477
+
478
+ ```ruby
479
+ login_as(user)
480
+ current_user
481
+ logged_in?
482
+ logged_out?
483
+ ```
484
+
485
+ Once you `login_as(user)`, your view can make use of the other helpers as you'd expect.
486
+
487
+ An example view spec using `factory_girl` and authenticate's view helpers:
488
+ ```ruby
489
+ require 'spec_helper'
490
+ describe 'dashboards/show', type: :view do
491
+ it 'displays user name' do
492
+ user = create(:user)
493
+ login_as(user)
494
+ render
495
+ expect(rendered).to match user.name # view uses `current_user`
496
+ end
497
+ end
498
+ ```
499
+
500
+
501
+ ## Additional Documentation
502
+
503
+ Consult the [Authenticate wiki](https://github.com/tomichj/authenticate/wiki/) for additional documentation.
504
+
505
+
506
+ ## Versions of Rails Supported
507
+
508
+ Authenticate is tested with rails 4.2, 5.0, and 5.1.
509
+
510
+
511
+ ## Changelog
355
512
 
513
+ For a summary of changes by version, see the [CHANGELOG.md](/CHANGELOG.md).
356
514
 
357
515
 
358
516
  ## License
359
517
 
360
- This project rocks and uses MIT-LICENSE.
518
+ Authenticate is copyright © 2015 Justin Tomich. It is free software, and may be
519
+ redistributed under the terms specified in the [`LICENSE`] file.
361
520
 
521
+ [`LICENSE`]: /LICENSE
362
522
 
@@ -1,6 +1,7 @@
1
1
  # Request password change via an emailed link with a unique token.
2
2
  # Thanks to devise and Clearance.
3
3
  class Authenticate::PasswordsController < Authenticate::AuthenticateController
4
+ skip_before_action :require_login, only: [:create, :edit, :new, :update], raise: false
4
5
  skip_before_action :require_authentication, only: [:create, :edit, :new, :update], raise: false
5
6
  before_action :ensure_existing_user, only: [:edit, :update]
6
7
 
@@ -3,12 +3,19 @@
3
3
  #
4
4
  class Authenticate::SessionsController < Authenticate::AuthenticateController
5
5
  before_action :redirect_signed_in_users, only: [:new]
6
+ skip_before_action :require_login, only: [:create, :new, :destroy], raise: false
6
7
  skip_before_action :require_authentication, only: [:create, :new, :destroy], raise: false
7
8
 
9
+ #
10
+ # Render the login screen.
11
+ #
8
12
  def new
9
13
  render template: 'sessions/new'
10
14
  end
11
15
 
16
+ #
17
+ # Log the user in, with the provided name and password.
18
+ #
12
19
  def create
13
20
  @user = authenticate(params)
14
21
  login(@user) do |status|
@@ -21,6 +28,9 @@ class Authenticate::SessionsController < Authenticate::AuthenticateController
21
28
  end
22
29
  end
23
30
 
31
+ #
32
+ # Log the user out
33
+ #
24
34
  def destroy
25
35
  logout
26
36
  redirect_to url_after_destroy
@@ -29,7 +39,7 @@ class Authenticate::SessionsController < Authenticate::AuthenticateController
29
39
  private
30
40
 
31
41
  def redirect_signed_in_users
32
- redirect_to url_for_signed_in_users if authenticated?
42
+ redirect_to url_for_signed_in_users if logged_in?
33
43
  end
34
44
 
35
45
  def url_after_create
@@ -3,6 +3,7 @@
3
3
  #
4
4
  class Authenticate::UsersController < Authenticate::AuthenticateController
5
5
  before_action :redirect_signed_in_users, only: [:create, :new]
6
+ skip_before_action :require_login, only: [:create, :new], raise: false
6
7
  skip_before_action :require_authentication, only: [:create, :new], raise: false
7
8
 
8
9
  def new
@@ -24,7 +25,7 @@ class Authenticate::UsersController < Authenticate::AuthenticateController
24
25
  private
25
26
 
26
27
  def redirect_signed_in_users
27
- redirect_to Authenticate.configuration.redirect_url if authenticated?
28
+ redirect_to Authenticate.configuration.redirect_url if logged_in?
28
29
  end
29
30
 
30
31
  def url_after_create
@@ -6,7 +6,7 @@
6
6
  </head>
7
7
  <body>
8
8
  <div id="header">
9
- <% if authenticated? -%>
9
+ <% if logged_in? -%>
10
10
  <%= link_to t(".sign_out"), sign_out_path %>
11
11
  <% else -%>
12
12
  <%= link_to t(".sign_in"), sign_in_path %>
data/authenticate.gemspec CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
29
29
  s.add_development_dependency 'rspec-rails', '~> 3.6'
30
30
  s.add_development_dependency 'pry', '~> 0.10'
31
31
  s.add_development_dependency 'sqlite3', '~> 1.3'
32
- s.add_development_dependency 'shoulda-matchers', '~> 2.8'
32
+ # s.add_development_dependency 'shoulda-matchers', '~> 2.8'
33
33
  s.add_development_dependency 'capybara', '~> 2.14'
34
34
  s.add_development_dependency 'database_cleaner', '~> 1.5'
35
35
  s.add_development_dependency 'timecop', '~> 0.8'
data/gemfiles/5.0.gemfile CHANGED
@@ -3,6 +3,5 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~> 5.0.0"
6
- gem "rails-controller-testing"
7
6
 
8
7
  gemspec path: "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 5.1"
6
+
7
+ gemspec path: "../"
data/lib/authenticate.rb CHANGED
@@ -6,6 +6,7 @@ require 'authenticate/session'
6
6
  require 'authenticate/user'
7
7
  require 'authenticate/engine'
8
8
  require 'authenticate/modules'
9
+ require 'authenticate/testing/integration_tests_sign_on'
9
10
 
10
11
  # Top level module of authenticate gem.
11
12
  module Authenticate
@@ -2,5 +2,5 @@
2
2
  #
3
3
  # If user failed to authenticate, toss them out.
4
4
  Authenticate.lifecycle.after_authentication name: 'authenticatable' do |_user, session, _opts|
5
- throw(:failure, I18n.t('callbacks.authenticatable.failure')) unless session && session.authenticated?
5
+ throw(:failure, I18n.t('callbacks.authenticatable.failure')) unless session && session.logged_in?
6
6
  end
@@ -2,7 +2,7 @@
2
2
  # Runs as a hook after authentication.
3
3
  Authenticate.lifecycle.prepend_after_authentication name: 'brute force protection' do |user, session, _options|
4
4
  include ActionView::Helpers::DateHelper
5
- unless session.authenticated? || Authenticate.configuration.max_consecutive_bad_logins_allowed.nil?
5
+ unless session.logged_in? || Authenticate.configuration.max_consecutive_bad_logins_allowed.nil?
6
6
  user_credentials = User.credentials(session.request.params)
7
7
  user ||= User.find_by_credentials(user_credentials)
8
8
  if user && user.respond_to?(:register_failed_login!)
@@ -6,7 +6,7 @@ module Authenticate
6
6
  #
7
7
  # class ApplicationController < ActionController::Base
8
8
  # include Authenticate::Controller
9
- # before_action :require_authentication
9
+ # before_action :require_login
10
10
  # protect_from_forgery with: :exception
11
11
  # end
12
12
  #
@@ -19,15 +19,16 @@ module Authenticate
19
19
  # * require_authentication - restrict access to authenticated users, often from ApplicationController
20
20
  #
21
21
  # Helpers, used anywhere:
22
- # * current_user - get the current user from the current Authenticate session.
23
- # * authenticated? - has the user been logged in?
22
+ # * current_user - get the currently logged in user
23
+ # * logged_in? - is the user logged in?
24
+ # * logged_out? - is the user not logged in?
24
25
  #
25
26
  module Controller
26
27
  extend ActiveSupport::Concern
27
28
  include Debug
28
29
 
29
30
  included do
30
- helper_method :current_user, :authenticated?
31
+ helper_method :current_user, :logged_in?, :logged_out?, :authenticated?
31
32
  attr_writer :authenticate_session
32
33
  end
33
34
 
@@ -43,7 +44,7 @@ module Authenticate
43
44
  def login(user, &block)
44
45
  authenticate_session.login user, &block
45
46
 
46
- if authenticated? && Authenticate.configuration.rotate_csrf_on_sign_in?
47
+ if logged_in? && Authenticate.configuration.rotate_csrf_on_sign_in?
47
48
  session.delete(:_csrf_token)
48
49
  form_authenticity_token
49
50
  end
@@ -59,25 +60,27 @@ module Authenticate
59
60
  # redirect_to '/', notice: 'You logged out successfully'
60
61
  # end
61
62
  def logout
62
- authenticate_session.deauthenticate
63
+ authenticate_session.logout
63
64
  end
64
65
 
65
- # Use this filter as a before_action to restrict controller actions to authenticated users.
66
- # Consider using in application_controller to restrict access to all controllers.
66
+ # Use this filter as a before_action to control access to controller actions,
67
+ # limiting to logged in users.
68
+ #
69
+ # Placing in application_controller will control access to all controllers.
67
70
  #
68
71
  # Example:
69
72
  #
70
73
  # class ApplicationController < ActionController::Base
71
- # before_action :require_authentication
74
+ # before_action :require_login
72
75
  #
73
76
  # def index
74
77
  # # ...
75
78
  # end
76
79
  # end
77
80
  #
78
- def require_authentication
79
- debug 'Controller::require_authentication'
80
- unauthorized unless authenticated?
81
+ def require_login
82
+ debug "!!!!!!!!!!!!!!!!!! controller#require_login " # logged_in? #{logged_in?}"
83
+ unauthorized unless logged_in?
81
84
  message = catch(:failure) do
82
85
  current_user = authenticate_session.current_user
83
86
  Authenticate.lifecycle.run_callbacks(:after_set_user, current_user, authenticate_session, event: :set_user)
@@ -87,14 +90,25 @@ module Authenticate
87
90
 
88
91
  # Has the user been logged in? Exposed as a helper, can be called from views.
89
92
  #
90
- # <% if authenticated? %>
91
- # <%= link_to logout_path, "Sign out" %>
93
+ # <% if logged_in? %>
94
+ # <%= link_to sign_out_path, "Sign out" %>
92
95
  # <% else %>
93
- # <%= link_to login_path, "Sign in" %>
96
+ # <%= link_to sign_in_path, "Sign in" %>
94
97
  # <% end %>
95
98
  #
96
- def authenticated?
97
- authenticate_session.authenticated?
99
+ def logged_in?
100
+ debug "!!!!!!!!!!!!!!!!!! controller#logged_in?"
101
+ authenticate_session.logged_in?
102
+ end
103
+
104
+ # Has the user not logged in? Exposed as a helper, can be called from views.
105
+ #
106
+ # <% if logged_out? %>
107
+ # <%= link_to sign_in_path, "Sign in" %>
108
+ # <% end %>
109
+ #
110
+ def logged_out?
111
+ !logged_in?
98
112
  end
99
113
 
100
114
  # Get the current user from the current Authenticate session.
@@ -115,11 +129,29 @@ module Authenticate
115
129
  is_a?(Authenticate::AuthenticateController)
116
130
  end
117
131
 
132
+ # The old API.
133
+ #
134
+ # todo: remove in a future version.
135
+ def require_authentication
136
+ warn "#{Kernel.caller.first}: [DEPRECATION] " +
137
+ "'require_authentication' is deprecated and will be removed in a future release. use 'require_login' instead"
138
+ require_login
139
+ end
140
+
141
+ # The old API.
142
+ #
143
+ # todo: remove in a future version.
144
+ def authenticated?
145
+ warn "#{Kernel.caller.first}: [DEPRECATION] " +
146
+ "'authenticated?' is deprecated and will be removed in a future release. Use 'logged_in?' instead."
147
+ logged_in?
148
+ end
149
+
118
150
  protected
119
151
 
120
152
  # User is not authorized, bounce 'em to sign in
121
153
  def unauthorized(msg = t('flashes.failure_when_not_signed_in'))
122
- authenticate_session.deauthenticate
154
+ authenticate_session.logout
123
155
  respond_to do |format|
124
156
  format.any(:js, :json, :xml) { head :unauthorized }
125
157
  format.any { redirect_unauthorized(msg) }
@@ -127,13 +159,13 @@ module Authenticate
127
159
  end
128
160
 
129
161
  def redirect_unauthorized(flash_message)
130
- store_location
162
+ store_location!
131
163
 
132
164
  if flash_message
133
165
  flash[:notice] = flash_message # TODO: use locales
134
166
  end
135
167
 
136
- if authenticated?
168
+ if logged_in?
137
169
  redirect_to url_after_denied_access_when_signed_in
138
170
  else
139
171
  redirect_to url_after_denied_access_when_signed_out
@@ -164,7 +196,7 @@ module Authenticate
164
196
  private
165
197
 
166
198
  # Write location to return to in user's session (normally a cookie).
167
- def store_location
199
+ def store_location!
168
200
  if request.get?
169
201
  session[:authenticate_return_to] = request.original_fullpath
170
202
  end
@@ -179,7 +211,7 @@ module Authenticate
179
211
  end
180
212
 
181
213
  def authenticate_session
182
- @authenticate_session ||= Authenticate::Session.new(request, cookies)
214
+ @authenticate_session ||= Authenticate::Session.new(request)
183
215
  end
184
216
  end
185
217
  end
@@ -8,18 +8,31 @@ module Authenticate
8
8
 
9
9
  attr_accessor :request
10
10
 
11
- def initialize(request, cookies)
11
+ # Initialize an Authenticate session.
12
+ #
13
+ # The presence of a session does NOT mean the user is logged in; call #logged_in? to determine login status.
14
+ def initialize(request)
12
15
  @request = request # trackable module accesses request
13
- @cookies = cookies
16
+ @cookies = request.cookie_jar
14
17
  @session_token = @cookies[cookie_name]
15
18
  debug 'SESSION initialize: @session_token: ' + @session_token.inspect
16
19
  end
17
20
 
18
21
  # Finish user login process, *after* the user has been authenticated.
22
+ #
19
23
  # Called when user creates an account or signs back into the app.
20
- # Runs all callbacks checking for any login failure. If a login failure
21
- # occurs, user is NOT logged in.
24
+ # Runs all configured callbacks, checking for login failure.
25
+ #
26
+ # If login is successful, @current_user is set and a session token is generated
27
+ # and returned to the client browser.
28
+ # If login fails, the user is NOT logged in. No session token is set,
29
+ # and @current_user will not be set.
30
+ #
31
+ # After callbacks are finished, a {LoginStatus} is yielded to the provided block,
32
+ # if one is provided.
22
33
  #
34
+ # @param [User] user login completed for this user
35
+ # @yieldparam [Success,Failure] status result of the sign in operation.
23
36
  # @return [User]
24
37
  def login(user)
25
38
  @current_user = user
@@ -45,23 +58,23 @@ module Authenticate
45
58
  #
46
59
  # @return [User]
47
60
  def current_user
48
- debug 'session.current_user'
61
+ debug "session.current_user #{@current_user.inspect}"
49
62
  @current_user ||= load_user_from_session_token if @session_token.present?
50
63
  @current_user
51
64
  end
52
65
 
53
- # Has this session successfully authenticated?
66
+ # Has this user successfully logged in?
54
67
  #
55
68
  # @return [Boolean]
56
- def authenticated?
57
- debug 'session.authenticated?'
69
+ def logged_in?
70
+ debug "session.logged_in? #{current_user.present?}"
58
71
  current_user.present?
59
72
  end
60
73
 
61
74
  # Invalidate the session token, unset the current user and remove the cookie.
62
75
  #
63
76
  # @return [void]
64
- def deauthenticate
77
+ def logout
65
78
  # nuke session_token in db
66
79
  current_user.reset_session_token! if current_user.present?
67
80
 
@@ -72,12 +85,6 @@ module Authenticate
72
85
  @cookies.delete cookie_name
73
86
  end
74
87
 
75
- protected
76
-
77
- def user_loaded?
78
- !@current_user.present?
79
- end
80
-
81
88
  private
82
89
 
83
90
  def write_cookie
@@ -0,0 +1,28 @@
1
+ module Authenticate
2
+ module Testing
3
+
4
+ # Helpers for controller tests/specs.
5
+ #
6
+ # Example:
7
+ #
8
+ # describe DashboardsController do
9
+ # describe '#show' do
10
+ # it 'shows view' do
11
+ # user = create(:user)
12
+ # login_as(user)
13
+ # get :show
14
+ # expect(response).to be_success
15
+ # end
16
+ # end
17
+ # end
18
+ module ControllerHelpers
19
+ def login_as(user)
20
+ controller.login(user)
21
+ end
22
+
23
+ def logout
24
+ controller.logout
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,72 @@
1
+ module Authenticate
2
+ module Testing
3
+
4
+ # Middleware which allows tests to bypass your sign on screen.
5
+ # Typically used by integration and feature tests, etc.
6
+ # Speeds up these tests by eliminating the need to visit and
7
+ # submit the signon form repeatedly.
8
+ #
9
+ # Sign a test user in by passing as=USER_ID in a query parameter.
10
+ # If `User#to_param` is overridden you may pass a block to override
11
+ # the default user lookup behaviour.
12
+ #
13
+ # Configure your application's test environment as follows:
14
+ #
15
+ # # config/environments/test.rb
16
+ # MyRailsApp::Application.configure do
17
+ # # ...
18
+ # config.middleware.use Authenticate::IntegrationTestsSignOn
19
+ # # ...
20
+ # end
21
+ #
22
+ # or if `User#to_param` is overridden (to `username` for example):
23
+ #
24
+ # # config/environments/test.rb
25
+ # MyRailsApp::Application.configure do
26
+ # # ...
27
+ # config.middleware.use Authenticate::IntegrationTestsSignOn do |username|
28
+ # User.find_by(username: username)
29
+ # end
30
+ # # ...
31
+ # end
32
+ #
33
+ # After configuring your app, usage in an integration tests is simple:
34
+ #
35
+ # user = ... # load user
36
+ # visit dashboard_path(as: user)
37
+ #
38
+ class IntegrationTestsSignOn
39
+ def initialize(app, &block)
40
+ @app = app
41
+ @block = block
42
+ end
43
+
44
+ def call(env)
45
+ do_login(env)
46
+ @app.call(env)
47
+ end
48
+
49
+ private
50
+
51
+ def do_login(env)
52
+ params = Rack::Utils.parse_query(env['QUERY_STRING'])
53
+ user_param = params['as']
54
+
55
+ user = find_user(user_param) if user_param.present?
56
+ if user.present?
57
+ user.generate_session_token && user.save if user.session_token.nil?
58
+ request = Rack::Request.new(env)
59
+ request.cookies[Authenticate.configuration.cookie_name] = user.session_token
60
+ end
61
+ end
62
+
63
+ def find_user(user_param)
64
+ if @block
65
+ @block.call(user_param)
66
+ else
67
+ Authenticate.configuration.user_model_class.find(user_param)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,10 @@
1
+ require 'authenticate/testing/controller_helpers'
2
+ require 'authenticate/testing/view_helpers'
3
+
4
+ RSpec.configure do |config|
5
+ config.include Authenticate::Testing::ControllerHelpers, type: :controller
6
+ config.include Authenticate::Testing::ViewHelpers, type: :view
7
+ config.before(:each, type: :view) do
8
+ view.extend Authenticate::Testing::ViewHelpers::CurrentUser
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ require 'authenticate/testing/controller_helpers'
2
+
3
+ # Support for test unit.
4
+ #
5
+ # As of Rails 5, controller tests subclass `ActionDispatch::IntegrationTest` and should use
6
+ # IntegrationTestsSignOn to bypass the sign on screen.
7
+ class ActionController::TestCase
8
+ include Authenticate::Testing::ControllerHelpers
9
+ end
@@ -0,0 +1,29 @@
1
+ module Authenticate
2
+ module Testing
3
+
4
+ # Helpers for view tests/specs.
5
+ #
6
+ # Use login_as to log in a user for your test case, which allows
7
+ # `current_user`, `logged_in?` and `logged_out?` to work properly in your test.
8
+ module ViewHelpers
9
+
10
+ # Set the current_user on the view being tested.
11
+ def login_as(user)
12
+ view.current_user = user
13
+ end
14
+
15
+ module CurrentUser
16
+ attr_accessor :current_user
17
+
18
+ def logged_in?
19
+ current_user.present?
20
+ end
21
+
22
+ def logged_out?
23
+ !logged_in?
24
+ end
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,3 @@
1
1
  module Authenticate
2
- VERSION = '0.6.1'.freeze
2
+ VERSION = '0.7.0'.freeze
3
3
  end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+ require 'support/controllers/controller_helpers'
3
+
4
+ # Matcher that asserts user was denied access.
5
+ RSpec::Matchers.define :deny_access do
6
+ match do |controller|
7
+ redirects_to_sign_in?(controller) && sets_flash?(controller)
8
+ end
9
+
10
+ def redirects_to_sign_in?(controller)
11
+ expect(controller).to redirect_to(controller.sign_in_url)
12
+ end
13
+
14
+ def sets_flash?(controller)
15
+ controller.flash[:notice].match(/sign in to continue/)
16
+ end
17
+ end
18
+
19
+ # A dummy 'secured' controller to test
20
+ class DeprecatedMethodsController < ActionController::Base
21
+ include Authenticate::Controller
22
+ before_action :require_authentication, only: :show
23
+
24
+ def new
25
+ head :ok
26
+ end
27
+
28
+ def show
29
+ head :ok
30
+ end
31
+ end
32
+
33
+ describe DeprecatedMethodsController, type: :controller do
34
+ before do
35
+ Rails.application.routes.draw do
36
+ resource :deprecated_methods, only: [:new, :show]
37
+ get '/sign_in' => 'authenticate/sessions#new', as: 'sign_in'
38
+ end
39
+ end
40
+
41
+ after do
42
+ Rails.application.reload_routes!
43
+ end
44
+
45
+ context 'with authenticated user' do
46
+ before { sign_in }
47
+
48
+ it 'warns but allows access to show' do
49
+ expect { do_get :show }.to output(/deprecated/i).to_stderr
50
+ expect(subject).to_not deny_access
51
+ end
52
+
53
+ it 'warns on authenticated?' do
54
+ expect { subject.authenticated? }.to output(/deprecated/i).to_stderr
55
+ expect(subject.authenticated?).to be_truthy
56
+ end
57
+ end
58
+ end
@@ -19,7 +19,7 @@ end
19
19
  # A dummy 'secured' controller to test
20
20
  class SecuredAppsController < ActionController::Base
21
21
  include Authenticate::Controller
22
- before_action :require_authentication, only: :show
22
+ before_action :require_login, only: :show
23
23
 
24
24
  def new
25
25
  head :ok
@@ -1,6 +1,7 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  include Authenticate::Controller
3
- before_action :require_authentication
3
+ # before_action :require_authentication
4
+ before_action :require_login
4
5
 
5
6
  # Prevent CSRF attacks by raising an exception.
6
7
  # For APIs, you may want to use :null_session instead.
@@ -9,7 +9,7 @@
9
9
  <body>
10
10
 
11
11
  <div id="header">
12
- <% if authenticated? -%>
12
+ <% if logged_in? -%>
13
13
  <%= link_to t(".sign_out"), sign_out_path %>
14
14
  <% else -%>
15
15
  <%= link_to t(".sign_in"), sign_in_path %>
@@ -5,4 +5,5 @@ Authenticate.configure do |config|
5
5
  config.bad_login_lockout_period = 10.minutes
6
6
  config.reset_password_within = 5.minutes
7
7
  config.password_length = 8..128
8
+ config.debug = true
8
9
  end
@@ -4,21 +4,19 @@ describe Authenticate::Session do
4
4
  describe 'session token' do
5
5
  it 'finds a user from session token' do
6
6
  user = create(:user, :with_session_token)
7
- request = mock_request
8
- cookies = cookies_for user
9
- session = Authenticate::Session.new(request, cookies)
7
+ request = mock_request cookies: session_cookie_for(user)
8
+ session = Authenticate::Session.new(request)
10
9
  expect(session.current_user).to eq user
11
10
  end
12
11
  it 'nil user without a session token' do
13
12
  request = mock_request
14
13
  cookies = {}
15
- session = Authenticate::Session.new(request, cookies)
14
+ session = Authenticate::Session.new(request)
16
15
  expect(session.current_user).to be_nil
17
16
  end
18
17
  it 'returns nil with a bogus session token' do
19
- request = mock_request
20
- cookies = { Authenticate.configuration.cookie_name.freeze.to_sym => 'some made up value' }
21
- session = Authenticate::Session.new(request, cookies)
18
+ request = mock_request cookies: { Authenticate.configuration.cookie_name.freeze.to_sym => 'some made up value' }
19
+ session = Authenticate::Session.new(request)
22
20
  expect(session.current_user).to be_nil
23
21
  end
24
22
  end
@@ -26,20 +24,20 @@ describe Authenticate::Session do
26
24
  describe '#login' do
27
25
  it 'sets current_user' do
28
26
  user = create(:user)
29
- session = Authenticate::Session.new(mock_request, {})
27
+ session = Authenticate::Session.new(mock_request)
30
28
  session.login(user)
31
29
  expect(session.current_user).to eq user
32
30
  end
33
31
  context 'with a block' do
34
32
  it 'passes the success status to the block when login succeeds' do
35
33
  user = create(:user)
36
- session = Authenticate::Session.new(mock_request, {})
34
+ session = Authenticate::Session.new(mock_request)
37
35
  session.login(user) do |status|
38
36
  expect(status.success?).to eq true
39
37
  end
40
38
  end
41
39
  it 'passes the failure status to the block when login fails' do
42
- session = Authenticate::Session.new(mock_request, {})
40
+ session = Authenticate::Session.new(mock_request)
43
41
  session.login nil do |status|
44
42
  expect(status.success?).to eq false
45
43
  end
@@ -47,7 +45,7 @@ describe Authenticate::Session do
47
45
  end
48
46
  context 'with nil argument' do
49
47
  it 'assigned current_user to nil' do
50
- session = Authenticate::Session.new(mock_request, {})
48
+ session = Authenticate::Session.new(mock_request)
51
49
  session.login nil
52
50
  expect(session.current_user).to be_nil
53
51
  end
@@ -55,13 +53,12 @@ describe Authenticate::Session do
55
53
  context 'modules' do
56
54
  it 'runs the callbacks' do
57
55
  user = create(:user, :with_session_token, sign_in_count: 0)
58
- cookies = { authenticate_session_token: user.session_token }
59
- session = Authenticate::Session.new(mock_request, cookies)
56
+ request = mock_request cookies: { authenticate_session_token: user.session_token }
57
+ session = Authenticate::Session.new(request)
60
58
  expect { session.login(user) }. to change { user.sign_in_count }.by(1)
61
59
  end
62
60
  it 'fails login if a callback fails' do
63
- cookies = {}
64
- session = Authenticate::Session.new(mock_request, cookies)
61
+ session = Authenticate::Session.new(mock_request)
65
62
  session.login nil do |status|
66
63
  expect(status.success?).to eq false
67
64
  expect(status.message).to eq I18n.t('callbacks.authenticatable.failure')
@@ -70,7 +67,3 @@ describe Authenticate::Session do
70
67
  end
71
68
  end
72
69
  end
73
-
74
- def cookies_for(user)
75
- { Authenticate.configuration.cookie_name.freeze.to_sym => user.session_token }
76
- end
data/spec/spec_helper.rb CHANGED
@@ -9,7 +9,7 @@ if ActiveRecord::VERSION::STRING >= '5.0'
9
9
  end
10
10
 
11
11
  require 'rspec/rails'
12
- require 'shoulda-matchers'
12
+ # require 'shoulda-matchers'
13
13
  require 'capybara/rails'
14
14
  require 'capybara/rspec'
15
15
  require 'database_cleaner'
@@ -23,7 +23,7 @@ DatabaseCleaner.strategy = :truncation
23
23
  # Load factory girl factories.
24
24
  Dir[File.join(File.dirname(__FILE__), 'factories/**/*.rb')].each { |f| require f }
25
25
 
26
- # Build test database in spec/dummy/db/
26
+ # Build test database in spec/dummy/db. There's probably a better way to do this.
27
27
  if defined?(ActiveRecord::Migration.maintain_test_schema!)
28
28
  ActiveRecord::Migration.maintain_test_schema! # rails 4.1+
29
29
  else
@@ -34,8 +34,6 @@ if ActiveRecord::VERSION::STRING >= '4.2' && ActiveRecord::VERSION::STRING < '5.
34
34
  ActiveRecord::Base.raise_in_transactional_callbacks = true
35
35
  end
36
36
 
37
- puts 'MAJOR:' + Rails::VERSION::MAJOR.to_s
38
-
39
37
  RSpec.configure do |config|
40
38
  config.include FactoryGirl::Syntax::Methods
41
39
  config.infer_spec_type_from_file_location!
@@ -57,13 +55,22 @@ RSpec.configure do |config|
57
55
  end
58
56
  end
59
57
 
60
- def mock_request(params = {})
58
+ #
59
+ # todo - enhance test helpers, put in main project
60
+ #
61
+ def mock_request(params: {}, cookies: {})
61
62
  req = double('request')
62
63
  allow(req).to receive(:params).and_return(params)
63
64
  allow(req).to receive(:remote_ip).and_return('111.111.111.111')
65
+ allow(req).to receive(:cookie_jar).and_return(cookies)
64
66
  req
65
67
  end
66
68
 
69
+ def session_cookie_for(user)
70
+ { Authenticate.configuration.cookie_name.freeze.to_sym => user.session_token }
71
+ end
72
+
73
+
67
74
  #
68
75
  # Dumb glue method, deal with rails 4 vs rails 5 get/post methods.
69
76
  #
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.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Tomich
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-17 00:00:00.000000000 Z
11
+ date: 2017-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bcrypt
@@ -114,20 +114,6 @@ dependencies:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
116
  version: '1.3'
117
- - !ruby/object:Gem::Dependency
118
- name: shoulda-matchers
119
- requirement: !ruby/object:Gem::Requirement
120
- requirements:
121
- - - "~>"
122
- - !ruby/object:Gem::Version
123
- version: '2.8'
124
- type: :development
125
- prerelease: false
126
- version_requirements: !ruby/object:Gem::Requirement
127
- requirements:
128
- - - "~>"
129
- - !ruby/object:Gem::Version
130
- version: '2.8'
131
117
  - !ruby/object:Gem::Dependency
132
118
  name: capybara
133
119
  requirement: !ruby/object:Gem::Requirement
@@ -240,6 +226,7 @@ files:
240
226
  - config/routes.rb
241
227
  - gemfiles/4.2.gemfile
242
228
  - gemfiles/5.0.gemfile
229
+ - gemfiles/5.1.gemfile
243
230
  - lib/authenticate.rb
244
231
  - lib/authenticate/callbacks/authenticatable.rb
245
232
  - lib/authenticate/callbacks/brute_force.rb
@@ -263,6 +250,11 @@ files:
263
250
  - lib/authenticate/model/username.rb
264
251
  - lib/authenticate/modules.rb
265
252
  - lib/authenticate/session.rb
253
+ - lib/authenticate/testing/controller_helpers.rb
254
+ - lib/authenticate/testing/integration_tests_sign_on.rb
255
+ - lib/authenticate/testing/rspec.rb
256
+ - lib/authenticate/testing/test_unit.rb
257
+ - lib/authenticate/testing/view_helpers.rb
266
258
  - lib/authenticate/token.rb
267
259
  - lib/authenticate/user.rb
268
260
  - lib/authenticate/version.rb
@@ -284,6 +276,7 @@ files:
284
276
  - lib/generators/authenticate/views/USAGE
285
277
  - lib/generators/authenticate/views/views_generator.rb
286
278
  - lib/tasks/authenticate_tasks.rake
279
+ - spec/controllers/deprecated_controller_methods_spec.rb
287
280
  - spec/controllers/secured_controller_spec.rb
288
281
  - spec/dummy/README.rdoc
289
282
  - spec/dummy/Rakefile