devise-secure_password 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Dockerfile +44 -0
- data/Dockerfile.prev +44 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +280 -0
- data/LICENSE.txt +21 -0
- data/README.md +326 -0
- data/Rakefile +11 -0
- data/app/controllers/devise/passwords_with_policy_controller.rb +52 -0
- data/app/views/devise/passwords_with_policy/edit.html.erb +16 -0
- data/bin/console +14 -0
- data/bin/setup +6 -0
- data/config/locales/en.yml +71 -0
- data/devise-secure_password.gemspec +57 -0
- data/docker-entrypoint.sh +6 -0
- data/gemfiles/rails-5_0_6.gemfile +17 -0
- data/gemfiles/rails-5_1_4.gemfile +16 -0
- data/lib/devise/secure_password.rb +70 -0
- data/lib/devise/secure_password/controllers/active_helpers.rb +40 -0
- data/lib/devise/secure_password/controllers/devise_helpers.rb +64 -0
- data/lib/devise/secure_password/hooks/password_requires_regular_updates.rb +5 -0
- data/lib/devise/secure_password/models/password_disallows_frequent_changes.rb +60 -0
- data/lib/devise/secure_password/models/password_disallows_frequent_reuse.rb +71 -0
- data/lib/devise/secure_password/models/password_has_required_content.rb +131 -0
- data/lib/devise/secure_password/models/password_requires_regular_updates.rb +56 -0
- data/lib/devise/secure_password/models/previous_password.rb +20 -0
- data/lib/devise/secure_password/routes.rb +11 -0
- data/lib/devise/secure_password/version.rb +5 -0
- data/lib/generators/devise/secure_password/install_generator.rb +30 -0
- data/lib/generators/devise/templates/README.txt +21 -0
- data/lib/generators/devise/templates/secure_password.rb +43 -0
- data/lib/support/string/character_counter.rb +53 -0
- data/pkg/devise-secure_password-1.0.0.gem +0 -0
- metadata +471 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017-2018, Valimail, Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,326 @@
|
|
1
|
+
# Devise Secure Password Extension
|
2
|
+
|
3
|
+
[](#license)
|
4
|
+
|
5
|
+
The __Devise Secure Password Extension__ is a user account password policy enforcement gem that can be
|
6
|
+
added to a Rails project to enforce password policies. The gem is implemented as an extension to the Rails
|
7
|
+
[devise](https://github.com/plataformatec/devise) authentication solution gem and requires that __devise__ is installed
|
8
|
+
as well.
|
9
|
+
|
10
|
+
## Build Status
|
11
|
+
|
12
|
+
| Service | rails 5.1.4 |
|
13
|
+
|:-----------|:-----------:|
|
14
|
+
| Circle CI | []() |
|
15
|
+
|
16
|
+
## Overview
|
17
|
+
|
18
|
+
The __Devise Secure Password Extension__ is composed of the following modules:
|
19
|
+
|
20
|
+
- __password_has_required_content__: require that passwords consist of a specific number (configurable) of letters,
|
21
|
+
numbers, and special characters (symbols)
|
22
|
+
- __password_disallows_frequent_reuse__: prevent the reuse of a number (configurable) of previous passwords when a user
|
23
|
+
changes their password
|
24
|
+
- __password_disallows_frequent_changes__: prevent the user from changing their password more than once within a time
|
25
|
+
duration (configurable)
|
26
|
+
- __password_requires_regular_updates__: require that a user change their password following a time duration
|
27
|
+
(configurable)
|
28
|
+
|
29
|
+
## Compatibility
|
30
|
+
|
31
|
+
The goal of this project is to provide compatibility for officially supported stable releases of [Ruby](https://www.ruby-lang.org/en/downloads/)
|
32
|
+
and [Ruby on Rails](http://guides.rubyonrails.org/maintenance_policy.html). More specifically, the following releases
|
33
|
+
are currently supported by the __Devise Secure Password Extension__:
|
34
|
+
|
35
|
+
- Ruby on Rails: __5.1.Z__, __5.0.Z__ (current and previous stable release)
|
36
|
+
- Ruby: __2.5.0__, __2.4.3__ (current and previous stable release)
|
37
|
+
|
38
|
+
## Installation
|
39
|
+
|
40
|
+
Add this line to your application's Gemfile:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
gem 'devise', '~> 4.2'
|
44
|
+
gem 'devise-secure_password', '~> 1.0.0'
|
45
|
+
```
|
46
|
+
|
47
|
+
And then execute:
|
48
|
+
|
49
|
+
```shell
|
50
|
+
prompt> bundle
|
51
|
+
```
|
52
|
+
|
53
|
+
Or install it yourself as:
|
54
|
+
|
55
|
+
```shell
|
56
|
+
prompt> gem install devise-secure_password
|
57
|
+
```
|
58
|
+
|
59
|
+
Finally, run the generator:
|
60
|
+
|
61
|
+
```shell
|
62
|
+
prompt> rails generate devise:secure_password:install
|
63
|
+
```
|
64
|
+
|
65
|
+
## Usage
|
66
|
+
|
67
|
+
### Configuration
|
68
|
+
|
69
|
+
The __Devise Secure Password Extension__ exposes configuration parameters as outlined below. Commented out configuration
|
70
|
+
parameters reflect the default settings.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
Devise.setup do |config|
|
74
|
+
# ==> Configuration for the Devise Secure Password extension
|
75
|
+
# Module: password_has_required_content
|
76
|
+
#
|
77
|
+
# Configure password content requirements including the number of uppercase,
|
78
|
+
# lowercase, number, and special characters that are required. To configure the
|
79
|
+
# minimum and maximum length refer to the Devise config.password_length
|
80
|
+
# standard configuration parameter.
|
81
|
+
|
82
|
+
# Passwords consist of at least one uppercase letter (latin A-Z):
|
83
|
+
# config.password_required_uppercase_count = 1
|
84
|
+
|
85
|
+
# Passwords consist of at least one lowercase characters (latin a-z):
|
86
|
+
# config.password_required_lowercase_count = 1
|
87
|
+
|
88
|
+
# Passwords consist of at least one number (0-9):
|
89
|
+
# config.password_required_number_count = 1
|
90
|
+
|
91
|
+
# Passwords consist of at least one special character (!@#$%^&*()_+-=[]{}|'):
|
92
|
+
# config.password_required_special_character_count = 1
|
93
|
+
|
94
|
+
# ==> Configuration for the Devise Secure Password extension
|
95
|
+
# Module: password_disallows_frequent_reuse
|
96
|
+
#
|
97
|
+
# Passwords cannot be reused. A user's last 24 password hashes are saved:
|
98
|
+
# config.password_previously_used_count = 24
|
99
|
+
|
100
|
+
# ==> Configuration for the Devise Secure Password extension
|
101
|
+
# Module: password_disallows_frequent_changes
|
102
|
+
# *Requires* password_disallows_frequent_reuse
|
103
|
+
#
|
104
|
+
# Passwords cannot be changed more frequently than once per day:
|
105
|
+
# config.password_minimum_age = 1.day
|
106
|
+
|
107
|
+
# ==> Configuration for the Devise Secure Password extension
|
108
|
+
# Module: password_requires_regular_updates
|
109
|
+
# *Requires* password_disallows_frequent_reuse
|
110
|
+
#
|
111
|
+
# Passwords must be changed every 60 days:
|
112
|
+
# config.password_maximum_age = 60.days
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
Enable the __Devise Secure Password Extension__ enforcement in your Devise model(s):
|
117
|
+
|
118
|
+
```ruby
|
119
|
+
devise :password_has_required_content, :password_disallows_frequent_reuse,
|
120
|
+
:password_disallows_frequent_changes, :password_requires_regular_updates
|
121
|
+
```
|
122
|
+
|
123
|
+
Usually, you would append these after your selection of Devise modules. So your configuration will more likely look like
|
124
|
+
the following:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
class User < ApplicationRecord
|
128
|
+
# Include default devise modules. Others available are:
|
129
|
+
# :confirmable, :lockable, :timeoutable and :omniauthable
|
130
|
+
devise :database_authenticatable, :registerable,
|
131
|
+
:recoverable, :rememberable, :trackable, :validatable,
|
132
|
+
:password_has_required_content, :password_disallows_frequent_reuse,
|
133
|
+
:password_disallows_frequent_changes, :password_requires_regular_updates
|
134
|
+
...
|
135
|
+
<YOUR USER MODEL CONTENT FOLLOWS>
|
136
|
+
end
|
137
|
+
```
|
138
|
+
|
139
|
+
>NOTE: Both `:password_disallows_frequent_changes` and `:password_requires_regular_updates` are dependent upon the
|
140
|
+
previous passwords memorization implemented by the `:password_disallows_frequent_reuse` module.
|
141
|
+
|
142
|
+
### Database migration
|
143
|
+
|
144
|
+
The following database migration needs to be applied:
|
145
|
+
|
146
|
+
```shell
|
147
|
+
prompt> rails generate migration create_previous_passwords salt:string encrypted_password:string user:references
|
148
|
+
```
|
149
|
+
|
150
|
+
Edit the resulting file to disallow null values for the hash and to add indexes for both hash and user_id fields:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
class CreatePreviousPasswords < ActiveRecord::Migration[5.1]
|
154
|
+
def change
|
155
|
+
create_table :previous_passwords do |t|
|
156
|
+
t.string :salt, null: false
|
157
|
+
t.string :encrypted_password, null: false
|
158
|
+
t.references :user, foreign_key: true
|
159
|
+
|
160
|
+
t.timestamps
|
161
|
+
end
|
162
|
+
|
163
|
+
add_index :previous_passwords, :encrypted_password
|
164
|
+
add_index :previous_passwords, [:user_id, :created_at]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
```
|
169
|
+
|
170
|
+
And then:
|
171
|
+
|
172
|
+
```shell
|
173
|
+
prompt> bundle exec rake db:migrate
|
174
|
+
```
|
175
|
+
|
176
|
+
<a name="running-tests"></a>
|
177
|
+
|
178
|
+
## Running Tests
|
179
|
+
|
180
|
+
This document assumes that you already have a [functioning ruby install](https://rvm.io/).
|
181
|
+
|
182
|
+
### Default Rails target
|
183
|
+
|
184
|
+
The __Devise Secure Password Extension__ provides compatibility for officially supported stable releases of Ruby on
|
185
|
+
Rails. To configure and test the default target (the most-recent supported Rails release):
|
186
|
+
|
187
|
+
```bash
|
188
|
+
prompt> bundle
|
189
|
+
prompt> bundle exec rake
|
190
|
+
```
|
191
|
+
|
192
|
+
### Selecting an alternate Rails target
|
193
|
+
|
194
|
+
To determine the Ruby on Rails versions supported by this release, run the following commands:
|
195
|
+
|
196
|
+
```bash
|
197
|
+
prompt> gem install flay ruby2ruby rubucop rspec
|
198
|
+
prompt> rake test:spec:targets
|
199
|
+
|
200
|
+
Available Rails targets: 5.0.6, 5.1.4
|
201
|
+
```
|
202
|
+
|
203
|
+
Reconfigure the project by specifying the correct Gemfile when running bundler, followed by running tests:
|
204
|
+
|
205
|
+
```bash
|
206
|
+
prompt> BUNDLE_GEMFILE=gemfiles/rails-5_0_6.gemfile bundle
|
207
|
+
prompt> BUNDLE_GEMFILE=gemfiles/rails-5_0_6.gemfile bundle exec rake
|
208
|
+
```
|
209
|
+
|
210
|
+
The only time you need to define the `BUNDLE_GEMFILE` environment variable is when testing a non-default target.
|
211
|
+
|
212
|
+
### Testing with code coverage (SimpleCov)
|
213
|
+
|
214
|
+
SimpleCov tests are enabled by defining the `test:spec:coverage` rake task:
|
215
|
+
|
216
|
+
```bash
|
217
|
+
prompt> bundle exec rake test:spec:coverage
|
218
|
+
```
|
219
|
+
|
220
|
+
A brief summary will be output at the end of the run but a more extensive eport will be saved in the `coverage`
|
221
|
+
directory (under the top-level project directory).
|
222
|
+
|
223
|
+
### Testing with headless Chrome
|
224
|
+
|
225
|
+
You will need to install the [ChromeDriver >= v2.3.4](https://sites.google.com/a/chromium.org/chromedriver/downloads)
|
226
|
+
for testing.
|
227
|
+
|
228
|
+
```bash
|
229
|
+
prompt> brew install chromedriver
|
230
|
+
```
|
231
|
+
|
232
|
+
>NOTE: __ChromeDriver__ < 2.33 has a bug for testing clickable targets; therefore, install >= 2.3.4.
|
233
|
+
|
234
|
+
You can always install [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/) by downloading and then
|
235
|
+
unpacking into the `/usr/local/bin` directory.
|
236
|
+
|
237
|
+
### Testing inside the spec/rails-app-X_y_z
|
238
|
+
|
239
|
+
To debug from inside of the dummy rails-app you will need to first install the rails bin stubs and then perform a db
|
240
|
+
migration:
|
241
|
+
|
242
|
+
```bash
|
243
|
+
prompt> cd spec/rails-app-X_y_z
|
244
|
+
prompt> rake app:update:bin
|
245
|
+
prompt> RAILS_ENV=development bundle exec rake db:migrate
|
246
|
+
```
|
247
|
+
|
248
|
+
Remember, the dummy app is not meant to be a full featured rails app: there is just enough functionality to test the
|
249
|
+
gem feature set.
|
250
|
+
|
251
|
+
### Running benchmarks
|
252
|
+
|
253
|
+
Available benchmarks can be run as follows:
|
254
|
+
|
255
|
+
```bash
|
256
|
+
prompt> bundle exec rake test:benchmark
|
257
|
+
```
|
258
|
+
|
259
|
+
Benchmarks are run within an RSpec context but are not run along with other tests as benchmarks merely seek to measure
|
260
|
+
performance and not enforce set performance targets.
|
261
|
+
|
262
|
+
### Screenshots
|
263
|
+
|
264
|
+
Failing tests that invoke the JavaScript driver will result in both the failing html along with a screenshot of the
|
265
|
+
page output to be saved in the `spec/rails-app-X_y_z/tmp/capybara` snapshot directory.
|
266
|
+
|
267
|
+
>NOTE: On __circleci__ the snapshots will be captured as artifacts.
|
268
|
+
|
269
|
+
The snapshot directory will be pruned automatically between runs.
|
270
|
+
|
271
|
+
## Docker
|
272
|
+
|
273
|
+
This repository includes a [Dockerfile](https://docs.docker.com/engine/reference/builder/) to facilitate testing in and
|
274
|
+
using [Docker](https://www.docker.com/).
|
275
|
+
|
276
|
+
To start the container simply build and launch the image:
|
277
|
+
|
278
|
+
```bash
|
279
|
+
prompt> docker build -t secure-password-dev .
|
280
|
+
prompt> docker run -it --rm secure-password-dev /bin/bash
|
281
|
+
```
|
282
|
+
|
283
|
+
The above `docker run` command will start the container, connect you to the command line within the project home
|
284
|
+
directory where you can issue the tests as documented in the [Running Tests](#running-tests) section above. When you exit
|
285
|
+
the shell, the container will be removed.
|
286
|
+
|
287
|
+
### Running tests in a Docker container
|
288
|
+
|
289
|
+
The Docker container is derived from the latest [circleci/ruby](https://hub.docker.com/r/circleci/ruby/) image. It is
|
290
|
+
critical that you update the bundler inside of the Docker image as the `circleci` user (i.e. the default user) before
|
291
|
+
initiating any development work including tests.
|
292
|
+
|
293
|
+
```bash
|
294
|
+
prompt> gem update bundler
|
295
|
+
```
|
296
|
+
|
297
|
+
## Contributing
|
298
|
+
|
299
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/valimail/devise-secure_password. This project
|
300
|
+
is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the
|
301
|
+
[Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
302
|
+
|
303
|
+
### Basic guidelines for contributors
|
304
|
+
|
305
|
+
1 Fork it
|
306
|
+
|
307
|
+
2 Create your feature branch (`git checkout -b my-new-feature`)
|
308
|
+
|
309
|
+
3 Commit your changes (`git commit -am 'Add some feature'`)
|
310
|
+
|
311
|
+
4 Push to the branch (`git push origin my-new-feature`)
|
312
|
+
|
313
|
+
5 Create new Pull Request
|
314
|
+
|
315
|
+
>NOTE: Contributions should always be based on the `master` branch. You may be asked to [rebase](https://git-scm.com/docs/git-rebase)
|
316
|
+
your contributions on the tip of the `master` branch, this is normal and is to be expected if the `master` branch has
|
317
|
+
moved ahead since your pull request was opened, discussed, and accepted.
|
318
|
+
|
319
|
+
## License
|
320
|
+
|
321
|
+
The __Devise Secure Password Extension__ gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
322
|
+
|
323
|
+
## Code of Conduct
|
324
|
+
|
325
|
+
Everyone interacting in the __Devise Secure Password Extension__ project’s codebases and issue trackers is expected to
|
326
|
+
follow the [code of conduct](https://github.com/valimail/devise-secure_password/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Devise
|
2
|
+
class PasswordsWithPolicyController < DeviseController
|
3
|
+
before_action :authenticate_scope!
|
4
|
+
|
5
|
+
def edit
|
6
|
+
self.resource = resource_class.new
|
7
|
+
if warden.session(scope_name)['secure_password_expired']
|
8
|
+
resource.errors.add(:base, "#{error_string_for_password_expired}.")
|
9
|
+
end
|
10
|
+
render :edit
|
11
|
+
end
|
12
|
+
|
13
|
+
def update
|
14
|
+
if resource.update_with_password(password_params)
|
15
|
+
prepare_for_redirect
|
16
|
+
redirect_to stored_location_for(scope_name) || :root
|
17
|
+
else
|
18
|
+
clean_up_passwords(resource)
|
19
|
+
render :edit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def authenticate_scope!
|
26
|
+
send(:"authenticate_#{scope_name}!")
|
27
|
+
self.resource = send("current_#{scope_name}")
|
28
|
+
end
|
29
|
+
|
30
|
+
def alert_string_for_password_updated
|
31
|
+
I18n.t('secure_password.password_requires_regular_updates.alerts.messages.password_updated')
|
32
|
+
end
|
33
|
+
|
34
|
+
def error_string_for_password_expired
|
35
|
+
return 'password expired' unless warden.user.class.respond_to?(:password_maximum_age)
|
36
|
+
I18n.t(
|
37
|
+
'secure_password.password_requires_regular_updates.errors.messages.password_expired',
|
38
|
+
timeframe: distance_of_time_in_words(warden.user.class.password_maximum_age)
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def password_params
|
43
|
+
params.require(scope_name).permit(:current_password, :password, :password_confirmation)
|
44
|
+
end
|
45
|
+
|
46
|
+
def prepare_for_redirect
|
47
|
+
warden.session(scope_name)[:secure_password_expired] = false
|
48
|
+
flash[:notice] = alert_string_for_password_updated
|
49
|
+
bypass_sign_in resource, scope: scope_name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<h2><%= t('.titles.section_title') %></h2>
|
2
|
+
|
3
|
+
<%= form_for(resource, as: resource_name, url: [resource_name, :password_with_policy], html: { method: :put }) do |f| %>
|
4
|
+
<%= devise_error_messages! %>
|
5
|
+
|
6
|
+
<p><%= f.label :current_password, t('.labels.current_password') %><br />
|
7
|
+
<%= f.password_field :current_password %></p>
|
8
|
+
|
9
|
+
<p><%= f.label :password, t('.labels.new_password') %><br />
|
10
|
+
<%= f.password_field :password %></p>
|
11
|
+
|
12
|
+
<p><%= f.label :password_confirmation, t('.labels.confirm_new_password') %><br />
|
13
|
+
<%= f.password_field :password_confirmation %></p>
|
14
|
+
|
15
|
+
<p><%= f.submit t('.buttons.change_my_password') %></p>
|
16
|
+
<% end %>
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "devise/secure_password"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|