pwned 2.0.1 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "net/http"
5
+
6
+ module Pwned
7
+ ##
8
+ # This class represents a password. It does all the work of talking to the
9
+ # Pwned Passwords API to find out if the password has been pwned.
10
+ # @see https://haveibeenpwned.com/API/v2#PwnedPasswords
11
+ module PasswordBase
12
+ ##
13
+ # The base URL for the Pwned Passwords API
14
+ API_URL = "https://api.pwnedpasswords.com/range/"
15
+
16
+ ##
17
+ # The number of characters from the start of the hash of the password that
18
+ # are used to search for the range of passwords.
19
+ HASH_PREFIX_LENGTH = 5
20
+
21
+ ##
22
+ # The total length of a SHA1 hash
23
+ SHA1_LENGTH = 40
24
+
25
+ ##
26
+ # The default request headers that are used to make HTTP requests to the
27
+ # API. A user agent is provided as requested in the documentation.
28
+ # @see https://haveibeenpwned.com/API/v2#UserAgent
29
+ DEFAULT_REQUEST_HEADERS = {
30
+ "User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}"
31
+ }.freeze
32
+
33
+ ##
34
+ # @example
35
+ # password = Pwned::Password.new("password")
36
+ # password.pwned? #=> true
37
+ # password.pwned? #=> true
38
+ #
39
+ # @return [Boolean] +true+ when the password has been pwned.
40
+ # @raise [Pwned::Error] if there are errors with the HTTP request.
41
+ # @raise [Pwned::TimeoutError] if the HTTP request times out.
42
+ # @since 1.0.0
43
+ def pwned?
44
+ pwned_count > 0
45
+ end
46
+
47
+ ##
48
+ # @example
49
+ # password = Pwned::Password.new("password")
50
+ # password.pwned_count #=> 3303003
51
+ #
52
+ # @return [Integer] the number of times the password has been pwned.
53
+ # @raise [Pwned::Error] if there are errors with the HTTP request.
54
+ # @raise [Pwned::TimeoutError] if the HTTP request times out.
55
+ # @since 1.0.0
56
+ def pwned_count
57
+ @pwned_count ||= fetch_pwned_count
58
+ end
59
+
60
+ ##
61
+ # Returns the full SHA1 hash of the given password in uppercase.
62
+ # @return [String] The full SHA1 hash of the given password.
63
+ # @since 1.0.0
64
+ attr_reader :hashed_password
65
+
66
+ private
67
+
68
+ attr_reader :request_options, :request_headers, :request_proxy, :ignore_env_proxy
69
+
70
+ def fetch_pwned_count
71
+ for_each_response_line do |line|
72
+ next unless line.start_with?(hashed_password_suffix)
73
+ # Count starts after the suffix, followed by a colon
74
+ return line[(SHA1_LENGTH-HASH_PREFIX_LENGTH+1)..-1].to_i
75
+ end
76
+
77
+ # The hash was not found, we can assume the password is not pwned [yet]
78
+ 0
79
+ end
80
+
81
+ def for_each_response_line(&block)
82
+ begin
83
+ with_http_response "#{API_URL}#{hashed_password_prefix}" do |response|
84
+ response.value # raise if request was unsuccessful
85
+ stream_response_lines(response, &block)
86
+ end
87
+ rescue Timeout::Error => e
88
+ raise Pwned::TimeoutError, e.message
89
+ rescue => e
90
+ raise Pwned::Error, e.message
91
+ end
92
+ end
93
+
94
+ def hashed_password_prefix
95
+ @hashed_password[0...HASH_PREFIX_LENGTH]
96
+ end
97
+
98
+ def hashed_password_suffix
99
+ @hashed_password[HASH_PREFIX_LENGTH..-1]
100
+ end
101
+
102
+ # Make a HTTP GET request given the url and headers.
103
+ # Yields a `Net::HTTPResponse`.
104
+ def with_http_response(url, &block)
105
+ uri = URI(url)
106
+
107
+ request = Net::HTTP::Get.new(uri)
108
+ request.initialize_http_header(request_headers)
109
+ request_options[:use_ssl] = true
110
+
111
+ environment_proxy = ignore_env_proxy ? nil : :ENV
112
+
113
+ Net::HTTP.start(
114
+ uri.host,
115
+ uri.port,
116
+ request_proxy&.host || environment_proxy,
117
+ request_proxy&.port,
118
+ request_proxy&.user,
119
+ request_proxy&.password,
120
+ request_options
121
+ ) do |http|
122
+ http.request(request, &block)
123
+ end
124
+ end
125
+
126
+ # Stream a Net::HTTPResponse by line, handling lines that cross chunks.
127
+ def stream_response_lines(response, &block)
128
+ last_line = ""
129
+
130
+ response.read_body do |chunk|
131
+ chunk_lines = (last_line + chunk).lines
132
+ # This could end with half a line, so save it for next time. If
133
+ # chunk_lines is empty, pop returns nil, so this also ensures last_line
134
+ # is always a string.
135
+ last_line = chunk_lines.pop || ""
136
+ chunk_lines.each(&block)
137
+ end
138
+
139
+ yield last_line unless last_line.empty?
140
+ end
141
+ end
142
+ end
data/lib/pwned/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  module Pwned
4
4
  ##
