password_breach_alert 0.1.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/MIT-LICENSE +20 -0
- data/README.md +141 -0
- data/Rakefile +17 -0
- data/app/views/devise/mailer/password_breach_alert.html.erb +27 -0
- data/app/views/devise/mailer/password_breach_alert.text.erb +24 -0
- data/lib/common_password/devise.rb +10 -0
- data/lib/common_password/list.rb +15 -0
- data/lib/common_password/model.rb +48 -0
- data/lib/common_password/passwords.txt +9999 -0
- data/lib/devise/password_breach_alert.rb +32 -0
- data/lib/devise/password_breach_alert/locales/en.yml +34 -0
- data/lib/devise/password_breach_alert/locales/it.yml +34 -0
- data/lib/devise/password_breach_alert/model.rb +25 -0
- data/lib/generators/password_breach_alert_generator.rb +13 -0
- data/lib/generators/templates/create_breaches.rb +19 -0
- data/lib/password_breach_alert.rb +9 -0
- data/lib/password_breach_alert/api/base.rb +39 -0
- data/lib/password_breach_alert/api/breach.rb +15 -0
- data/lib/password_breach_alert/api/breachedaccount.rb +18 -0
- data/lib/password_breach_alert/breaches_filters.rb +8 -0
- data/lib/password_breach_alert/breaches_filters/after_user_last_checked_at.rb +34 -0
- data/lib/password_breach_alert/breaches_filters/all_with_user.rb +21 -0
- data/lib/password_breach_alert/breaches_filters/new_with_user.rb +21 -0
- data/lib/password_breach_alert/breaches_policies.rb +6 -0
- data/lib/password_breach_alert/breaches_policies/send_devise_notification.rb +12 -0
- data/lib/password_breach_alert/checker.rb +45 -0
- data/lib/password_breach_alert/mailer.rb +9 -0
- data/lib/password_breach_alert/models/breach.rb +55 -0
- data/lib/password_breach_alert/rails.rb +10 -0
- data/lib/password_breach_alert/railtie.rb +4 -0
- data/lib/password_breach_alert/version.rb +3 -0
- data/lib/pwned_password/devise.rb +13 -0
- data/lib/pwned_password/hooks.rb +6 -0
- data/lib/pwned_password/model.rb +66 -0
- data/lib/tasks/password_breach_alert.rake +15 -0
- data/lib/zxcvbn_password/devise.rb +20 -0
- data/lib/zxcvbn_password/email_tokeniser.rb +7 -0
- data/lib/zxcvbn_password/errors.rb +2 -0
- data/lib/zxcvbn_password/model.rb +84 -0
- data/lib/zxcvbn_password/tester.rb +36 -0
- metadata +261 -0
@@ -0,0 +1,10 @@
|
|
1
|
+
module PasswordBreachAlert
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
config.to_prepare do
|
4
|
+
Devise.mailer.send :include, PasswordBreachAlert::Mailer
|
5
|
+
unless Devise.mailer.ancestors.include?(Devise::Mailers::Helpers)
|
6
|
+
Devise.mailer.send :include, Devise::Mailers::Helpers
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'devise'
|
4
|
+
|
5
|
+
module PwnedPassword
|
6
|
+
module Devise
|
7
|
+
mattr_accessor :min_password_matches, :min_password_matches_warn, :pwned_password_open_timeout, :pwned_password_read_timeout
|
8
|
+
@@min_password_matches = 1
|
9
|
+
@@min_password_matches_warn = nil
|
10
|
+
@@pwned_password_open_timeout = 5
|
11
|
+
@@pwned_password_read_timeout = 5
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Warden::Manager.after_set_user except: :fetch do |user, auth, opts|
|
4
|
+
password = auth.request.params.fetch(opts[:scope], {}).fetch(:password, nil)
|
5
|
+
password && auth.authenticated?(opts[:scope]) && user.respond_to?(:password_pwned?) && user.password_pwned?(password)
|
6
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pwned'
|
4
|
+
require 'pwned_password/hooks'
|
5
|
+
|
6
|
+
module PwnedPassword
|
7
|
+
# The PwnedPassword module adds a new validation for Devise Models.
|
8
|
+
# No modifications to routes or controllers needed.
|
9
|
+
# Simply add :pwned_password to the list of included modules in your
|
10
|
+
# devise module, and all new registrations will be blocked if they use
|
11
|
+
# a password in this dataset https://haveibeenpwned.com/Passwords.
|
12
|
+
module Model
|
13
|
+
extend ActiveSupport::Concern
|
14
|
+
|
15
|
+
included do
|
16
|
+
validate :not_pwned_password, if: proc { password_required? && self.class.pwned_active && !self.errors.include?(:password) }
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
::Devise::Models.config(self, :min_password_matches)
|
21
|
+
::Devise::Models.config(self, :min_password_matches_warn)
|
22
|
+
::Devise::Models.config(self, :pwned_password_open_timeout)
|
23
|
+
::Devise::Models.config(self, :pwned_password_read_timeout)
|
24
|
+
end
|
25
|
+
|
26
|
+
def pwned?
|
27
|
+
@pwned ||= false
|
28
|
+
end
|
29
|
+
|
30
|
+
def pwned_count
|
31
|
+
@pwned_count ||= 0
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns true if password is present in the PwnedPasswords dataset
|
35
|
+
# TODO: Implement retry behaviour described here https://haveibeenpwned.com/API/v2#RateLimiting
|
36
|
+
def password_pwned?(password)
|
37
|
+
@pwned = false
|
38
|
+
@pwned_count = 0
|
39
|
+
|
40
|
+
options = {
|
41
|
+
'User-Agent' => 'devise_pwned_password',
|
42
|
+
read_timeout: self.class.pwned_password_read_timeout,
|
43
|
+
open_timeout: self.class.pwned_password_open_timeout
|
44
|
+
}
|
45
|
+
pwned_password = Pwned::Password.new(password.to_s, options)
|
46
|
+
begin
|
47
|
+
@pwned_count = pwned_password.pwned_count
|
48
|
+
@pwned = @pwned_count >= (persisted? ? self.class.min_password_matches_warn || self.class.min_password_matches : self.class.min_password_matches)
|
49
|
+
return @pwned
|
50
|
+
rescue Pwned::Error
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def not_pwned_password
|
60
|
+
# This deliberately fails silently on 500's etc. Most apps wont want to tie the ability to sign up customers to the availability of a third party API
|
61
|
+
if password_pwned?(password)
|
62
|
+
errors.add(:password, :pwned)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
namespace :password_breach_alert do
|
2
|
+
desc 'Fetch breaches from HIBP, check email and enforce policy'
|
3
|
+
task :checker, [:model_name] => [:environment] do |_task, args|
|
4
|
+
args.with_defaults(model_name: :user)
|
5
|
+
model_name = args[:model_name].to_sym
|
6
|
+
mapping = Devise.mappings[model_name]
|
7
|
+
if !mapping
|
8
|
+
puts "Could not find Devise mapping for #{model_name}"
|
9
|
+
next
|
10
|
+
end
|
11
|
+
|
12
|
+
model_class = mapping.class_name.constantize
|
13
|
+
PasswordBreachAlert::Checker.new.call(users: model_class.all)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "devise"
|
4
|
+
require "zxcvbn_password/tester"
|
5
|
+
|
6
|
+
module ZxcvbnPassword
|
7
|
+
module Devise
|
8
|
+
|
9
|
+
# The minimun score for a password.
|
10
|
+
mattr_accessor :zxcvbn_min_password_score
|
11
|
+
mattr_accessor :zxcvbn_tester
|
12
|
+
@@zxcvbn_min_password_score = 4
|
13
|
+
@@zxcvbn_tester = Tester.new
|
14
|
+
|
15
|
+
def self.zxcvbn_min_password_score=(score)
|
16
|
+
raise "The zxcvbn_min_password_score must be an integer and between 0..4" unless (0..4).include?(score)
|
17
|
+
@@zxcvbn_min_password_score = score
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require "zxcvbn_password/email_tokeniser"
|
2
|
+
require "zxcvbn_password/errors"
|
3
|
+
require "ostruct"
|
4
|
+
|
5
|
+
module ZxcvbnPassword
|
6
|
+
module Model
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
# delegate :zxcvbn_min_password_score, to: "self.class"
|
10
|
+
# delegate :zxcvbn_tester, to: "self.class"
|
11
|
+
|
12
|
+
included do
|
13
|
+
validate :not_zxcvbn_password, if: proc { password_required? && self.class.zxcvbn_active && !self.errors.include?(:password) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def zxcvbn_password_score
|
17
|
+
@zxcvbn_password_score = self.class.zxcvbn_password_score(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def zxcvbn_password_crack_time
|
21
|
+
zxcvbn_password_score.crack_times_display["offline_slow_hashing_1e4_per_second"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def password_zxcvbn?
|
25
|
+
zxcvbn_password_score.score < ::Devise.zxcvbn_min_password_score
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def not_zxcvbn_password
|
31
|
+
if password_zxcvbn?
|
32
|
+
errors.add :password, :zxcvbn, i18n_variables
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def i18n_variables
|
37
|
+
{
|
38
|
+
# feedback: zxcvbn_feedback,
|
39
|
+
crack_time_display: zxcvbn_password_crack_time,
|
40
|
+
score: zxcvbn_password_score.score,
|
41
|
+
# zxcvbn_min_password_score: ::Devise.zxcvbn_min_password_score
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def zxcvbn_feedback
|
46
|
+
feedback = zxcvbn_password_score.feedback.values.flatten.reject(&:empty?)
|
47
|
+
return "Add another word or two. Uncommon words are better." if feedback.empty?
|
48
|
+
|
49
|
+
feedback.join(". ").gsub(/\.\s*\./, ".")
|
50
|
+
end
|
51
|
+
|
52
|
+
class_methods do
|
53
|
+
::Devise::Models.config(self, :zxcvbn_min_password_score)
|
54
|
+
::Devise::Models.config(self, :zxcvbn_tester)
|
55
|
+
|
56
|
+
def zxcvbn_password_score(user, arg_email = nil)
|
57
|
+
return raise ZxcvbnPasswordError, "the object must respond to password" unless user.respond_to?(:password)
|
58
|
+
|
59
|
+
password = user.password.to_s
|
60
|
+
|
61
|
+
zxcvbn_weak_words = []
|
62
|
+
|
63
|
+
if arg_email
|
64
|
+
zxcvbn_weak_words += [arg_email, *ZxcvbnPassword::EmailTokeniser.split(arg_email)]
|
65
|
+
end
|
66
|
+
|
67
|
+
# User method results are saved locally to prevent repeat calls that might be expensive
|
68
|
+
if user.respond_to?(:email)
|
69
|
+
local_email = user.email
|
70
|
+
zxcvbn_weak_words += [local_email, *ZxcvbnPassword::EmailTokeniser.split(local_email)]
|
71
|
+
end
|
72
|
+
|
73
|
+
if user.respond_to?(:weak_words)
|
74
|
+
return raise ZxcvbnPasswordError, "weak_words must return an Array" unless user.weak_words.is_a?(Array)
|
75
|
+
|
76
|
+
local_weak_words = user.weak_words
|
77
|
+
zxcvbn_weak_words += local_weak_words
|
78
|
+
end
|
79
|
+
|
80
|
+
::Devise.zxcvbn_tester.test(password, zxcvbn_weak_words)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "zxcvbn"
|
2
|
+
|
3
|
+
class Tester < Zxcvbn::Tester
|
4
|
+
IT = {
|
5
|
+
"less than a second" => "meno di un secondo",
|
6
|
+
"seconds" => "secondi",
|
7
|
+
"second" => "secondo",
|
8
|
+
"minutes" => "minuti",
|
9
|
+
"minute" => "minuto",
|
10
|
+
"hours" => "ore",
|
11
|
+
"hour" => "ora",
|
12
|
+
"days" => "giorni",
|
13
|
+
"day" => "giorno",
|
14
|
+
"months" => "mesi",
|
15
|
+
"month" => "mese",
|
16
|
+
"centuries" => "secoli"
|
17
|
+
}
|
18
|
+
|
19
|
+
def test(password, user_inputs = [])
|
20
|
+
result = super(password, user_inputs)
|
21
|
+
result.crack_times_display['offline_slow_hashing_1e4_per_second'] = localize(result.crack_times_display['offline_slow_hashing_1e4_per_second'])
|
22
|
+
result
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def localize(string)
|
28
|
+
if I18n.locale == :it
|
29
|
+
IT.each_pair do |key, val|
|
30
|
+
return string.gsub(key, val) if string =~ /#{key}/i
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
string
|
35
|
+
end
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,261 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: password_breach_alert
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Carlo Martinucci
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-07-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: actionmailer
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: devise
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pwned
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.2.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.2.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: zxcvbn-js
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.4.1
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 4.4.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: letter_opener
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: overcommit
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rails
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: 5.2.2
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 5.2.2
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.72'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.72'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rubocop-performance
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubocop-rails
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: simplecov
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: sqlite3
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: |2
|
182
|
+
Password Breach Alert is a Devise extension that adds 1) server-side check of password
|
183
|
+
strength before registration, using a list of common passwords, zxcvbn and haveibeenpwned;
|
184
|
+
2) a way to check users emails against recent verified security breaches, and implement
|
185
|
+
different customized policies.
|
186
|
+
email:
|
187
|
+
- carlo.martinucci@gmail.com
|
188
|
+
executables: []
|
189
|
+
extensions: []
|
190
|
+
extra_rdoc_files: []
|
191
|
+
files:
|
192
|
+
- MIT-LICENSE
|
193
|
+
- README.md
|
194
|
+
- Rakefile
|
195
|
+
- app/views/devise/mailer/password_breach_alert.html.erb
|
196
|
+
- app/views/devise/mailer/password_breach_alert.text.erb
|
197
|
+
- lib/common_password/devise.rb
|
198
|
+
- lib/common_password/list.rb
|
199
|
+
- lib/common_password/model.rb
|
200
|
+
- lib/common_password/passwords.txt
|
201
|
+
- lib/devise/password_breach_alert.rb
|
202
|
+
- lib/devise/password_breach_alert/locales/en.yml
|
203
|
+
- lib/devise/password_breach_alert/locales/it.yml
|
204
|
+
- lib/devise/password_breach_alert/model.rb
|
205
|
+
- lib/generators/password_breach_alert_generator.rb
|
206
|
+
- lib/generators/templates/create_breaches.rb
|
207
|
+
- lib/password_breach_alert.rb
|
208
|
+
- lib/password_breach_alert/api/base.rb
|
209
|
+
- lib/password_breach_alert/api/breach.rb
|
210
|
+
- lib/password_breach_alert/api/breachedaccount.rb
|
211
|
+
- lib/password_breach_alert/breaches_filters.rb
|
212
|
+
- lib/password_breach_alert/breaches_filters/after_user_last_checked_at.rb
|
213
|
+
- lib/password_breach_alert/breaches_filters/all_with_user.rb
|
214
|
+
- lib/password_breach_alert/breaches_filters/new_with_user.rb
|
215
|
+
- lib/password_breach_alert/breaches_policies.rb
|
216
|
+
- lib/password_breach_alert/breaches_policies/send_devise_notification.rb
|
217
|
+
- lib/password_breach_alert/checker.rb
|
218
|
+
- lib/password_breach_alert/mailer.rb
|
219
|
+
- lib/password_breach_alert/models/breach.rb
|
220
|
+
- lib/password_breach_alert/rails.rb
|
221
|
+
- lib/password_breach_alert/railtie.rb
|
222
|
+
- lib/password_breach_alert/version.rb
|
223
|
+
- lib/pwned_password/devise.rb
|
224
|
+
- lib/pwned_password/hooks.rb
|
225
|
+
- lib/pwned_password/model.rb
|
226
|
+
- lib/tasks/password_breach_alert.rake
|
227
|
+
- lib/zxcvbn_password/devise.rb
|
228
|
+
- lib/zxcvbn_password/email_tokeniser.rb
|
229
|
+
- lib/zxcvbn_password/errors.rb
|
230
|
+
- lib/zxcvbn_password/model.rb
|
231
|
+
- lib/zxcvbn_password/tester.rb
|
232
|
+
homepage: https://github.com/Uqido/password_breach_alert
|
233
|
+
licenses:
|
234
|
+
- MIT
|
235
|
+
metadata:
|
236
|
+
bug_tracker_uri: https://github.com/Uqido/password_breach_alert/issues
|
237
|
+
changelog_uri: https://github.com/Uqido/password_breach_alert/CHANGELOG.md
|
238
|
+
documentation_uri: https://github.com/Uqido/password_breach_alert/README.md
|
239
|
+
homepage_uri: https://github.com/Uqido/password_breach_alert
|
240
|
+
source_code_uri: https://github.com/Uqido/password_breach_alert
|
241
|
+
wiki_uri: https://github.com/Uqido/password_breach_alert/wiki
|
242
|
+
post_install_message:
|
243
|
+
rdoc_options: []
|
244
|
+
require_paths:
|
245
|
+
- lib
|
246
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - ">="
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: '0'
|
251
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
252
|
+
requirements:
|
253
|
+
- - ">="
|
254
|
+
- !ruby/object:Gem::Version
|
255
|
+
version: '0'
|
256
|
+
requirements: []
|
257
|
+
rubygems_version: 3.0.2
|
258
|
+
signing_key:
|
259
|
+
specification_version: 4
|
260
|
+
summary: Never let a user use a compromised password in your application again!
|
261
|
+
test_files: []
|