├── .codeclimate.yml
├── .gitignore
├── .rspec
├── .rubocop.yml
├── .ruby-version
├── .travis.yml
├── Appraisals
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gemfile
├── LICENSE
├── README.md
├── Rakefile
├── app
├── controllers
│ └── authenticate
│ │ ├── authenticate_controller.rb
│ │ ├── passwords_controller.rb
│ │ ├── sessions_controller.rb
│ │ └── users_controller.rb
├── mailers
│ └── authenticate_mailer.rb
└── views
│ ├── authenticate_mailer
│ ├── change_password.html.erb
│ └── change_password.text.erb
│ ├── layouts
│ └── application.html.erb
│ ├── passwords
│ ├── edit.html.erb
│ └── new.html.erb
│ ├── sessions
│ └── new.html.erb
│ └── users
│ └── new.html.erb
├── authenticate.gemspec
├── bin
├── rails
└── setup
├── config
├── locales
│ └── authenticate.en.yml
└── routes.rb
├── gemfiles
├── 4.2.gemfile
├── 5.0.gemfile
├── 5.1.gemfile
└── 5.2.gemfile
├── lib
├── authenticate.rb
├── authenticate
│ ├── callbacks
│ │ ├── authenticatable.rb
│ │ ├── brute_force.rb
│ │ ├── lifetimed.rb
│ │ ├── timeoutable.rb
│ │ └── trackable.rb
│ ├── configuration.rb
│ ├── controller.rb
│ ├── crypto
│ │ └── bcrypt.rb
│ ├── debug.rb
│ ├── engine.rb
│ ├── lifecycle.rb
│ ├── login_status.rb
│ ├── model
│ │ ├── brute_force.rb
│ │ ├── db_password.rb
│ │ ├── email.rb
│ │ ├── lifetimed.rb
│ │ ├── password_reset.rb
│ │ ├── timeoutable.rb
│ │ ├── trackable.rb
│ │ └── username.rb
│ ├── modules.rb
│ ├── session.rb
│ ├── testing
│ │ ├── controller_helpers.rb
│ │ ├── integration_tests_sign_on.rb
│ │ ├── rspec.rb
│ │ ├── test_unit.rb
│ │ └── view_helpers.rb
│ ├── token.rb
│ ├── user.rb
│ └── version.rb
├── generators
│ └── authenticate
│ │ ├── controllers
│ │ ├── USAGE
│ │ └── controllers_generator.rb
│ │ ├── helpers.rb
│ │ ├── install
│ │ ├── USAGE
│ │ ├── install_generator.rb
│ │ └── templates
│ │ │ ├── authenticate.rb
│ │ │ ├── db
│ │ │ └── migrate
│ │ │ │ ├── add_authenticate_brute_force_to_users.rb
│ │ │ │ ├── add_authenticate_password_reset_to_users.rb
│ │ │ │ ├── add_authenticate_timeoutable_to_users.rb
│ │ │ │ ├── add_authenticate_to_users.rb
│ │ │ │ └── create_users.rb
│ │ │ └── user.rb.erb
│ │ ├── routes
│ │ ├── USAGE
│ │ ├── routes_generator.rb
│ │ └── templates
│ │ │ └── routes.rb
│ │ └── views
│ │ ├── USAGE
│ │ └── views_generator.rb
└── tasks
│ └── authenticate_tasks.rake
└── spec
├── controllers
├── deprecated_controller_methods_spec.rb
└── secured_controller_spec.rb
├── dummy
├── README.rdoc
├── Rakefile
├── app
│ ├── assets
│ │ ├── images
│ │ │ └── .keep
│ │ ├── javascripts
│ │ │ └── application.js
│ │ └── stylesheets
│ │ │ └── application.css
│ ├── controllers
│ │ ├── application_controller.rb
│ │ ├── concerns
│ │ │ └── .keep
│ │ └── welcome_controller.rb
│ ├── helpers
│ │ └── application_helper.rb
│ ├── mailers
│ │ └── .keep
│ ├── models
│ │ ├── .keep
│ │ ├── concerns
│ │ │ └── .keep
│ │ └── user.rb
│ └── views
│ │ ├── layouts
│ │ └── application.html.erb
│ │ └── welcome
│ │ └── index.html.erb
├── bin
│ ├── bundle
│ ├── rails
│ ├── rake
│ └── setup
├── config.ru
├── config
│ ├── application.rb
│ ├── boot.rb
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ │ ├── development.rb
│ │ ├── production.rb
│ │ └── test.rb
│ ├── initializers
│ │ ├── assets.rb
│ │ ├── authenticate.rb
│ │ ├── backtrace_silencers.rb
│ │ ├── cookies_serializer.rb
│ │ ├── filter_parameter_logging.rb
│ │ ├── inflections.rb
│ │ ├── mime_types.rb
│ │ ├── session_store.rb
│ │ └── wrap_parameters.rb
│ ├── locales
│ │ └── en.yml
│ ├── routes.rb
│ └── secrets.yml
├── db
│ └── schema.rb
├── lib
│ └── assets
│ │ └── .keep
├── log
│ └── .keep
└── public
│ ├── 404.html
│ ├── 422.html
│ ├── 500.html
│ └── favicon.ico
├── factories
└── users.rb
├── features
├── brute_force_spec.rb
├── create_user_spec.rb
├── max_session_lifetime_spec.rb
├── new_user_form_spec.rb
├── password_reset_spec.rb
├── password_update_spec.rb
├── sign_in_spec.rb
├── sign_out_spec.rb
├── sign_up_spec.rb
└── timeoutable_spec.rb
├── model
├── brute_force_spec.rb
├── configuration_spec.rb
├── db_password_spec.rb
├── email_spec.rb
├── lifetimed_spec.rb
├── modules_spec.rb
├── password_reset_spec.rb
├── session_spec.rb
├── timeoutable_spec.rb
├── token_spec.rb
├── trackable_spec.rb
└── user_spec.rb
├── orm
└── active_record.rb
├── requests
├── csrf_rotation_spec.rb
└── session_key_spec.rb
├── spec_helper.rb
└── support
├── controllers
└── controller_helpers.rb
├── features
└── feature_helpers.rb
├── mailer.rb
└── request_helpers.rb
/.codeclimate.yml:
--------------------------------------------------------------------------------
1 | ---
2 | engines:
3 | brakeman:
4 | enabled: true
5 | bundler-audit:
6 | enabled: true
7 | duplication:
8 | enabled: true
9 | exclude_fingerprints:
10 | - 120bdc2114c46391e7ad73ea4a10b314
11 | - 339cd5bbb9922a29a82aaf5f3d727deb
12 | config:
13 | languages:
14 | - ruby
15 | - javascript
16 | - python
17 | - php
18 | fixme:
19 | enabled: true
20 | rubocop:
21 | enabled: true
22 | ratings:
23 | paths:
24 | - Gemfile.lock
25 | - "**.erb"
26 | - "**.haml"
27 | - "**.rb"
28 | - "**.rhtml"
29 | - "**.slim"
30 | - "**.inc"
31 | - "**.js"
32 | - "**.jsx"
33 | - "**.module"
34 | - "**.php"
35 | - "**.py"
36 | exclude_paths:
37 | - config/
38 | - spec/
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .bundle/
2 | authenticate-*.gem
3 | log/*.log
4 | pkg/
5 | spec/dummy/db/*.sqlite3
6 | spec/dummy/db/*.sqlite3-journal
7 | spec/dummy/log/*.log
8 | spec/dummy/tmp/
9 | spec/dummy/log/test.log
10 | spec/dummy/log/development.log
11 | /.idea
12 | Gemfile.lock
13 | *.gemfile.lock
14 |
--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --color
2 | --require spec_helper
3 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | AllCops:
2 | Exclude:
3 | - 'bin/rails'
4 | - 'spec/dummy/**/*'
5 | - 'lib/generators/authenticate/install/templates/db/migrate/**/*'
6 | - 'lib/generators/authenticate/routes/templates/routes.rb'
7 |
8 | Metrics/LineLength:
9 | Max: 120
10 |
11 | Documentation:
12 | Exclude:
13 | - 'lib/generators/**/*'
14 |
15 | Metrics/MethodLength:
16 | Max: 16
17 |
18 | Metrics/ClassLength:
19 | Exclude:
20 | - 'lib/generators/authenticate/install/install_generator.rb'
21 |
22 | Metrics/AbcSize:
23 | Max: 18
24 |
25 | Lint/IneffectiveAccessModifier:
26 | Exclude:
27 | - 'lib/generators/authenticate/**/*'
28 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.4.1
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | cache: bundler
2 |
3 | language:
4 | - ruby
5 |
6 | rvm:
7 | - 2.1.8
8 | - 2.2.4
9 | - 2.3.3
10 | - 2.4.1
11 |
12 | gemfile:
13 | - gemfiles/4.2.gemfile
14 | - gemfiles/5.0.gemfile
15 | - gemfiles/5.1.gemfile
16 | - gemfiles/5.2.gemfile
17 |
18 |
19 | matrix:
20 | exclude:
21 | - rvm: 2.1.8
22 | gemfile: gemfiles/5.0.gemfile
23 | - rvm: 2.1.8
24 | gemfile: gemfiles/5.1.gemfile
25 | - rvm: 2.1.8
26 | gemfile: gemfiles/5.2.gemfile
27 | - rvm: 2.4.1
28 | gemfile: gemfiles/4.2.gemfile
29 |
30 | install:
31 | - "bin/setup"
32 |
33 | sudo: false
34 |
--------------------------------------------------------------------------------
/Appraisals:
--------------------------------------------------------------------------------
1 | if RUBY_VERSION < "2.4.0"
2 | appraise "4.2" do
3 | gem "rails", "~> 4.2.0"
4 | end
5 | end
6 |
7 | if RUBY_VERSION >= "2.2.2"
8 | appraise "5.0" do
9 | gem "rails", "~> 5.0.0"
10 | end
11 |
12 | appraise "5.1" do
13 | gem "rails", "~> 5.1"
14 | end
15 |
16 | appraise "5.2" do
17 | gem "rails", "~> 5.2"
18 | end
19 | end
20 |
21 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Authenticate Changelog
2 |
3 |
4 | ## [0.7.3] - June 7, 2018
5 |
6 | ### Support for rails 5.2
7 | - added rails 5.2 support to gemspec
8 | - added rails 5.2 to Appraisals, .travis.yml, gemfiles
9 | - added `sqlite3.represent_boolean_as_integer = true` to dummy application config
10 | - bumped authenticate version
11 | - update request specs, looks for 302 after login
12 | - added build and release tasks to Rakefile
13 |
14 | [0.7.3]: https://github.com/tomichj/authenticate/compare/v0.7.2...v0.7.3
15 |
16 |
17 | ## [0.7.2] - June 22, 2017
18 |
19 | ### API change
20 | - removed new_users_path, sign_up_path remains
21 |
22 | ### New Feature
23 | - added allow_sign_up flag to install generator
24 |
25 | [0.7.2]: https://github.com/tomichj/authenticate/compare/v0.7.1...v0.7.2
26 |
27 |
28 | ## [0.7.1] - June 22, 2017
29 |
30 | ### Fixed
31 | - routes respects config.allow_sign_up? and shuts off /sign_up
32 | - removed spurious output from install generator
33 |
34 | [0.7.1]: https://github.com/tomichj/authenticate/compare/v0.7.0...v0.7.1
35 |
36 |
37 | ## [0.7.0] - May 25, 2017
38 |
39 | ### API Changes
40 | - controller#require_authentication is deprecated, use controller#require_login
41 | - controller#authenticated? is deprecated, use controller#logged_in?
42 | - added controller#logged_out?
43 | `authenticated?` and `required_authentication` will be removed in a future release.
44 |
45 | ### Test support
46 | - Added login_as via middleware for feature/integration/system tests.
47 | - added rspec helpers for view and controller tests
48 | - added test-unit helpers for controller/view tests
49 |
50 | ### Internal changes - will not affect normal apps
51 | - Session#initialize(request, cookies) is now Session#initialize(request)
52 | - Session API changes, #authenticated? renamed #logged_in?
53 |
54 | [0.7.0]: https://github.com/tomichj/authenticate/compare/v0.6.1...v0.7.0
55 |
56 |
57 | ## [0.6.1] - May 16, 2017
58 |
59 | ### Fixed
60 | - install migrations now correctly support rails 4.2.x and rails 5.x.
61 |
62 | [0.6.1]: https://github.com/tomichj/authenticate/compare/v0.6.0...v0.6.1
63 |
64 |
65 |
66 | ## [0.6.0] - May 16, 2017
67 |
68 | ### Security
69 | - Prevent [password reset token leakage] through HTTP referrer across domains. password#edit removes the password
70 | reset token from the url, sets it into the user's session (typically a cookie), and redirects to password#url
71 | without the token in the url.
72 |
73 | - Prevent [session fixation] attacks by rotating CSRF tokens on sign-in by setting
74 | `Authentication.configuration.rotate_csrf_on_sign_in` to `true`. This is recommended for
75 | all applications. The setting defaults to `false` in this release, but will default to `true`
76 | in a future release.
77 |
78 | ### Fixed
79 | - Location to return to after login is now written to session. Was previously written explicitly to a cookie.
80 | - Most controller tests rewritten as feature and request tests.
81 |
82 | [password reset token leakage]: https://security.stackexchange.com/questions/69074/how-to-implement-password-reset-functionality-without-becoming-susceptible-to-cr
83 | [session fixation]: http://guides.rubyonrails.org/security.html#session-fixation
84 | [0.6.0]: https://github.com/tomichj/authenticate/compare/v0.5.0...v0.6.0
85 |
86 |
87 |
88 | ## [0.5.0] - March 26, 2017oh
89 |
90 | ### Support for rails 5.1.
91 |
92 | [0.5.0]: https://github.com/tomichj/authenticate/compare/v0.4.0...v0.5.0
93 |
94 |
95 |
96 | ## [0.4.0] - June 2, 2016
97 |
98 | ### Fixed
99 | - Install generator User: ActiveRecord::Base for Rails 4 apps, ApplicationRecord for rails 5 (issue #2).
100 |
101 | [0.4.0]: https://github.com/tomichj/authenticate/compare/v0.3.3...v0.4.0
102 |
103 |
104 |
105 | ## [0.3.3] - April 29, 2016
106 |
107 | - Password change uses active record's dirty bit to detect that password was updated.
108 | - password_updated attribute removed.
109 | - spec_helper now calls ActiveRecord::Migration.maintain_test_schema! (or check_pending!) to handle dummy test db.
110 | - Added CodeClimate config.
111 |
112 | [0.3.3]: https://github.com/tomichj/authenticate/compare/v0.3.2...v0.3.3
113 |
114 |
115 |
116 | ## [0.3.2] - April 28, 2016
117 |
118 | - Error now raised if User model is missing required attributes.
119 | - All code now conforms to a rubocode profile.
120 |
121 | [0.3.2]: https://github.com/tomichj/authenticate/compare/v0.3.1...v0.3.2
122 |
123 |
124 |
125 | ## [0.3.1] - March 10, 2016
126 |
127 | - User controller now allows arbitrary parameters without having to explicitly declare
128 | them. Still requires email and password.
129 | - Mailer now checks for mail.respond_to?(:deliver_later) rather than rails version, to decide deliver vs deliver_later.
130 | - Removed unused user_id_parameter config method.
131 |
132 | [0.3.1]: https://github.com/tomichj/authenticate/compare/v0.3.0...v0.3.1
133 |
134 |
135 |
136 | ## [0.3.0] - February 24, 2016
137 |
138 | - Moved normalize_email and find_normalized_email methods to base User module.
139 | - Added full suite of controller and feature tests.
140 |
141 | ### Fixes
142 | - failed login count fix was off by one.
143 | - password validation now done only in correct circumstances
144 |
145 | [0.3.0]: https://github.com/tomichj/authenticate/compare/v0.2.2...v0.3.0
146 |
147 |
148 |
149 | ## [0.2.3] - February 13, 2016
150 |
151 | - Small bugfix for :username authentication.
152 | - Improved documentation, started adding wiki pages.
153 |
154 | [0.2.3]: https://github.com/tomichj/authenticate/compare/v0.2.2...v0.2.3
155 |
156 |
157 |
158 | ## [0.2.2] - February 9, 2016
159 |
160 | - Password length range requirements added, defaults to 8..128.
161 | - Generators and app now respect model class more completely, including in routes.
162 |
163 | [0.2.2]: https://github.com/tomichj/authenticate/compare/v0.2.1...v0.2.2
164 |
165 |
166 |
167 | ## [0.2.1] - February 9, 2016
168 |
169 | - Fixed potential password_reset nil pointer.
170 | - Continued adding I18n support.
171 | - Minor documentation improvements.
172 |
173 | [0.2.1]: https://github.com/tomichj/authenticate/compare/v0.2.0...v0.2.1
174 |
175 |
176 |
177 | ## [0.2.0] - February 2, 2016
178 |
179 | - Added app/ including controllers, views, routes, mailers.
180 |
181 | [0.2.0]: https://github.com/tomichj/authenticate/compare/v0.1.0...v0.2.0
182 |
183 |
184 |
185 | ## 0.1.0 - January 23, 2016
186 |
187 | - Initial Release, barely functioning
188 |
189 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | I love pull requests. I'm trying to keep it as easy as possible to contribute changes. There
4 | are just a couple of guidelines to follow to help me stay on top of things.
5 |
6 |
7 | ## Let's talk
8 |
9 | Whether you're fixing a bug or adding a feature, feel free to talk to me first on
10 | [twitter](https://twitter.com/JustinTomich). We can make sure the change isn't already
11 | underway somewhere else.
12 |
13 |
14 | ## Getting started
15 |
16 | * Make sure you have a [GitHub account](https://github.com/signup/free)
17 | * Open a [New Issue](https://github.com/tomichj/authenticate/issues) on github for your change,
18 | assuming one does not already exist. If one already exists, join the conversation.
19 | * Fork the repository on GitHub.
20 |
21 | ## Setup
22 |
23 | Clone the repo:
24 |
25 | `git clone https://github.com/ <%= t(".opening") %>
4 | <%= link_to t(".link_text", default: "Change my password"),
5 | edit_users_password_url(@user, token: @user.password_reset_token.html_safe) %>
6 | <%= raw t(".closing") %>
<%= t(".description") %>
5 | 6 | <%= form_for :password_reset, 7 | url: users_password_path(@user, token: @user.password_reset_token), 8 | html: { method: :put } do |form| %> 9 | 10 |<%= t(".description") %>
5 | 6 | <%= form_for :password, url: passwords_path do |form| %> 7 | 8 |Your email address: <%= current_user.email %>
118 | # 119 | def current_user 120 | authenticate_session.current_user 121 | end 122 | 123 | # Return true if it's an Authenticate controller. Useful if you want to apply a before 124 | # filter to all controllers, except the ones in Authenticate, e.g. 125 | # 126 | # before_action :my_filter, unless: :authenticate_controller? 127 | # 128 | def authenticate_controller? 129 | is_a?(Authenticate::AuthenticateController) 130 | end 131 | 132 | # The old API. DEPRECATED, use #require_login instead. 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. DEPRECATED, use #logged_in? instead. 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 | 150 | protected 151 | 152 | # User is not authorized, bounce 'em to sign in 153 | def unauthorized(msg = t('flashes.failure_when_not_signed_in')) 154 | authenticate_session.logout 155 | respond_to do |format| 156 | format.any(:js, :json, :xml) { head :unauthorized } 157 | format.any { redirect_unauthorized(msg) } 158 | end 159 | end 160 | 161 | def redirect_unauthorized(flash_message) 162 | store_location! 163 | 164 | if flash_message 165 | flash[:notice] = flash_message # TODO: use locales 166 | end 167 | 168 | if logged_in? 169 | redirect_to url_after_denied_access_when_signed_in 170 | else 171 | redirect_to url_after_denied_access_when_signed_out 172 | end 173 | end 174 | 175 | def redirect_back_or(default) 176 | redirect_to(stored_location || default) 177 | clear_stored_location 178 | end 179 | 180 | # Used as the redirect location when {#unauthorized} is called and there is a 181 | # currently signed in user. 182 | # 183 | # @return [String] 184 | def url_after_denied_access_when_signed_in 185 | Authenticate.configuration.redirect_url 186 | end 187 | 188 | # Used as the redirect location when {#unauthorized} is called and there is 189 | # no currently signed in user. 190 | # 191 | # @return [String] 192 | def url_after_denied_access_when_signed_out 193 | sign_in_url 194 | end 195 | 196 | private 197 | 198 | # Write location to return to in user's session (normally a cookie). 199 | def store_location! 200 | if request.get? 201 | session[:authenticate_return_to] = request.original_fullpath 202 | end 203 | end 204 | 205 | def stored_location 206 | session[:authenticate_return_to] 207 | end 208 | 209 | def clear_stored_location 210 | session[:authenticate_return_to] = nil 211 | end 212 | 213 | def authenticate_session 214 | @authenticate_session ||= Authenticate::Session.new(request) 215 | end 216 | end 217 | end 218 | -------------------------------------------------------------------------------- /lib/authenticate/crypto/bcrypt.rb: -------------------------------------------------------------------------------- 1 | module Authenticate 2 | module Crypto 3 | # 4 | # All crypto providers must implement encrypt(secret) and match?(secret, encrypted) 5 | module BCrypt 6 | require 'bcrypt' 7 | 8 | def encrypt(secret) 9 | ::BCrypt::Password.create secret, cost: cost 10 | end 11 | 12 | def match?(secret, encrypted) 13 | return false unless encrypted.present? 14 | ::BCrypt::Password.new(encrypted) == secret 15 | end 16 | 17 | def cost 18 | @cost ||= ::BCrypt::Engine::DEFAULT_COST 19 | end 20 | 21 | def cost=(val) 22 | if val < ::BCrypt::Engine::MIN_COST 23 | msg = "bcrypt cost cannot be set below the engine's min cost (#{::BCrypt::Engine::MIN_COST})" 24 | raise ArgumentError.new(msg), msg 25 | end 26 | @cost = val 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/authenticate/debug.rb: -------------------------------------------------------------------------------- 1 | module Authenticate 2 | # 3 | # Simple debug output for gem. 4 | # 5 | module Debug 6 | extend ActiveSupport::Concern 7 | 8 | def debug(msg) 9 | if defined?(Rails) && defined?(Rails.logger) && Authenticate.configuration.debug 10 | Rails.logger.info msg.to_s 11 | elsif Authenticate.configuration.debug 12 | puts msg.to_s 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/authenticate/engine.rb: -------------------------------------------------------------------------------- 1 | require 'authenticate' 2 | require 'rails' 3 | 4 | module Authenticate 5 | # 6 | # Authenticate Rails engine. 7 | # Filter password, token, from spewing out. 8 | # 9 | class Engine < ::Rails::Engine 10 | initializer 'authenticate.filter' do |app| 11 | app.config.filter_parameters += [:password, :token] 12 | end 13 | 14 | config.generators do |g| 15 | g.test_framework :rspec 16 | g.fixture_replacement :factory_girl, dir: 'spec/factories' 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/authenticate/lifecycle.rb: -------------------------------------------------------------------------------- 1 | # Authenticate Lifecycle methods within 2 | module Authenticate 3 | # 4 | # Lifecycle stores and runs callbacks for authorization events. 5 | # 6 | # Heavily borrowed from warden (https://github.com/hassox/warden). 7 | # 8 | # = Events: 9 | # * :set_user - called after the user object is loaded, either through id/password or via session token. 10 | # * :authentication - called after the user authenticates with id & password 11 | # 12 | # Callbacks are added via after_set_user or after_authentication. 13 | # 14 | # Callbacks can throw(:failure,message) to signal an authentication/authorization failure, or perform 15 | # actions on the user or session. 16 | # 17 | # = Options 18 | # 19 | # The callback options may optionally specify when to run the callback: 20 | # * only - executes the callback only if it matches the event(s) given 21 | # * except - executes the callback except if it matches the event(s) given 22 | # 23 | # The callback may also specify a 'name' key in options. This is for debugging purposes only. 24 | # 25 | # = Callback block parameters 26 | # 27 | # Callbacks are invoked with the following block parameters: |user, session, opts| 28 | # * user - the user object just loaded 29 | # * session - the Authenticate::Session 30 | # * opts - any options you want passed into the callback 31 | # 32 | # = Example 33 | # 34 | # # A callback to track the users successful logins: 35 | # Authenticate.lifecycle.after_set_user do |user, session, opts| 36 | # user.sign_in_count += 1 37 | # end 38 | # 39 | class Lifecycle 40 | include Debug 41 | 42 | def initialize 43 | @conditions = [:only, :except, :event].freeze 44 | end 45 | 46 | # This callback is triggered after the first time a user is set during per-hit authorization, or during login. 47 | def after_set_user(options = {}, method = :push, &block) 48 | add_callback(after_set_user_callbacks, options, method, &block) 49 | end 50 | 51 | # A callback to run after the user successfully authenticates, during the login process. 52 | # Mechanically identical to [#after_set_user]. 53 | def after_authentication(options = {}, method = :push, &block) 54 | add_callback(after_authentication_callbacks, options, method, &block) 55 | end 56 | 57 | # Run callbacks of the given kind. 58 | # 59 | # * kind - :authenticate or :after_set_user 60 | # * args - user, session, opts hash. Opts is an optional event, e.g. { event: :authentication } 61 | # 62 | # Example: 63 | # Authenticate.lifecycle.run_callbacks(:after_set_user, @current_user, self, { event: :authentication }) 64 | # 65 | def run_callbacks(kind, user, session, *args) # args - |user, session, opts| 66 | # Last callback arg MUST be a Hash 67 | options = args.last 68 | send("#{kind}_callbacks").each do |callback, conditions| # each callback has 'conditions' stored with it 69 | conditions = conditions.dup.delete_if { |key, _val| !@conditions.include? key } 70 | invalid = conditions.find do |key, value| 71 | value.is_a?(Array) ? !value.include?(options[key]) : (value != options[key]) 72 | end 73 | callback.call(user, session, *args) unless invalid 74 | end 75 | nil 76 | end 77 | 78 | def prepend_after_authentication(options = {}, &block) 79 | after_authentication(options, :unshift, &block) 80 | end 81 | 82 | private 83 | 84 | def add_callback(callbacks, options = {}, method = :push, &block) 85 | raise BlockNotGiven unless block_given? 86 | options = process_opts(options) 87 | callbacks.send(method, [block, options]) 88 | end 89 | 90 | # set event: to run callback on based on options 91 | def process_opts(options) 92 | if options.key?(:only) 93 | options[:event] = options.delete(:only) 94 | elsif options.key?(:except) 95 | options[:event] = [:set_user, :authentication] - Array(options.delete(:except)) 96 | end 97 | options 98 | end 99 | 100 | def after_set_user_callbacks 101 | @after_set_user_callbacks ||= [] 102 | end 103 | 104 | def after_authentication_callbacks 105 | @after_authentication_callbacks ||= [] 106 | end 107 | end 108 | 109 | # Invoke lifecycle methods. Example: 110 | # Authenticate.lifecycle.run_callbacks(:after_set_user, current_user, authenticate_session, { event: :set_user }) 111 | # 112 | def self.lifecycle 113 | @lifecycle ||= Lifecycle.new 114 | end 115 | 116 | def self.lifecycle=(lifecycle) 117 | @lifecycle = lifecycle 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /lib/authenticate/login_status.rb: -------------------------------------------------------------------------------- 1 | module Authenticate 2 | # 3 | # Indicate login attempt was successful. Allows caller to supply a block to login() predicated on success? 4 | # 5 | class Success 6 | def success? 7 | true 8 | end 9 | end 10 | 11 | # 12 | # Indicate login attempt was a failure, with a message. 13 | # Allows caller to supply a block to login() predicated on success? 14 | # 15 | class Failure 16 | # The reason the sign in failed. 17 | attr_reader :message 18 | 19 | # @param [String] message The reason the login failed. 20 | def initialize(message) 21 | @message = message 22 | end 23 | 24 | def success? 25 | false 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /lib/authenticate/model/brute_force.rb: -------------------------------------------------------------------------------- 1 | require 'authenticate/callbacks/brute_force' 2 | 3 | module Authenticate 4 | module Model 5 | # 6 | # Protect from brute force attacks. Lock accounts that have too many failed consecutive logins. 7 | # Todo: email user to allow unlocking via a token. 8 | # 9 | # To enable brute force protection, set the config params shown below. Example: 10 | # 11 | # Authenticate.configure do |config| 12 | # config.bad_login_lockout_period = 5.minutes 13 | # config.max_consecutive_bad_logins_allowed = 3 14 | # end 15 | # 16 | # = Columns 17 | # * failed_logins_count - each consecutive failed login increments this counter. Set back to 0 on successful login. 18 | # * lock_expires_at - datetime a locked account will again become available. 19 | # 20 | # = Configuration 21 | # * max_consecutive_bad_logins_allowed - how many failed logins are allowed? 22 | # * bad_login_lockout_period - how long is the user locked out? nil indicates forever. 23 | # 24 | # = Methods 25 | # The following methods are added to your user model: 26 | # * register_failed_login! - increment failed_logins_count, lock account if in violation 27 | # * lock! - lock the account, setting the lock_expires_at attribute 28 | # * unlock! - reset failed_logins_count to 0, lock_expires_at to nil 29 | # * locked? - is the account locked? @return[Boolean] 30 | # * unlocked? - is the account unlocked? @return[Boolean] 31 | # 32 | module BruteForce 33 | extend ActiveSupport::Concern 34 | 35 | def self.required_fields(_klass) 36 | [:failed_logins_count, :lock_expires_at] 37 | end 38 | 39 | def register_failed_login! 40 | self.failed_logins_count ||= 0 41 | self.failed_logins_count += 1 42 | lock! if self.failed_logins_count > max_bad_logins 43 | end 44 | 45 | def lock! 46 | update_attribute(:lock_expires_at, Time.now.utc + lockout_period) 47 | end 48 | 49 | def unlock! 50 | update_attributes(failed_logins_count: 0, lock_expires_at: nil) 51 | end 52 | 53 | def locked? 54 | !unlocked? 55 | end 56 | 57 | def unlocked? 58 | lock_expires_at.nil? 59 | end 60 | 61 | private 62 | 63 | def max_bad_logins 64 | Authenticate.configuration.max_consecutive_bad_logins_allowed 65 | end 66 | 67 | def lockout_period 68 | Authenticate.configuration.bad_login_lockout_period 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/authenticate/model/db_password.rb: -------------------------------------------------------------------------------- 1 | require 'authenticate/crypto/bcrypt' 2 | 3 | module Authenticate 4 | module Model 5 | # 6 | # Encrypts and stores a password in the database to validate the authenticity of a user while logging in. 7 | # 8 | # Authenticate can plug in any crypto provider, but currently only features BCrypt. 9 | # 10 | # A crypto provider must provide: 11 | # * encrypt(secret) - encrypt the secret, @return [String] 12 | # * match?(secret, encrypted) - does the secret match the encrypted? @return [Boolean] 13 | # 14 | # = Columns 15 | # * encrypted_password - the user's password, encrypted 16 | # 17 | # = Methods 18 | # The following methods are added to your user model: 19 | # * password=(new_password) - encrypt and set the user password 20 | # * password_match?(password) - checks to see if the user's password matches the given password 21 | # 22 | # = Validations 23 | # * :password validation, requiring the password is set unless we're skipping due to a password change 24 | # 25 | module DbPassword 26 | extend ActiveSupport::Concern 27 | 28 | def self.required_fields(_klass) 29 | [:encrypted_password] 30 | end 31 | 32 | included do 33 | private_class_method :crypto_provider 34 | include crypto_provider 35 | attr_reader :password 36 | validates :password, 37 | presence: true, 38 | length: { in: password_length }, 39 | unless: :skip_password_validation? 40 | end 41 | 42 | def password_match?(password) 43 | match?(password, encrypted_password) 44 | end 45 | 46 | def password=(new_password) 47 | @password = new_password 48 | self.encrypted_password = encrypt(new_password) unless new_password.nil? 49 | end 50 | 51 | private 52 | 53 | # Class methods for database password management. 54 | module ClassMethods 55 | # We only have one crypto provider at the moment, but look up the provider in the config. 56 | def crypto_provider 57 | Authenticate.configuration.crypto_provider || Authenticate::Crypto::BCrypt 58 | end 59 | 60 | def password_length 61 | Authenticate.configuration.password_length 62 | end 63 | end 64 | 65 | # If we already have an encrypted password and it's not changing, skip the validation. 66 | def skip_password_validation? 67 | encrypted_password.present? && !encrypted_password_changed? 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /lib/authenticate/model/email.rb: -------------------------------------------------------------------------------- 1 | require 'email_validator' 2 | 3 | module Authenticate 4 | module Model 5 | # 6 | # Use :email as the identifier for the user. Email must be unique. 7 | # 8 | # = Columns 9 | # * email - the email address of the user 10 | # 11 | # = Validations 12 | # * :email - require email is set, is a valid format, and is unique 13 | # 14 | # = Callbacks 15 | # 16 | # = Methods 17 | # * normalize_email - normalize the email, removing spaces etc, before saving 18 | # 19 | # = Class Methods 20 | # * credentials(params) - return the credentials required for authorization by email 21 | # * authenticate(credentials) - find user with given email, validate their password, return user if authenticated 22 | # * normalize_email(email) - clean up the given email and return it. 23 | # * find_by_credentials(credentials) - find and return the user with the email address in the credentials 24 | # 25 | module Email 26 | extend ActiveSupport::Concern 27 | 28 | def self.required_fields(_klass) 29 | [:email] 30 | end 31 | 32 | included do 33 | before_validation :normalize_email 34 | validates :email, 35 | email: { strict_mode: true }, 36 | presence: true, 37 | uniqueness: { allow_blank: true } 38 | end 39 | 40 | # Class methods for authenticating using email as the user identifier. 41 | module ClassMethods 42 | # Retrieve credentials from params. 43 | # 44 | # @return [id, pw] 45 | def credentials(params) 46 | return [] if params.nil? || params[:session].nil? 47 | [params[:session][:email], params[:session][:password]] 48 | end 49 | 50 | def authenticate(credentials) 51 | user = find_by_credentials(credentials) 52 | user && user.password_match?(credentials[1]) ? user : nil 53 | end 54 | 55 | def find_by_credentials(credentials) 56 | email = credentials[0] 57 | find_by_normalized_email(email) 58 | end 59 | end 60 | 61 | # Sets the email on this instance to the value returned by class method #normalize_email 62 | # 63 | # @return [String] 64 | def normalize_email 65 | self.email = self.class.normalize_email(email) 66 | end 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/authenticate/model/lifetimed.rb: -------------------------------------------------------------------------------- 1 | require 'authenticate/callbacks/lifetimed' 2 | 3 | module Authenticate 4 | module Model 5 | # 6 | # Imposes a maximum allowed lifespan on a user's session, after which the session is expired and requires 7 | # re-authentication. 8 | # 9 | # = Configuration 10 | # Set the maximum session lifetime in the initializer, giving a timestamp. 11 | # 12 | # Authenticate.configure do |config| 13 | # config.max_session_lifetime = 8.hours 14 | # end 15 | # 16 | # If the max_session_lifetime configuration parameter is nil, the :lifetimed module is not loaded. 17 | # 18 | # = Columns 19 | # * current_sign_in_at - requires `current_sign_in_at` column. This column is managed by the :trackable plugin. 20 | # 21 | # = Methods 22 | # * max_session_lifetime_exceeded? - true if the user's session has exceeded the max lifetime allowed 23 | # 24 | # 25 | module Lifetimed 26 | extend ActiveSupport::Concern 27 | 28 | def self.required_fields(_klass) 29 | [:current_sign_in_at] 30 | end 31 | 32 | # Has the session reached its maximum allowed lifespan? 33 | def max_session_lifetime_exceeded? 34 | return false if max_session_lifetime.nil? 35 | return false if current_sign_in_at.nil? 36 | current_sign_in_at <= max_session_lifetime.ago 37 | end 38 | 39 | private 40 | 41 | def max_session_lifetime 42 | Authenticate.configuration.max_session_lifetime 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/authenticate/model/password_reset.rb: -------------------------------------------------------------------------------- 1 | module Authenticate 2 | module Model 3 | # Support 'forgot my password' functionality. 4 | # 5 | # = Columns 6 | # * password_reset_token - token required to reset a password 7 | # * password_reset_sent_at - datetime password reset token was emailed to user 8 | # * email - email address of user 9 | # 10 | # = Methods 11 | # * update_password(new_password) - call password setter below, generate a new session token if user.valid?, & save 12 | # * forgot_password! - generate a new password reset token, timestamp, and save 13 | # * reset_password_period_valid? - is the password reset token still usable? 14 | # 15 | module PasswordReset 16 | extend ActiveSupport::Concern 17 | 18 | def self.required_fields(_klass) 19 | [:password_reset_token, :password_reset_sent_at, :email] 20 | end 21 | 22 | # Sets the user's password to the new value. The new password will be encrypted with 23 | # the selected encryption scheme (defaults to Bcrypt). 24 | # 25 | # Updating the user password also generates a new session token. 26 | # 27 | # Validations will be run as part of this update. If the user instance is 28 | # not valid, the password change will not be persisted, and this method will 29 | # return `false`. 30 | # 31 | # @return [Boolean] Was the save successful? 32 | def update_password(new_password) 33 | return false unless reset_password_period_valid? 34 | self.password = new_password 35 | if valid? 36 | clear_reset_password_token 37 | generate_session_token 38 | end 39 | save 40 | end 41 | 42 | # Generates a {#password_reset_token} for the user, which allows them to reset 43 | # their password via an email link. 44 | # 45 | # The user model is saved without validations. Any other changes you made to 46 | # this user instance will also be persisted, without validation. 47 | # It is intended to be called on an instance with no changes (`dirty? == false`). 48 | # 49 | # @return [Boolean] Was the save successful? 50 | def forgot_password! 51 | self.password_reset_token = Authenticate::Token.new 52 | self.password_reset_sent_at = Time.now.utc 53 | save validate: false 54 | end 55 | 56 | # Checks if the reset password token is within the time limit. 57 | # If the application's reset_password_within is nil, then always return true. 58 | # 59 | # Example: 60 | # # reset_password_within = 1.day and reset_password_sent_at = today 61 | # reset_password_period_valid? # returns true 62 | # 63 | def reset_password_period_valid? 64 | reset_within = Authenticate.configuration.reset_password_within 65 | return true if reset_within.nil? 66 | return true if password_reset_sent_at.nil? && password_reset_token.nil? 67 | password_reset_sent_at && password_reset_sent_at.utc >= reset_within.ago.utc 68 | end 69 | 70 | private 71 | 72 | def clear_reset_password_token 73 | self.password_reset_token = nil 74 | self.password_reset_sent_at = nil 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/authenticate/model/timeoutable.rb: -------------------------------------------------------------------------------- 1 | require 'authenticate/callbacks/timeoutable' 2 | 3 | module Authenticate 4 | module Model 5 | # Expire user sessions that have not been accessed within a certain period of time. 6 | # Expired users will be asked for credentials again. 7 | # 8 | # Timeoutable is enabled and configured with the `timeout_in` configuration parameter. 9 | # Example: 10 | # 11 | # Authenticate.configure do |config| 12 | # config.timeout_in = 15.minutes 13 | # end 14 | # 15 | # = Columns 16 | # This module expects and tracks this column on your user model: 17 | # * last_access_at - datetime of the last access by the user 18 | # 19 | # = Configuration 20 | # * timeout_in - maximum idle time allowed before session is invalidated. nil shuts off this feature. 21 | # 22 | # You must specify a non-nil timeout_in in your initializer to enable Timeoutable. 23 | # 24 | # = Methods 25 | # * timedout? - has this user timed out? @return[Boolean] 26 | # * timeout_in - look up timeout period in config, @return [ActiveSupport::CoreExtensions::Numeric::Time] 27 | # 28 | module Timeoutable 29 | extend ActiveSupport::Concern 30 | 31 | def self.required_fields(_klass) 32 | [:last_access_at] 33 | end 34 | 35 | # Checks whether the user session has expired based on configured time. 36 | def timedout? 37 | return false if timeout_in.nil? 38 | return false if last_access_at.nil? 39 | last_access_at <= timeout_in.ago 40 | end 41 | 42 | private 43 | 44 | def timeout_in 45 | Authenticate.configuration.timeout_in 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/authenticate/model/trackable.rb: -------------------------------------------------------------------------------- 1 | require 'authenticate/callbacks/trackable' 2 | 3 | module Authenticate 4 | module Model 5 | # 6 | # Track information about your user sign ins. This module is always enabled. 7 | # 8 | # = Methods 9 | # * update_tracked_fields - update the user's tracked fields based on the request. 10 | # * update_tracked_fields! - update tracked fields and save immediately, bypassing validations 11 | # 12 | # = Columns 13 | # - sign_in_count - increase every time a sign in is successful 14 | # - current_sign_in_at - a timestamp updated at each sign in 15 | # - last_sign_in_at - a timestamp of the previous sign in 16 | # - current_sign_in_ip - the remote ip address of the user at sign in 17 | # - previous_sign_in_ip - the remote ip address of the previous sign in 18 | # 19 | module Trackable 20 | extend ActiveSupport::Concern 21 | 22 | def self.required_fields(_klass) 23 | [:current_sign_in_at, :current_sign_in_ip, :last_sign_in_at, :last_sign_in_ip, :sign_in_count] 24 | end 25 | 26 | def update_tracked_fields(request) 27 | old_current = current_sign_in_at 28 | new_current = Time.now.utc 29 | self.last_sign_in_at = old_current || new_current 30 | self.current_sign_in_at = new_current 31 | 32 | old_current = current_sign_in_ip 33 | new_current = request.remote_ip 34 | self.last_sign_in_ip = old_current || new_current 35 | self.current_sign_in_ip = new_current 36 | 37 | self.sign_in_count ||= 0 38 | self.sign_in_count += 1 39 | end 40 | 41 | def update_tracked_fields!(request) 42 | update_tracked_fields(request) 43 | save(validate: false) 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/authenticate/model/username.rb: -------------------------------------------------------------------------------- 1 | module Authenticate 2 | module Model 3 | # 4 | # Use :username as the identifier for the user. Username must be unique. 5 | # 6 | # = Columns 7 | # * username - the username of your user 8 | # 9 | # = Validations 10 | # * :username requires username is set, ensure it is unique 11 | # 12 | # = class methods 13 | # * credentials(params) - return the credentials required for authorization by username 14 | # * authenticate(credentials) - find user with given username, validate their password, return user if authenticated 15 | # * find_by_credentials(credentials) - find and return the user with the username in the credentials 16 | # 17 | module Username 18 | extend ActiveSupport::Concern 19 | 20 | def self.required_fields(_klass) 21 | [:username, :email] 22 | end 23 | 24 | included do 25 | # before_validation :normalize_username 26 | validates :username, 27 | presence: true, 28 | uniqueness: { allow_blank: true } 29 | end 30 | 31 | # Class methods for managing username-based authentication 32 | module ClassMethods 33 | def credentials(params) 34 | [params[:session][:username], params[:session][:password]] 35 | end 36 | 37 | def authenticate(credentials) 38 | user = find_by_credentials(credentials) 39 | user && user.password_match?(credentials[1]) ? user : nil 40 | end 41 | 42 | def find_by_credentials(credentials) 43 | username = credentials[0] 44 | find_by_username username 45 | end 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/authenticate/modules.rb: -------------------------------------------------------------------------------- 1 | module Authenticate 2 | # 3 | # Modules injects Authenticate modules into the app User model. 4 | # 5 | # Modules are loaded into the user model with class method `load_modules`. Authenticate::User calls 6 | # `load_modules`. Modules are specified in the config as constants; `load_modules` requires them, 7 | # turns them into constants, checks for required fields (see below), and then includes them into the user. 8 | # 9 | # Any module being loaded into User can optionally define a class method `required_fields(klass)` defining 10 | # any required attributes in the User model. For example, the :username module declares: 11 | # 12 | # module Username 13 | # extend ActiveSupport::Concern 14 | # 15 | # def self.required_fields(klass) 16 | # [:username] 17 | # end 18 | # ... 19 | # 20 | # If the model class is missing a required field, Authenticate will fail with a MissingAttribute error. 21 | # The error will declare what required fields are missing. 22 | # 23 | # 24 | module Modules 25 | extend ActiveSupport::Concern 26 | # 27 | # Class methods injected into User model. 28 | # 29 | module ClassMethods 30 | # 31 | # Load all modules declared in Authenticate.configuration.modules. 32 | # Requires them, then loads as a constant, then checks fields, and finally includes. 33 | # 34 | # @raise MissingAttribute if attributes required by Authenticate are missing. 35 | def load_modules 36 | modules_to_include = [] 37 | Authenticate.configuration.modules.each do |mod| 38 | # The built-in modules are referred to by symbol. Additional module classes (constants) can be added 39 | # via Authenticate.configuration.modules. 40 | require "authenticate/model/#{mod}" if mod.is_a?(Symbol) 41 | mod = load_constant(mod) if mod.is_a?(Symbol) 42 | modules_to_include << mod 43 | end 44 | check_fields modules_to_include 45 | modules_to_include.each { |mod| include mod } 46 | end 47 | 48 | private 49 | 50 | def load_constant(module_symbol) 51 | Authenticate::Model.const_get(module_symbol.to_s.classify) 52 | end 53 | 54 | # For each module, look at the fields it requires. Ensure the User 55 | # model including the module has the required fields. 56 | # @raise MissingAttribute if required attributes are missing. 57 | def check_fields(modules) 58 | failed_attributes = [] 59 | instance = new 60 | modules.each do |mod| 61 | if mod.respond_to?(:required_fields) 62 | mod.required_fields(self).each { |field| failed_attributes << field unless instance.respond_to?(field) } 63 | end 64 | end 65 | 66 | if failed_attributes.any? 67 | raise MissingAttribute.new(failed_attributes), 68 | "Required attribute are missing on your user model: #{failed_attributes.join(', ')}" 69 | end 70 | end 71 | end 72 | 73 | # Thrown if required attributes are missing. 74 | class MissingAttribute < StandardError 75 | def initialize(attributes) 76 | @attributes = attributes 77 | end 78 | 79 | def message 80 | "Required attributes are missing on your user model: #{@attributes.join(', ')}" 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/authenticate/session.rb: -------------------------------------------------------------------------------- 1 | require 'authenticate/login_status' 2 | require 'authenticate/debug' 3 | 4 | module Authenticate 5 | # Represents an Authenticate session. 6 | class Session 7 | include Debug 8 | 9 | attr_accessor :request 10 | 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) 15 | @request = request # trackable module accesses request 16 | @cookies = request.cookie_jar 17 | @session_token = @cookies[cookie_name] 18 | debug 'SESSION initialize: @session_token: ' + @session_token.inspect 19 | end 20 | 21 | # Finish user login process, *after* the user has been authenticated. 22 | # The user is authenticated by Authenticate::Controller#authenticate. 23 | # 24 | # Called when user creates an account or signs back into the app. 25 | # Runs all configured callbacks, checking for login failure. 26 | # 27 | # If login is successful, @current_user is set and a session token is generated 28 | # and returned to the client browser. 29 | # If login fails, the user is NOT logged in. No session token is set, 30 | # and @current_user will not be set. 31 | # 32 | # After callbacks are finished, a {LoginStatus} is yielded to the provided block, 33 | # if one is provided. 34 | # 35 | # @param [User] user login completed for this user 36 | # @yieldparam [Success,Failure] status result of the sign in operation. 37 | # @return [User] 38 | def login(user) 39 | @current_user = user 40 | @current_user.generate_session_token if user.present? 41 | 42 | message = catch(:failure) do 43 | Authenticate.lifecycle.run_callbacks(:after_set_user, @current_user, self, event: :authentication) 44 | Authenticate.lifecycle.run_callbacks(:after_authentication, @current_user, self, event: :authentication) 45 | end 46 | 47 | status = message.present? ? Failure.new(message) : Success.new 48 | if status.success? 49 | @current_user.save 50 | write_cookie if @current_user.session_token 51 | else 52 | @current_user = nil 53 | end 54 | 55 | yield(status) if block_given? 56 | end 57 | 58 | # Get the user represented by this session. 59 | # 60 | # @return [User] 61 | def current_user 62 | debug "session.current_user #{@current_user.inspect}" 63 | @current_user ||= load_user_from_session_token if @session_token.present? 64 | @current_user 65 | end 66 | 67 | # Has this user successfully logged in? 68 | # 69 | # @return [Boolean] 70 | def logged_in? 71 | debug "session.logged_in? #{current_user.present?}" 72 | current_user.present? 73 | end 74 | 75 | # Invalidate the session token, unset the current user and remove the cookie. 76 | # 77 | # @return [void] 78 | def logout 79 | # nuke session_token in db 80 | current_user.reset_session_token! if current_user.present? 81 | 82 | # nuke notion of current_user 83 | @current_user = nil 84 | 85 | # nuke session_token cookie from the client browser 86 | @cookies.delete cookie_name 87 | end 88 | 89 | private 90 | 91 | def write_cookie 92 | cookie_hash = { 93 | path: Authenticate.configuration.cookie_path, 94 | secure: Authenticate.configuration.secure_cookie, 95 | httponly: Authenticate.configuration.cookie_http_only, 96 | value: @current_user.session_token, 97 | expires: Authenticate.configuration.cookie_expiration.call 98 | } 99 | cookie_hash[:domain] = Authenticate.configuration.cookie_domain if Authenticate.configuration.cookie_domain 100 | # Consider adding an option for a signed cookie 101 | @cookies[cookie_name] = cookie_hash 102 | end 103 | 104 | def cookie_name 105 | Authenticate.configuration.cookie_name.freeze.to_sym 106 | end 107 | 108 | def load_user_from_session_token 109 | Authenticate.configuration.user_model_class.where(session_token: @session_token).first 110 | end 111 | end 112 | end 113 | -------------------------------------------------------------------------------- /lib/authenticate/testing/controller_helpers.rb: -------------------------------------------------------------------------------- 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 29 | -------------------------------------------------------------------------------- /lib/authenticate/testing/integration_tests_sign_on.rb: -------------------------------------------------------------------------------- 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 73 | -------------------------------------------------------------------------------- /lib/authenticate/testing/rspec.rb: -------------------------------------------------------------------------------- 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 11 | -------------------------------------------------------------------------------- /lib/authenticate/testing/test_unit.rb: -------------------------------------------------------------------------------- 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 10 | -------------------------------------------------------------------------------- /lib/authenticate/testing/view_helpers.rb: -------------------------------------------------------------------------------- 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 30 | -------------------------------------------------------------------------------- /lib/authenticate/token.rb: -------------------------------------------------------------------------------- 1 | module Authenticate 2 | # 3 | # A secure token, consisting of a big random number. 4 | # 5 | class Token 6 | def self.new 7 | SecureRandom.hex(20).encode('UTF-8') 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/authenticate/user.rb: -------------------------------------------------------------------------------- 1 | require 'authenticate/configuration' 2 | require 'authenticate/token' 3 | require 'authenticate/callbacks/authenticatable' 4 | 5 | module Authenticate 6 | # Required to be included in your configured user class, which is `User` by 7 | # default, but can be changed with {Configuration#user_model=}. 8 | # 9 | # class User 10 | # include Authenticate::User 11 | # # ... 12 | # end 13 | # 14 | # To change the user class from the default User, assign it : 15 | # 16 | # Authenticate.configure do |config| 17 | # config.user_model = 'MyPackage::Gundan' 18 | # end 19 | # 20 | # The fields and methods included by Authenticate::User will depend on what modules you have included in your 21 | # configuration. When your user class is loaded, User will load any modules at that time. If you have another 22 | # initializer that loads User before Authenticate's initializer has run, this may cause interfere with the 23 | # configuration of your user. 24 | # 25 | # Every user will have two methods to manage session tokens: 26 | # - generate_session_token - generates and sets the Authenticate session token 27 | # - reset_session_token! - calls generate_session_token and save! immediately 28 | # 29 | # Every user will have these two class methods to normalize email addresses: 30 | # - normalize_email(email) - normalize the given email address by downcasing, removing spaces. 31 | # - find_by_normalized_email(email) - find a user by his/her normalized email address 32 | module User 33 | extend ActiveSupport::Concern 34 | 35 | included do 36 | include Modules 37 | load_modules 38 | end 39 | 40 | # Generate a new session token for the user, overwriting the existing session token, if any. 41 | # This is not automatically persisted; call {#reset_session_token!} to automatically 42 | # generate and persist the session token update. 43 | def generate_session_token 44 | self.session_token = Authenticate::Token.new 45 | end 46 | 47 | # Generate a new session token and persist the change, ignoring validations. 48 | # This effectively signs out all existing sessions. Called as part of logout. 49 | def reset_session_token! 50 | generate_session_token 51 | save validate: false 52 | end 53 | 54 | # Class methods added to users. 55 | module ClassMethods 56 | def normalize_email(email) 57 | email.to_s.downcase.gsub(/\s+/, '') 58 | end 59 | 60 | # We need to find users by email even if they don't use email to log in 61 | def find_by_normalized_email(email) 62 | find_by_email normalize_email(email) 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/authenticate/version.rb: -------------------------------------------------------------------------------- 1 | module Authenticate 2 | VERSION = '0.7.3'.freeze 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/authenticate/controllers/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Override the default authentication controllers and mailers. This generator will copy all of the 3 | base authenticate controllers and mailers into your project. 4 | 5 | Examples: 6 | rails generate authenticate:controllers 7 | 8 | View: app/controllers/authenticate/passwords_controller.rb 9 | View: app/controllers/authenticate/sessions_controller.rb 10 | View: app/controllers/authenticate/users_controller.rb 11 | View: app/mailers/authenticate_mailer.rb 12 | 13 | -------------------------------------------------------------------------------- /lib/generators/authenticate/controllers/controllers_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/base' 2 | 3 | # 4 | # deploy view and locale assets 5 | # 6 | module Authenticate 7 | module Generators 8 | class ControllersGenerator < Rails::Generators::Base 9 | source_root File.expand_path('../../../../..', __FILE__) 10 | 11 | def create_controllers 12 | directory 'app/controllers' 13 | end 14 | 15 | def create_mailers 16 | directory 'app/mailers' 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/generators/authenticate/helpers.rb: -------------------------------------------------------------------------------- 1 | module Authenticate 2 | module Generators 3 | module Helpers 4 | private 5 | 6 | # Either return the model passed in a classified form or return the default "User". 7 | def model_class_name 8 | options[:model] ? options[:model].classify : 'User' 9 | end 10 | 11 | def model_path 12 | @model_path ||= File.join('app', 'models', "#{file_path}.rb") 13 | end 14 | 15 | def file_path 16 | model_name.underscore 17 | end 18 | 19 | def namespace 20 | Rails::Generators.namespace if Rails::Generators.respond_to?(:namespace) 21 | end 22 | 23 | def namespaced? 24 | !namespace.nil? 25 | end 26 | 27 | def model_name 28 | if namespaced? 29 | [namespace.to_s] + [model_class_name] 30 | else 31 | [model_class_name] 32 | end.join('::') 33 | end 34 | 35 | def table_name 36 | @table_name ||= begin 37 | base = plural_name 38 | (class_path + [base]).join('_') 39 | end 40 | end 41 | 42 | def class_path 43 | @class_path 44 | end 45 | 46 | def singular_name 47 | @file_name 48 | end 49 | 50 | def plural_name 51 | singular_name.pluralize 52 | end 53 | 54 | def assign_names!(name) #:nodoc: 55 | @class_path = name.include?('/') ? name.split('/') : name.split('::') 56 | @class_path.map!(&:underscore) 57 | @file_name = @class_path.pop 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/generators/authenticate/install/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Explain the generator 3 | 4 | Example: 5 | rails generate authenticate:install 6 | 7 | This will create: 8 | -------------------------------------------------------------------------------- /lib/generators/authenticate/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/base' 2 | require 'rails/generators/active_record' 3 | require 'generators/authenticate/helpers' 4 | 5 | module Authenticate 6 | module Generators 7 | class InstallGenerator < Rails::Generators::Base 8 | include Rails::Generators::Migration 9 | include Authenticate::Generators::Helpers 10 | 11 | source_root File.expand_path('../templates', __FILE__) 12 | class_option :model, 13 | optional: true, 14 | type: :string, 15 | banner: 'model', 16 | desc: "Specify the model class name if you will use anything other than 'User'" 17 | 18 | class_option :allow_sign_up, 19 | optional: true, 20 | type: :boolean, 21 | banner: 'allow_sign_up', 22 | desc: 'Disable the sign up route' 23 | 24 | def initialize(*) 25 | super 26 | assign_names!(model_class_name) 27 | end 28 | 29 | def verify 30 | if options[:model] && !File.exist?(model_path) 31 | puts "Exiting: the model class you specified, #{options[:model]}, is not found." 32 | exit 1 33 | end 34 | end 35 | 36 | def create_or_inject_into_user_model 37 | if File.exist? model_path 38 | inject_into_class(model_path, model_class_name, " include Authenticate::User\n\n") 39 | else 40 | @model_base_class = model_base_class 41 | template 'user.rb.erb', 'app/models/user.rb' 42 | end 43 | end 44 | 45 | def create_authenticate_user_migration 46 | if users_table_exists? 47 | create_add_columns_migration 48 | else 49 | create_new_users_migration 50 | end 51 | end 52 | 53 | def copy_migration_files 54 | copy_migration 'add_authenticate_brute_force_to_users.rb' 55 | copy_migration 'add_authenticate_timeoutable_to_users.rb' 56 | copy_migration 'add_authenticate_password_reset_to_users.rb' 57 | end 58 | 59 | def inject_into_application_controller 60 | inject_into_class( 61 | 'app/controllers/application_controller.rb', 62 | ApplicationController, 63 | " include Authenticate::Controller\n\n" 64 | ) 65 | end 66 | 67 | def create_initializer 68 | copy_file 'authenticate.rb', 'config/initializers/authenticate.rb' 69 | if options[:model] 70 | inject_into_file( 71 | 'config/initializers/authenticate.rb', 72 | " config.user_model = '#{options[:model]}'\n", 73 | after: "Authenticate.configure do |config|\n" 74 | ) 75 | end 76 | 77 | if options.key? :allow_sign_up 78 | inject_into_file( 79 | 'config/initializers/authenticate.rb', 80 | " config.allow_sign_up = #{options['allow_sign_up']}\n", 81 | after: "Authenticate.configure do |config|\n" 82 | ) 83 | end 84 | end 85 | 86 | private 87 | 88 | def create_new_users_migration 89 | config = { 90 | new_columns: new_columns, 91 | new_indexes: new_indexes 92 | } 93 | copy_migration 'create_users.rb', config 94 | end 95 | 96 | def create_add_columns_migration 97 | if migration_needed? 98 | config = { 99 | new_columns: new_columns, 100 | new_indexes: new_indexes 101 | } 102 | copy_migration('add_authenticate_to_users.rb', config) 103 | end 104 | end 105 | 106 | def copy_migration(migration_name, config = {}) 107 | unless migration_exists?(migration_name) 108 | migration_template( 109 | "db/migrate/#{migration_name}", 110 | "db/migrate/#{migration_name}", 111 | config.merge(migration_version: migration_version) 112 | ) 113 | end 114 | end 115 | 116 | def migration_needed? 117 | new_columns.any? || new_indexes.any? 118 | end 119 | 120 | def new_columns 121 | @new_columns ||= { 122 | email: 't.string :email', 123 | encrypted_password: 't.string :encrypted_password, limit: 128', 124 | session_token: 't.string :session_token, limit: 128', 125 | 126 | # trackable, lifetimed 127 | current_sign_in_at: 't.datetime :current_sign_in_at', 128 | current_sign_in_ip: 't.string :current_sign_in_ip, limit: 128', 129 | last_sign_in_at: 't.datetime :last_sign_in_at', 130 | last_sign_in_ip: 't.string :last_sign_in_ip, limit: 128', 131 | sign_in_count: 't.integer :sign_in_count' 132 | }.reject { |column| existing_users_columns.include?(column.to_s) } 133 | end 134 | 135 | def new_indexes 136 | @new_indexes ||= { 137 | index_users_on_email: "add_index :#{table_name}, :email, unique: true", 138 | index_users_on_session_token: "add_index :#{table_name}, :session_token" 139 | }.reject { |index| existing_users_indexes.include?(index.to_s) } 140 | end 141 | 142 | def migration_exists?(name) 143 | existing_migrations.include?(name) 144 | end 145 | 146 | def existing_migrations 147 | @existing_migrations ||= Dir.glob('db/migrate/*.rb').map do |file| 148 | migration_name_without_timestamp(file) 149 | end 150 | end 151 | 152 | def migration_name_without_timestamp(file) 153 | file.sub(%r{^.*(db/migrate/)(?:\d+_)?}, '') 154 | end 155 | 156 | # def users_table_exists? 157 | # ActiveRecord::Base.connection.table_exists?(table_name) 158 | # end 159 | 160 | def users_table_exists? 161 | # Rails 5 uses 'data sources' 162 | if ActiveRecord::Base.connection.respond_to?(:data_source_exists?) 163 | ActiveRecord::Base.connection.data_source_exists?(table_name) 164 | else 165 | # Rails 4 uses 'tables' 166 | ActiveRecord::Base.connection.table_exists?(table_name) 167 | end 168 | end 169 | 170 | 171 | def existing_users_columns 172 | return [] unless users_table_exists? 173 | ActiveRecord::Base.connection.columns(table_name).map(&:name) 174 | end 175 | 176 | def existing_users_indexes 177 | return [] unless users_table_exists? 178 | ActiveRecord::Base.connection.indexes(table_name).map(&:name) 179 | end 180 | 181 | # for generating a timestamp when using `create_migration` 182 | def self.next_migration_number(dir) 183 | ActiveRecord::Generators::Base.next_migration_number(dir) 184 | end 185 | 186 | def model_base_class 187 | (Rails.version >= '5.0.0') ? 'ApplicationRecord' : 'ActiveRecord::Base' 188 | end 189 | 190 | def migration_version 191 | if Rails.version >= '5.0.0' 192 | "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" 193 | end 194 | end 195 | end 196 | end 197 | end 198 | -------------------------------------------------------------------------------- /lib/generators/authenticate/install/templates/authenticate.rb: -------------------------------------------------------------------------------- 1 | Authenticate.configure do |config| 2 | config.rotate_csrf_on_sign_in = true 3 | 4 | # config.user_model = 'User' 5 | # config.cookie_name = 'authenticate_session_token' 6 | # config.cookie_expiration = { 1.month.from_now.utc } 7 | # config.cookie_domain = nil 8 | # config.cookie_path = '/' 9 | # config.secure_cookie = false # set to true in production https environments 10 | # config.cookie_http_only = false # set to true if you can 11 | # config.mailer_sender = 'reply@example.com' 12 | # config.crypto_provider = Authenticate::Model::BCrypt 13 | # config.timeout_in = 45.minutes 14 | # config.max_session_lifetime = 8.hours 15 | # config.max_consecutive_bad_logins_allowed = 4 16 | # config.bad_login_lockout_period = 10.minutes 17 | # config.password_length = 8..128 18 | # config.authentication_strategy = :email 19 | # config.redirect_url = '/' 20 | # config.allow_sign_up = true 21 | # config.routes = true 22 | # config.reset_password_within = 2.days 23 | # config.modules = [] 24 | end 25 | -------------------------------------------------------------------------------- /lib/generators/authenticate/install/templates/db/migrate/add_authenticate_brute_force_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAuthenticateBruteForceToUsers < ActiveRecord::Migration<%= migration_version %> 2 | def change 3 | add_column :<%= table_name %>, :failed_logins_count, :integer, default: 0 4 | add_column :<%= table_name %>, :lock_expires_at, :datetime, default: nil 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/generators/authenticate/install/templates/db/migrate/add_authenticate_password_reset_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAuthenticatePasswordResetToUsers < ActiveRecord::Migration<%= migration_version %> 2 | def change 3 | add_column :<%= table_name %>, :password_reset_token, :string, default: nil 4 | add_column :<%= table_name %>, :password_reset_sent_at, :datetime, default: nil 5 | add_index :<%= table_name %>, :password_reset_token 6 | end 7 | end 8 | 9 | -------------------------------------------------------------------------------- /lib/generators/authenticate/install/templates/db/migrate/add_authenticate_timeoutable_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAuthenticateTimeoutableToUsers < ActiveRecord::Migration<%= migration_version %> 2 | def change 3 | add_column :<%= table_name %>, :last_access_at, :datetime, default: nil 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/authenticate/install/templates/db/migrate/add_authenticate_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAuthenticateToUsers < ActiveRecord::Migration<%= migration_version %> 2 | def self.up 3 | change_table :<%= table_name %> do |t| 4 | <% config[:new_columns].values.each do |column| -%> 5 | <%= column %> 6 | <% end -%> 7 | end 8 | 9 | <% config[:new_indexes].values.each do |index| -%> 10 | <%= index %> 11 | <% end -%> 12 | end 13 | 14 | def self.down 15 | change_table :<%= table_name %> do |t| 16 | <% if config[:new_columns].any? -%> 17 | t.remove <%= new_columns.keys.map { |column| ":#{column}" }.join(", ") %> 18 | <% end -%> 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/generators/authenticate/install/templates/db/migrate/create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration<%= migration_version %> 2 | def change 3 | create_table :<%= table_name %> do |t| 4 | <% config[:new_columns].values.each do |column| -%> 5 | <%= column %> 6 | <% end -%> 7 | end 8 | 9 | <% config[:new_indexes].values.each do |index| -%> 10 | <%= index %> 11 | <% end -%> 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/generators/authenticate/install/templates/user.rb.erb: -------------------------------------------------------------------------------- 1 | class User < <%= @model_base_class %> 2 | include Authenticate::User 3 | end 4 | -------------------------------------------------------------------------------- /lib/generators/authenticate/routes/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Inject the authenticate routes into your `config/routes.rb`. Also turns off authenticate's built-in routes 3 | by adding `config.routes = false` into your `config/initializers/authenticate.rb`. 4 | 5 | Examples: 6 | rails generate authenticate:routes 7 | 8 | 9 | -------------------------------------------------------------------------------- /lib/generators/authenticate/routes/routes_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/base' 2 | require 'generators/authenticate/helpers' 3 | 4 | module Authenticate 5 | module Generators 6 | class RoutesGenerator < Rails::Generators::Base 7 | include Authenticate::Generators::Helpers 8 | 9 | source_root File.expand_path('../templates', __FILE__) 10 | 11 | def add_authenticate_routes 12 | route(authenticate_routes) 13 | end 14 | 15 | def disable_authenticate_internal_routes 16 | inject_into_file( 17 | 'config/initializers/authenticate.rb', 18 | " config.routes = false \n", 19 | after: "Authenticate.configure do |config|\n" 20 | ) 21 | end 22 | 23 | private 24 | 25 | def authenticate_routes 26 | @user_model = Authenticate.configuration.user_model_route_key 27 | ERB.new(File.read(routes_file_path)).result(binding) 28 | end 29 | 30 | def routes_file_path 31 | File.expand_path(find_in_source_paths('routes.rb')) 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /lib/generators/authenticate/routes/templates/routes.rb: -------------------------------------------------------------------------------- 1 | resource :session, controller: 'authenticate/sessions', only: [:create, :new, :destroy] 2 | resources :passwords, controller: 'authenticate/passwords', only: [:new, :create] 3 | 4 | resource :<%= @user_model %>, controller: 'authenticate/users', only: [:new, :create] do 5 | resources :passwords, controller: 'authenticate/passwords', only: [:edit, :update] 6 | end 7 | 8 | get '/sign_up', to: 'authenticate/users#new', as: 'sign_up' 9 | get '/sign_in', to: 'authenticate/sessions#new', as: 'sign_in' 10 | get '/sign_out', to: 'authenticate/sessions#destroy', as: 'sign_out' 11 | -------------------------------------------------------------------------------- /lib/generators/authenticate/views/USAGE: -------------------------------------------------------------------------------- 1 | Description: 2 | Override the default authenticate views and locale file. This generator will copy all of the 3 | base authenticate views and locale file into your project. 4 | 5 | Examples: 6 | rails generate authenticate:views 7 | 8 | View: app/views/authenticate_mailer/change_password.html.erb 9 | View: app/views/layouts/application.html.erb 10 | View: app/views/passwords/edit.html.erb 11 | View: app/views/passwords/new.html.erb 12 | View: app/views/sessions/new.html.erb 13 | View: app/views/users/new.html.erb 14 | Locale: config/locales/en.yml 15 | -------------------------------------------------------------------------------- /lib/generators/authenticate/views/views_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/base' 2 | 3 | # 4 | # deploy view and locale assets 5 | # 6 | module Authenticate 7 | module Generators 8 | class ViewsGenerator < Rails::Generators::Base 9 | source_root File.expand_path('../../../../..', __FILE__) 10 | 11 | def create_views 12 | directory 'app/views' 13 | end 14 | 15 | def create_locales 16 | directory 'config/locales' 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/tasks/authenticate_tasks.rake: -------------------------------------------------------------------------------- 1 | # desc "Explaining what the task does" 2 | # task :authenticate do 3 | # # Task goes here 4 | # end 5 | -------------------------------------------------------------------------------- /spec/controllers/deprecated_controller_methods_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Matcher that asserts user was denied access. 4 | RSpec::Matchers.define :deny_access do 5 | match do |controller| 6 | redirects_to_sign_in?(controller) && sets_flash?(controller) 7 | end 8 | 9 | def redirects_to_sign_in?(controller) 10 | expect(controller).to redirect_to(controller.sign_in_url) 11 | end 12 | 13 | def sets_flash?(controller) 14 | controller.flash[:notice].match(/sign in to continue/) 15 | end 16 | end 17 | 18 | # A dummy 'secured' controller to test 19 | class DeprecatedMethodsController < ActionController::Base 20 | include Authenticate::Controller 21 | before_action :require_authentication, only: :show 22 | 23 | def new 24 | head :ok 25 | end 26 | 27 | def show 28 | head :ok 29 | end 30 | end 31 | 32 | describe DeprecatedMethodsController, type: :controller do 33 | before do 34 | Rails.application.routes.draw do 35 | resource :deprecated_methods, only: [:new, :show] 36 | get '/sign_in' => 'authenticate/sessions#new', as: 'sign_in' 37 | end 38 | end 39 | 40 | after do 41 | Rails.application.reload_routes! 42 | end 43 | 44 | context 'with authenticated user' do 45 | before { sign_in } 46 | 47 | it 'warns but allows access to show' do 48 | expect { do_get :show }.to output(/deprecated/i).to_stderr 49 | expect(subject).to_not deny_access 50 | end 51 | 52 | it 'warns on authenticated?' do 53 | expect { subject.authenticated? }.to output(/deprecated/i).to_stderr 54 | end 55 | 56 | it 'authenticates' do 57 | silence do 58 | expect(subject.authenticated?).to be_truthy 59 | end 60 | end 61 | end 62 | end 63 | 64 | 65 | def silence 66 | # Store the original stderr and stdout in order to restore them later 67 | @original_stderr = $stderr 68 | @original_stdout = $stdout 69 | 70 | # Redirect stderr and stdout 71 | $stderr = $stdout = StringIO.new 72 | 73 | yield 74 | 75 | $stderr = @original_stderr 76 | $stdout = @original_stdout 77 | @original_stderr = nil 78 | @original_stdout = nil 79 | end -------------------------------------------------------------------------------- /spec/controllers/secured_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | # Matcher that asserts user was denied access. 4 | RSpec::Matchers.define :deny_access do 5 | match do |controller| 6 | redirects_to_sign_in?(controller) && sets_flash?(controller) 7 | end 8 | 9 | def redirects_to_sign_in?(controller) 10 | expect(controller).to redirect_to(controller.sign_in_url) 11 | end 12 | 13 | def sets_flash?(controller) 14 | controller.flash[:notice].match(/sign in to continue/) 15 | end 16 | end 17 | 18 | # A dummy 'secured' controller to test 19 | class SecuredAppsController < ActionController::Base 20 | include Authenticate::Controller 21 | before_action :require_login, only: :show 22 | 23 | def new 24 | head :ok 25 | end 26 | 27 | def show 28 | head :ok 29 | end 30 | end 31 | 32 | describe SecuredAppsController, type: :controller do 33 | before do 34 | Rails.application.routes.draw do 35 | resource :secured_app, only: [:new, :show] 36 | get '/sign_in' => 'authenticate/sessions#new', as: 'sign_in' 37 | end 38 | end 39 | 40 | after do 41 | Rails.application.reload_routes! 42 | end 43 | 44 | context 'with authenticated user' do 45 | before { sign_in } 46 | 47 | it 'allows access to new' do 48 | do_get :new 49 | expect(subject).to_not deny_access 50 | end 51 | 52 | it 'allows access to show' do 53 | do_get :show 54 | expect(subject).to_not deny_access 55 | end 56 | end 57 | 58 | context 'with an unauthenticated visitor' do 59 | it 'allows access to new' do 60 | do_get :new 61 | expect(subject).to_not deny_access 62 | end 63 | 64 | it 'denies access to show' do 65 | do_get :show 66 | expect(subject).to deny_access 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /spec/dummy/README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | 26 | 27 | Please feel free to use a different markup language if you do not plan to run 28 | rake doc:app. 29 | -------------------------------------------------------------------------------- /spec/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomichj/authenticate/f4cdcbc6e42886394a440182f84ce7fa80cd714a/spec/dummy/app/assets/images/.keep -------------------------------------------------------------------------------- /spec/dummy/app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require_tree . 14 | -------------------------------------------------------------------------------- /spec/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any styles 10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new 11 | * file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include Authenticate::Controller 3 | # before_action :require_authentication 4 | before_action :require_login 5 | 6 | # Prevent CSRF attacks by raising an exception. 7 | # For APIs, you may want to use :null_session instead. 8 | protect_from_forgery with: :exception 9 | end 10 | -------------------------------------------------------------------------------- /spec/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomichj/authenticate/f4cdcbc6e42886394a440182f84ce7fa80cd714a/spec/dummy/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /spec/dummy/app/controllers/welcome_controller.rb: -------------------------------------------------------------------------------- 1 | class WelcomeController < ApplicationController 2 | def index 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /spec/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /spec/dummy/app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomichj/authenticate/f4cdcbc6e42886394a440182f84ce7fa80cd714a/spec/dummy/app/mailers/.keep -------------------------------------------------------------------------------- /spec/dummy/app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomichj/authenticate/f4cdcbc6e42886394a440182f84ce7fa80cd714a/spec/dummy/app/models/.keep -------------------------------------------------------------------------------- /spec/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomichj/authenticate/f4cdcbc6e42886394a440182f84ce7fa80cd714a/spec/dummy/app/models/concerns/.keep -------------------------------------------------------------------------------- /spec/dummy/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | include Authenticate::User 3 | end 4 | -------------------------------------------------------------------------------- /spec/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |Find me in app/views/welcome/index.html.erb
3 | 4 | <%= link_to "Sign out", sign_out_path %> 5 | -------------------------------------------------------------------------------- /spec/dummy/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /spec/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../../config/application', __FILE__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /spec/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /spec/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /spec/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /spec/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | Bundler.require(*Rails.groups) 6 | require "authenticate" 7 | 8 | module Dummy 9 | class Application < Rails::Application 10 | # Settings in config/environments/* take precedence over those specified here. 11 | # Application configuration should go into files in config/initializers 12 | # -- all .rb files in that directory are automatically loaded. 13 | 14 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 15 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 16 | # config.time_zone = 'Central Time (US & Canada)' 17 | 18 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 19 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 20 | # config.i18n.default_locale = :de 21 | 22 | # Do not swallow errors in after_commit/after_rollback callbacks. 23 | # config.active_record.raise_in_transactional_callbacks = true 24 | 25 | end 26 | end 27 | 28 | Rails.application.routes.default_url_options[:host] = 'localhost:3000' 29 | 30 | if Rails.application.config.active_record.sqlite3.respond_to? :represent_boolean_as_integer 31 | Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true 32 | end 33 | 34 | -------------------------------------------------------------------------------- /spec/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | $LOAD_PATH.unshift File.expand_path('../../../../lib', __FILE__) 6 | -------------------------------------------------------------------------------- /spec/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /spec/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger. 20 | config.active_support.deprecation = :log 21 | 22 | # Raise an error on page load if there are pending migrations. 23 | config.active_record.migration_error = :page_load 24 | 25 | # Debug mode disables concatenation and preprocessing of assets. 26 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | 30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 31 | # yet still be able to expire them through the digest params. 32 | config.assets.digest = true 33 | 34 | # Adds additional error checking when serving assets at runtime. 35 | # Checks for improperly declared sprockets dependencies. 36 | # Raises helpful error messages. 37 | config.assets.raise_runtime_errors = true 38 | 39 | # Raises error for missing translations 40 | # config.action_view.raise_on_missing_translations = true 41 | end 42 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 35 | # yet still be able to expire them through the digest params. 36 | config.assets.digest = true 37 | 38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | # config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :debug 50 | 51 | # Prepend all log lines with the following tags. 52 | # config.log_tags = [ :subdomain, :uuid ] 53 | 54 | # Use a different logger for distributed setups. 55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 61 | # config.action_controller.asset_host = 'http://assets.example.com' 62 | 63 | # Ignore bad email addresses and do not raise email delivery errors. 64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 65 | # config.action_mailer.raise_delivery_errors = false 66 | 67 | 68 | 69 | 70 | # Tell Action Mailer not to deliver emails to the real world. 71 | # The :test delivery method accumulates sent emails in the 72 | # ActionMailer::Base.deliveries array. 73 | config.action_mailer.delivery_method = :test 74 | 75 | 76 | 77 | 78 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 79 | # the I18n.default_locale when a translation cannot be found). 80 | config.i18n.fallbacks = true 81 | 82 | # Send deprecation notices to registered listeners. 83 | config.active_support.deprecation = :notify 84 | 85 | # Use default logging formatter so that PID and timestamp are not suppressed. 86 | config.log_formatter = ::Logger::Formatter.new 87 | 88 | # Do not dump schema after migrations. 89 | config.active_record.dump_schema_after_migration = false 90 | end 91 | 92 | -------------------------------------------------------------------------------- /spec/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static file server for tests with Cache-Control for performance. 16 | # config.serve_static_files = true 17 | # config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | config.action_mailer.perform_deliveries = true 34 | 35 | # Randomize the order test cases are executed. 36 | config.active_support.test_order = :random 37 | 38 | # Print deprecation notices to the stderr. 39 | config.active_support.deprecation = :stderr 40 | 41 | # Raises error for missing translations 42 | # config.action_view.raise_on_missing_translations = true 43 | end 44 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/authenticate.rb: -------------------------------------------------------------------------------- 1 | Authenticate.configure do |config| 2 | config.timeout_in = 45.minutes 3 | config.max_session_lifetime = 20.minutes 4 | config.max_consecutive_bad_logins_allowed = 2 5 | config.bad_login_lockout_period = 10.minutes 6 | config.reset_password_within = 5.minutes 7 | config.password_length = 8..128 8 | config.debug = true 9 | end 10 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_dummy_session' 4 | -------------------------------------------------------------------------------- /spec/dummy/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /spec/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /spec/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | resources :welcome, only: [:index] 3 | root 'welcome#index' 4 | 5 | # The priority is based upon order of creation: first created -> highest priority. 6 | # See how all your routes lay out with "rake routes". 7 | 8 | # You can have the root of your site routed with "root" 9 | # root 'welcome#index' 10 | 11 | # Example of regular route: 12 | # get 'products/:id' => 'catalog#view' 13 | 14 | # Example of named route that can be invoked with purchase_url(id: product.id) 15 | # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase 16 | 17 | # Example resource route (maps HTTP verbs to controller actions automatically): 18 | # resources :products 19 | 20 | # Example resource route with options: 21 | # resources :products do 22 | # member do 23 | # get 'short' 24 | # post 'toggle' 25 | # end 26 | # 27 | # collection do 28 | # get 'sold' 29 | # end 30 | # end 31 | 32 | # Example resource route with sub-resources: 33 | # resources :products do 34 | # resources :comments, :sales 35 | # resource :seller 36 | # end 37 | 38 | # Example resource route with more complex sub-resources: 39 | # resources :products do 40 | # resources :comments 41 | # resources :sales do 42 | # get 'recent', on: :collection 43 | # end 44 | # end 45 | 46 | # Example resource route with concerns: 47 | # concern :toggleable do 48 | # post 'toggle' 49 | # end 50 | # resources :posts, concerns: :toggleable 51 | # resources :photos, concerns: :toggleable 52 | 53 | # Example resource route within a namespace: 54 | # namespace :admin do 55 | # # Directs /admin/products/* to Admin::ProductsController 56 | # # (app/controllers/admin/products_controller.rb) 57 | # resources :products 58 | # end 59 | end 60 | -------------------------------------------------------------------------------- /spec/dummy/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 8b743fc5fc1621f167b724fc5743a8bc2879babd89d3d378be441e688a3ae4ed3833db2d1d7236a155648b04973ab9513b42ce2bcf7b6107417fc269f7f2a93c 15 | 16 | test: 17 | secret_key_base: cba9ecd3512c1e69ab918b88a57a5ebaaaea6c35ad3347f43cba5c965733138c20b4ef9a33d59747eef26da8f95615ec418b5a12c2c433abd3bbdf4af7e5906c 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /spec/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20160130192731) do 15 | 16 | create_table "users", force: :cascade do |t| 17 | t.string "email" 18 | t.string "encrypted_password", limit: 128 19 | t.string "session_token", limit: 128 20 | t.datetime "current_sign_in_at" 21 | t.string "current_sign_in_ip", limit: 128 22 | t.datetime "last_sign_in_at" 23 | t.string "last_sign_in_ip", limit: 128 24 | t.integer "sign_in_count" 25 | t.integer "failed_logins_count", default: 0 26 | t.datetime "lock_expires_at" 27 | t.datetime "last_access_at" 28 | t.string "password_reset_token" 29 | t.datetime "password_reset_sent_at" 30 | end 31 | 32 | add_index "users", ["email"], name: "index_users_on_email", unique: true 33 | add_index "users", ["session_token"], name: "index_users_on_session_token" 34 | 35 | end 36 | -------------------------------------------------------------------------------- /spec/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomichj/authenticate/f4cdcbc6e42886394a440182f84ce7fa80cd714a/spec/dummy/lib/assets/.keep -------------------------------------------------------------------------------- /spec/dummy/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomichj/authenticate/f4cdcbc6e42886394a440182f84ce7fa80cd714a/spec/dummy/log/.keep -------------------------------------------------------------------------------- /spec/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |You may have mistyped the address or the page may have moved.
63 |If you are the application owner check the logs for more information.
65 |Maybe you tried to change something you didn't have access to.
63 |If you are the application owner check the logs for more information.
65 |If you are the application owner check the logs for more information.
64 |