5
5
  # The current version of the +pwned+ gem.
6
- VERSION = "2.0.1"
6
+ VERSION = "2.3.0"
7
7
  end
data/lib/pwned.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "digest"
3
4
  require "pwned/version"
4
5
  require "pwned/error"
5
6
  require "pwned/password"
7
+ require "pwned/hashed_password"
6
8
 
7
9
  begin
8
10
  # Load Rails and our custom validator
@@ -31,8 +33,11 @@ module Pwned
31
33
  # @param password [String] The password you want to check against the API.
32
34
  # @param [Hash] request_options Options that can be passed to +Net::HTTP.start+ when
33
35
  # calling the API
34
- # @option request_options [Symbol] :headers ({ "User-Agent" => '"Ruby Pwned::Password #{Pwned::VERSION}" })
36
+ # @option request_options [Symbol] :headers ({ "User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}" })
35
37
  # HTTP headers to include in the request
38
+ # @option request_options [Symbol] :ignore_env_proxy (false) The library
39
+ # will try to infer an HTTP proxy from the `http_proxy` environment
40
+ # variable. If you do not want this behaviour, set this option to true.
36
41
  # @return [Boolean] Whether the password appears in the data breaches or not.
37
42
  # @since 1.1.0
38
43
  def self.pwned?(password, request_options={})
@@ -49,12 +54,29 @@ module Pwned
49
54
  # @param password [String] The password you want to check against the API.
50
55
  # @param [Hash] request_options Options that can be passed to +Net::HTTP.start+ when
51
56
  # calling the API
52
- # @option request_options [Symbol] :headers ({ "User-Agent" => '"Ruby Pwned::Password #{Pwned::VERSION}" })
57
+ # @option request_options [Symbol] :headers ({ "User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}" })
53
58
  # HTTP headers to include in the request
59
+ # @option request_options [Symbol] :ignore_env_proxy (false) The library
60
+ # will try to infer an HTTP proxy from the `http_proxy` environment
61
+ # variable. If you do not want this behaviour, set this option to true.
54
62
  # @return [Integer] The number of times the password has appeared in the data
55
63
  # breaches.
56
64
  # @since 1.1.0
57
65
  def self.pwned_count(password, request_options={})
58
66
  Pwned::Password.new(password, request_options).pwned_count
59
67
  end
68
+
69
+ ##
70
+ # Returns the full SHA1 hash of the given password in uppercase. This can be safely passed around your code
71
+ # before making the pwned request (e.g. dropped into a queue table).
72
+ #
73
+ # @example
74
+ # Pwned.hash_password("password") #=> 5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8
75
+ #
76
+ # @param password [String] The password you want to check against the API
77
+ # @return [String] An uppercase SHA1 hash of the password
78
+ # @since 2.1.0
79
+ def self.hash_password(password)
80
+ Digest::SHA1.hexdigest(password).upcase
81
+ end
60
82
  end
data/pwned.gemspec CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  spec.metadata = {
17
17
  "bug_tracker_uri" => "https://github.com/philnash/pwned/issues",
18
18
  "change_log_uri" => "https://github.com/philnash/pwned/blob/master/CHANGELOG.md",
19
- "documentation_uri" => "https://philnash.github.io/pwned/",
19
+ "documentation_uri" => "https://www.rubydoc.info/gems/pwned",
20
20
  "homepage_uri" => "https://github.com/philnash/pwned",
21
21
  "source_code_uri" => "https://github.com/philnash/pwned"
22
22
  }
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.executables = ["pwned"]
29
29
 
30
30
  spec.add_development_dependency "bundler", ">= 1.16", "< 3.0"
31
- spec.add_development_dependency "rake", "~> 10.0"
31
+ spec.add_development_dependency "rake", "~> 13.0"
32
32
  spec.add_development_dependency "rspec", "~> 3.0"
33
33
  spec.add_development_dependency "webmock", "~> 3.3"
34
34
  spec.add_development_dependency "yard", "~> 0.9.12"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pwned
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phil Nash
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-13 00:00:00.000000000 Z
11
+ date: 2021-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '10.0'
39
+ version: '13.0'
40
40
  type: :development
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '10.0'
46
+ version: '13.0'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -94,9 +94,10 @@ executables:
94
94
  extensions: []
95
95
  extra_rdoc_files: []
96
96
  files:
97
+ - ".github/FUNDING.yml"
98
+ - ".github/workflows/tests.yml"
97
99
  - ".gitignore"
98
100
  - ".rspec"
99
- - ".travis.yml"
100
101
  - ".yardopts"
101
102
  - CHANGELOG.md
102
103
  - CODE_OF_CONDUCT.md
@@ -107,31 +108,13 @@ files:
107
108
  - bin/console
108
109
  - bin/pwned
109
110
  - bin/setup
110
- - docs/NotPwnedValidator.html
111
- - docs/Pwned.html
112
- - docs/Pwned/Error.html
113
- - docs/Pwned/Password.html
114
- - docs/Pwned/TimeoutError.html
115
- - docs/PwnedValidator.html
116
- - docs/_index.html
117
- - docs/class_list.html
118
- - docs/css/common.css
119
- - docs/css/full_list.css
120
- - docs/css/style.css
121
- - docs/file.README.html
122
- - docs/file_list.html
123
- - docs/frames.html
124
- - docs/index.html
125
- - docs/js/app.js
126
- - docs/js/full_list.js
127
- - docs/js/jquery.js
128
- - docs/method_list.html
129
- - docs/top-level-namespace.html
130
111
  - lib/locale/en.yml
131
112
  - lib/pwned.rb
132
113
  - lib/pwned/error.rb
114
+ - lib/pwned/hashed_password.rb
133
115
  - lib/pwned/not_pwned_validator.rb
134
116
  - lib/pwned/password.rb
117
+ - lib/pwned/password_base.rb
135
118
  - lib/pwned/version.rb
136
119
  - pwned.gemspec
137
120
  homepage: https://github.com/philnash/pwned
@@ -140,10 +123,10 @@ licenses:
140
123
  metadata:
141
124
  bug_tracker_uri: https://github.com/philnash/pwned/issues
142
125
  change_log_uri: https://github.com/philnash/pwned/blob/master/CHANGELOG.md
143
- documentation_uri: https://philnash.github.io/pwned/
126
+ documentation_uri: https://www.rubydoc.info/gems/pwned
144
127
  homepage_uri: https://github.com/philnash/pwned
145
128
  source_code_uri: https://github.com/philnash/pwned
146
- post_install_message:
129
+ post_install_message:
147
130
  rdoc_options: []
148
131
  require_paths:
149
132
  - lib
@@ -158,9 +141,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
158
141
  - !ruby/object:Gem::Version
159
142
  version: '0'
160
143
  requirements: []
161
- rubyforge_project:
162
- rubygems_version: 2.7.6
163
- signing_key:
144
+ rubygems_version: 3.1.2
145
+ signing_key:
164
146
  specification_version: 4
165
147
  summary: Tools to use the Pwned Passwords API.
166
148
  test_files: []
data/.travis.yml DELETED
@@ -1,27 +0,0 @@
1
- sudo: false
2
- language: ruby
3
-
4
- env:
5
- matrix:
6
- - RAILS_VERSION=4.2.11.1
7
- - RAILS_VERSION=5.0.7.2
8
- - RAILS_VERSION=5.1.7
9
- - RAILS_VERSION=5.2.3
10
- - RAILS_VERSION=6.0.0
11
-
12
- rvm:
13
- - 2.7
14
- - 2.6
15
- - 2.5
16
- - 2.4
17
- - jruby
18
- - ruby-head
19
-
20
- before_install: gem install bundler
21
-
22
- matrix:
23
- allow_failures:
24
- - rvm: ruby-head
25
- exclude:
26
- - rvm: 2.4
27
- env: RAILS_VERSION=6.0.0
@@ -1,494 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>
7
- Class: NotPwnedValidator
8
-
9
- &mdash; Documentation by YARD 0.9.20
10
-
11
- </title>
12
-
13
- <link rel="stylesheet" href="css/style.css" type="text/css" charset="utf-8" />
14
-
15
- <link rel="stylesheet" href="css/common.css" type="text/css" charset="utf-8" />
16
-
17
- <script type="text/javascript" charset="utf-8">
18
- pathId = "NotPwnedValidator";
19
- relpath = '';
20
- </script>
21
-
22
-
23
- <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
-
25
- <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
-
27
-
28
- </head>
29
- <body>
30
- <div class="nav_wrap">
31
- <iframe id="nav" src="class_list.html?1"></iframe>
32
- <div id="resizer"></div>
33
- </div>
34
-
35
- <div id="main" tabindex="-1">
36
- <div id="header">
37
- <div id="menu">
38
-
39
- <a href="_index.html">Index (N)</a> &raquo;
40
-
41
-
42
- <span class="title">NotPwnedValidator</span>
43
-
44
- </div>
45
-
46
- <div id="search">
47
-
48
- <a class="full_list_link" id="class_list_link"
49
- href="class_list.html">
50
-
51
- <svg width="24" height="24">
52
- <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
- <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
- <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
- </svg>
56
- </a>
57
-
58
- </div>
59
- <div class="clear"></div>
60
- </div>
61
-
62
- <div id="content"><h1>Class: NotPwnedValidator
63
-
64
-
65
-
66
- </h1>
67
- <div class="box_info">
68
-
69
- <dl>
70
- <dt>Inherits:</dt>
71
- <dd>
72
- <span class="inheritName">ActiveModel::EachValidator</span>
73
-
74
- <ul class="fullTree">
75
- <li>Object</li>
76
-
77
- <li class="next">ActiveModel::EachValidator</li>
78
-
79
- <li class="next">NotPwnedValidator</li>
80
-
81
- </ul>
82
- <a href="#" class="inheritanceTree">show all</a>
83
-
84
- </dd>
85
- </dl>
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
- <dl>
98
- <dt>Defined in:</dt>
99
- <dd>lib/pwned/not_pwned_validator.rb</dd>
100
- </dl>
101
-
102
- </div>
103
-
104
- <h2>Overview</h2><div class="docstring">
105
- <div class="discussion">
106
-
107
- <p>An <code>ActiveModel</code> validator to check passwords against the Pwned
108
- Passwords API.</p>
109
-
110
-
111
- </div>
112
- </div>
113
- <div class="tags">
114
-
115
- <div class="examples">
116
- <p class="tag_title">Examples:</p>
117
-
118
-
119
- <p class="example_title"><div class='inline'>
120
- <p>Validate a password on a <code>User</code> model with the default options.</p>
121
- </div></p>
122
-
123
- <pre class="example code"><code><span class='kw'>class</span> <span class='const'>User</span> <span class='op'>&lt;</span> <span class='const'>ApplicationRecord</span>
124
- <span class='id identifier rubyid_validates'>validates</span> <span class='symbol'>:password</span><span class='comma'>,</span> <span class='label'>not_pwned:</span> <span class='kw'>true</span>
125
- <span class='kw'>end</span></code></pre>
126
-
127
-
128
- <p class="example_title"><div class='inline'>
129
- <p>Validate a password on a <code>User</code> model with a custom error
130
- message.</p>
131
- </div></p>
132
-
133
- <pre class="example code"><code><span class='kw'>class</span> <span class='const'>User</span> <span class='op'>&lt;</span> <span class='const'>ApplicationRecord</span>
134
- <span class='id identifier rubyid_validates'>validates</span> <span class='symbol'>:password</span><span class='comma'>,</span> <span class='label'>not_pwned:</span> <span class='lbrace'>{</span> <span class='label'>message:</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>has been pwned %{count} times</span><span class='tstring_end'>&quot;</span></span> <span class='rbrace'>}</span>
135
- <span class='kw'>end</span></code></pre>
136
-
137
-
138
- <p class="example_title"><div class='inline'>
139
- <p>Validate a password on a <code>User</code> model that allows the password
140
- to have been breached once.</p>
141
- </div></p>
142
-
143
- <pre class="example code"><code><span class='kw'>class</span> <span class='const'>User</span> <span class='op'>&lt;</span> <span class='const'>ApplicationRecord</span>
144
- <span class='id identifier rubyid_validates'>validates</span> <span class='symbol'>:password</span><span class='comma'>,</span> <span class='label'>not_pwned:</span> <span class='lbrace'>{</span> <span class='label'>threshold:</span> <span class='int'>1</span> <span class='rbrace'>}</span>
145
- <span class='kw'>end</span></code></pre>
146
-
147
-
148
- <p class="example_title"><div class='inline'>
149
- <p>Validate a password on a <code>User</code> model, handling API errors in
150
- various ways</p>
151
- </div></p>
152
-
153
- <pre class="example code"><code><span class='kw'>class</span> <span class='const'>User</span> <span class='op'>&lt;</span> <span class='const'>ApplicationRecord</span>
154
- <span class='comment'># The record is marked as invalid on network errors
155
- </span> <span class='comment'># (error message &quot;could not be verified against the past data breaches&quot;.)
156
- </span> <span class='id identifier rubyid_validates'>validates</span> <span class='symbol'>:password</span><span class='comma'>,</span> <span class='label'>not_pwned:</span> <span class='lbrace'>{</span> <span class='label'>on_error:</span> <span class='symbol'>:invalid</span> <span class='rbrace'>}</span>
157
-
158
- <span class='comment'># The record is marked as invalid on network errors with custom error.
159
- </span> <span class='id identifier rubyid_validates'>validates</span> <span class='symbol'>:password</span><span class='comma'>,</span> <span class='label'>not_pwned:</span> <span class='lbrace'>{</span> <span class='label'>on_error:</span> <span class='symbol'>:invalid</span><span class='comma'>,</span> <span class='label'>error_message:</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='tstring_content'>might be pwned</span><span class='tstring_end'>&quot;</span></span> <span class='rbrace'>}</span>
160
-
161
- <span class='comment'># An error is raised on network errors.
162
- </span> <span class='comment'># This means that `record.valid?` will raise `Pwned::Error`.
163
- </span> <span class='comment'># Not recommended to use in production.
164
- </span> <span class='id identifier rubyid_validates'>validates</span> <span class='symbol'>:password</span><span class='comma'>,</span> <span class='label'>not_pwned:</span> <span class='lbrace'>{</span> <span class='label'>on_error:</span> <span class='symbol'>:raise_error</span> <span class='rbrace'>}</span>
165
-
166
- <span class='comment'># Call custom proc on error. For example, capture errors in Sentry,
167
- </span> <span class='comment'># but do not mark the record as invalid.
168
- </span> <span class='id identifier rubyid_validates'>validates</span> <span class='symbol'>:password</span><span class='comma'>,</span> <span class='label'>not_pwned:</span> <span class='lbrace'>{</span>
169
- <span class='label'>on_error:</span> <span class='tlambda'>-&gt;</span><span class='lparen'>(</span><span class='id identifier rubyid_record'>record</span><span class='comma'>,</span> <span class='id identifier rubyid_error'>error</span><span class='rparen'>)</span> <span class='tlambeg'>{</span> <span class='const'>Raven</span><span class='period'>.</span><span class='id identifier rubyid_capture_exception'>capture_exception</span><span class='lparen'>(</span><span class='id identifier rubyid_error'>error</span><span class='rparen'>)</span> <span class='rbrace'>}</span>
170
- <span class='rbrace'>}</span>
171
- <span class='kw'>end</span></code></pre>
172
-
173
- </div>
174
-
175
- <p class="tag_title">Since:</p>
176
- <ul class="since">
177
-
178
- <li>
179
-
180
-
181
-
182
-
183
-
184
- <div class='inline'>
185
- <p>1.2.0</p>
186
- </div>
187
-
188
- </li>
189
-
190
- </ul>
191
-
192
- </div><div id="subclasses">
193
- <h2>Direct Known Subclasses</h2>
194
- <p class="children"><span class='object_link'><a href="PwnedValidator.html" title="PwnedValidator (class)">PwnedValidator</a></span></p>
195
- </div>
196
-
197
-
198
- <h2>
199
- Constant Summary
200
- <small><a href="#" class="constants_summary_toggle">collapse</a></small>
201
- </h2>
202
-
203
- <dl class="constants">
204
-
205
- <dt id="DEFAULT_ON_ERROR-constant" class="">DEFAULT_ON_ERROR =
206
- <div class="docstring">
207
- <div class="discussion">
208
-
209
- <p>The default behaviour of this validator in the case of an API failure. The
210
- default will mean that if the API fails the object will not be marked
211
- invalid.</p>
212
-
213
-
214
- </div>
215
- </div>
216
- <div class="tags">
217
-
218
- <p class="tag_title">Since:</p>
219
- <ul class="since">
220
-
221
- <li>
222
-
223
-
224
-
225
-
226
-
227
- <div class='inline'>
228
- <p>1.2.0</p>
229
- </div>
230
-
231
- </li>
232
-
233
- </ul>
234
-
235
- </div>
236
- </dt>
237
- <dd><pre class="code"><span class='symbol'>:valid</span></pre></dd>
238
-
239
- <dt id="DEFAULT_THRESHOLD-constant" class="">DEFAULT_THRESHOLD =
240
- <div class="docstring">
241
- <div class="discussion">
242
-
243
- <p>The default threshold for whether a breach is considered pwned. The default
244
- is 0, so any password that appears in a breach will mark the record as
245
- invalid.</p>
246
-
247
-
248
- </div>
249
- </div>
250
- <div class="tags">
251
-
252
- <p class="tag_title">Since:</p>
253
- <ul class="since">
254
-
255
- <li>
256
-
257
-
258
-
259
-
260
-
261
- <div class='inline'>
262
- <p>1.2.0</p>
263
- </div>
264
-
265
- </li>
266
-
267
- </ul>
268
-
269
- </div>
270
- </dt>
271
- <dd><pre class="code"><span class='int'>0</span></pre></dd>
272
-
273
- </dl>
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
- <h2>
284
- Instance Method Summary
285
- <small><a href="#" class="summary_toggle">collapse</a></small>
286
- </h2>
287
-
288
- <ul class="summary">
289
-
290
- <li class="public ">
291
- <span class="summary_signature">
292
-
293
- <a href="#validate_each-instance_method" title="#validate_each (instance method)">#<strong>validate_each</strong>(record, attribute, value) &#x21d2; Object </a>
294
-
295
-
296
-
297
- </span>
298
-
299
-
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
- <span class="summary_desc"><div class='inline'>
308
- <p>Validates the <code>value</code> against the Pwned Passwords API.</p>
309
- </div></span>
310
-
311
- </li>
312
-
313
-
314
- </ul>
315
-
316
-
317
-
318
-
319
-
320
- <div id="instance_method_details" class="method_details_list">
321
- <h2>Instance Method Details</h2>
322
-
323
-
324
- <div class="method_details first">
325
- <h3 class="signature first" id="validate_each-instance_method">
326
-
327
- #<strong>validate_each</strong>(record, attribute, value) &#x21d2; <tt>Object</tt>
328
-
329
-
330
-
331
-
332
-
333
- </h3><div class="docstring">
334
- <div class="discussion">
335
-
336
- <p>Validates the <code>value</code> against the Pwned Passwords API. If the
337
- <code>pwned_count</code> is higher than the optional <code>threshold</code>
338
- then the record is marked as invalid.</p>
339
-
340
- <p>In the case of an API error the validator will either mark the record as
341
- valid or invalid. Alternatively it will run an associated proc or re-raise
342
- the original error.</p>
343
-
344
- <p>The validation will short circuit and return with no errors added if the
345
- password is blank. The <code>Pwned::Password</code> initializer expects the
346
- password to be a string and will throw a <code>TypeError</code> if it is
347
- <code>nil</code>. Also, technically the empty string is not a password that
348
- is reported to be found in data breaches, so returns <code>false</code>,
349
- short circuiting that using <code>value.blank?</code> saves us a trip to
350
- the API.</p>
351
-
352
-
353
- </div>
354
- </div>
355
- <div class="tags">
356
- <p class="tag_title">Parameters:</p>
357
- <ul class="param">
358
-
359
- <li>
360
-
361
- <span class='name'>record</span>
362
-
363
-
364
- <span class='type'>(<tt>ActiveModel::Validations</tt>)</span>
365
-
366
-
367
-
368
- &mdash;
369
- <div class='inline'>
370
- <p>The object being validated</p>
371
- </div>
372
-
373
- </li>
374
-
375
- <li>
376
-
377
- <span class='name'>attribute</span>
378
-
379
-
380
- <span class='type'>(<tt>Symbol</tt>)</span>
381
-
382
-
383
-
384
- &mdash;
385
- <div class='inline'>
386
- <p>The attribute on the record that is currently being validated.</p>
387
- </div>
388
-
389
- </li>
390
-
391
- <li>
392
-
393
- <span class='name'>value</span>
394
-
395
-
396
- <span class='type'>(<tt>String</tt>)</span>
397
-
398
-
399
-
400
- &mdash;
401
- <div class='inline'>
402
- <p>The value of the attribute on the record that is the subject of the
403
- validation</p>
404
- </div>
405
-
406
- </li>
407
-
408
- </ul>
409
-
410
- <p class="tag_title">Since:</p>
411
- <ul class="since">
412
-
413
- <li>
414
-
415
-
416
-
417
-
418
-
419
- <div class='inline'>
420
- <p>1.2.0</p>
421
- </div>
422
-
423
- </li>
424
-
425
- </ul>
426
-
427
- </div><table class="source_code">
428
- <tr>
429
- <td>
430
- <pre class="lines">
431
-
432
-
433
- 77
434
- 78
435
- 79
436
- 80
437
- 81
438
- 82
439
- 83
440
- 84
441
- 85
442
- 86
443
- 87
444
- 88
445
- 89
446
- 90
447
- 91
448
- 92
449
- 93
450
- 94
451
- 95
452
- 96</pre>
453
- </td>
454
- <td>
455
- <pre class="code"><span class="info file"># File 'lib/pwned/not_pwned_validator.rb', line 77</span>
456
-
457
- <span class='kw'>def</span> <span class='id identifier rubyid_validate_each'>validate_each</span><span class='lparen'>(</span><span class='id identifier rubyid_record'>record</span><span class='comma'>,</span> <span class='id identifier rubyid_attribute'>attribute</span><span class='comma'>,</span> <span class='id identifier rubyid_value'>value</span><span class='rparen'>)</span>
458
- <span class='kw'>return</span> <span class='kw'>if</span> <span class='id identifier rubyid_value'>value</span><span class='period'>.</span><span class='id identifier rubyid_blank?'>blank?</span>
459
- <span class='kw'>begin</span>
460
- <span class='id identifier rubyid_pwned_check'>pwned_check</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="Pwned.html" title="Pwned (module)">Pwned</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Pwned/Password.html" title="Pwned::Password (class)">Password</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Pwned/Password.html#initialize-instance_method" title="Pwned::Password#initialize (method)">new</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_value'>value</span><span class='comma'>,</span> <span class='id identifier rubyid_request_options'>request_options</span><span class='rparen'>)</span>
461
- <span class='kw'>if</span> <span class='id identifier rubyid_pwned_check'>pwned_check</span><span class='period'>.</span><span class='id identifier rubyid_pwned_count'>pwned_count</span> <span class='op'>&gt;</span> <span class='id identifier rubyid_threshold'>threshold</span>
462
- <span class='id identifier rubyid_record'>record</span><span class='period'>.</span><span class='id identifier rubyid_errors'>errors</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span><span class='lparen'>(</span><span class='id identifier rubyid_attribute'>attribute</span><span class='comma'>,</span> <span class='symbol'>:not_pwned</span><span class='comma'>,</span> <span class='id identifier rubyid_options'>options</span><span class='period'>.</span><span class='id identifier rubyid_merge'>merge</span><span class='lparen'>(</span><span class='label'>count:</span> <span class='id identifier rubyid_pwned_check'>pwned_check</span><span class='period'>.</span><span class='id identifier rubyid_pwned_count'>pwned_count</span><span class='rparen'>)</span><span class='rparen'>)</span>
463
- <span class='kw'>end</span>
464
- <span class='kw'>rescue</span> <span class='const'><span class='object_link'><a href="Pwned.html" title="Pwned (module)">Pwned</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Pwned/Error.html" title="Pwned::Error (class)">Error</a></span></span> <span class='op'>=&gt;</span> <span class='id identifier rubyid_error'>error</span>
465
- <span class='kw'>case</span> <span class='id identifier rubyid_on_error'>on_error</span>
466
- <span class='kw'>when</span> <span class='symbol'>:invalid</span>
467
- <span class='id identifier rubyid_record'>record</span><span class='period'>.</span><span class='id identifier rubyid_errors'>errors</span><span class='period'>.</span><span class='id identifier rubyid_add'>add</span><span class='lparen'>(</span><span class='id identifier rubyid_attribute'>attribute</span><span class='comma'>,</span> <span class='symbol'>:pwned_error</span><span class='comma'>,</span> <span class='id identifier rubyid_options'>options</span><span class='period'>.</span><span class='id identifier rubyid_merge'>merge</span><span class='lparen'>(</span><span class='label'>message:</span> <span class='id identifier rubyid_options'>options</span><span class='lbracket'>[</span><span class='symbol'>:error_message</span><span class='rbracket'>]</span><span class='rparen'>)</span><span class='rparen'>)</span>
468
- <span class='kw'>when</span> <span class='symbol'>:valid</span>
469
- <span class='comment'># Do nothing, consider the record valid
470
- </span> <span class='kw'>when</span> <span class='const'>Proc</span>
471
- <span class='id identifier rubyid_on_error'>on_error</span><span class='period'>.</span><span class='id identifier rubyid_call'>call</span><span class='lparen'>(</span><span class='id identifier rubyid_record'>record</span><span class='comma'>,</span> <span class='id identifier rubyid_error'>error</span><span class='rparen'>)</span>
472
- <span class='kw'>else</span>
473
- <span class='id identifier rubyid_raise'>raise</span>
474
- <span class='kw'>end</span>
475
- <span class='kw'>end</span>
476
- <span class='kw'>end</span></pre>
477
- </td>
478
- </tr>
479
- </table>
480
- </div>
481
-
482
- </div>
483
-
484
- </div>
485
-
486
- <div id="footer">
487
- Generated on Tue Oct 1 21:19:37 2019 by
488
- <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
489
- 0.9.20 (ruby-2.5.5).
490
- </div>
491
-
492
- </div>
493
- </body>
494
- </html>