pwned 1.2.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b6891bb23ef6923eaad6c5af0b4fa2d36471c9a6903fbb66940720c6a5a69c2b
4
- data.tar.gz: d138630dcac1204a01adfa731cce9a52ca984b67544f4c0beabe3e51f56d28ed
3
+ metadata.gz: 1790330e2068217b48ba0929c4b2409dfecd939e76f7d231acc872ef9802320a
4
+ data.tar.gz: afa13cc83a53df1be859a74876e00992b15eed757e571bec652168d85b3ab73e
5
5
  SHA512:
6
- metadata.gz: a89fca08322c92a4866d774dd5d2bce8d495326a99d7dfa8b2e8fe12b43ab3d57c0ed29f9aa8dab5d94ed5c002127701cd57daa43f9a621804232c02a4122d15
7
- data.tar.gz: 67a0aaf46f9d84ee81b36e39e72d761aa7b08293246d7fec3abb35bb122d38cf2cd17519850b97573d16bbe346680de6478b1a569c47a1600085b4167e7faa51
6
+ metadata.gz: 23482aeeab95bb130ba04f1fdc57f769282d78c7c4c56c594f15652c472ffce9573967d8a31d539548a325c85f54aa038d65bb023aded41352147ad9a906534e
7
+ data.tar.gz: d31fb7ad5171adc4cc8ee28ed97d125fbd19c93bc68e65e7921076d1fb586748a1a4f12007e70cbfe6378e3c868effa756efaa08e627bfa82b2c73166e0b7dd1
@@ -0,0 +1 @@
1
+ github: philnash
@@ -0,0 +1,39 @@
1
+ name: tests
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ runs-on: ubuntu-latest
8
+ strategy:
9
+ fail-fast: false
10
+ matrix:
11
+ ruby: [2.5, 2.6, 2.7, 3.0, head]
12
+ rails: [4.2.11.3, 5.0.7.2, 5.1.7, 5.2.4.4, 6.0.3.4, 6.1.0]
13
+ exclude:
14
+ # Ruby 3.0 and Rails 5 do not get along together.
15
+ - ruby: 3.0
16
+ rails: 5.0.7.2
17
+ - ruby: 3.0
18
+ rails: 5.1.7
19
+ - ruby: 3.0
20
+ rails: 5.2.4.4
21
+ - ruby: head
22
+ rails: 5.0.7.2
23
+ - ruby: head
24
+ rails: 5.1.7
25
+ - ruby: head
26
+ rails: 5.2.4.4
27
+ continue-on-error: ${{ endsWith(matrix.ruby, 'head') }}
28
+ env:
29
+ RAILS_VERSION: ${{ matrix.rails }}
30
+ steps:
31
+ - uses: actions/checkout@v2
32
+ - name: Set up Ruby ${{ matrix.ruby }}
33
+ uses: ruby/setup-ruby@v1
34
+ with:
35
+ ruby-version: ${{ matrix.ruby }}
36
+ - name: "Install dependencies (rails: ${{matrix.rails}})"
37
+ run: bundle install
38
+ - name: Run tests
39
+ run: bundle exec rspec
data/CHANGELOG.md CHANGED
@@ -1,31 +1,88 @@
1
1
  # Changelog for `Pwned`
2
2
 
3
- ## Ongoing [☰](https://github.com/philnash/pwned/compare/v1.2.1...master)
3
+ ## Ongoing [☰](https://github.com/philnash/pwned/compare/v2.0.2...master)
4
4
 
5
- ...
5
+ ## 2.2.0 (March 27, 2021) [☰](https://github.com/philnash/pwned/compare/v2.1.0...v2.2.0)
6
6
 
7
- ## 1.2.1 (March 17, 2018) [☰](https://github.com/philnash/pwned/commits/v1.2.1)
7
+ - Minor updates
8
8
 
9
- * Minor updates
10
- * Validator no longer raises `TypeError` when password is `nil`
9
+ - Adds `:proxy` option to `request_options` to directly set a proxy on the
10
+ request. Fixes #21, thanks [dparpyani](https://github.com/dparpyani).
11
11
 
12
- ## 1.2.0 (March 15, 2018) [☰](https://github.com/philnash/pwned/commits/v1.2.0)
12
+ ## 2.1.0 (July 8, 2020) [☰](https://github.com/philnash/pwned/compare/v2.0.2...v2.1.0)
13
13
 
14
- * Major updates
15
- * Changes `PwnedValidator` to `NotPwnedValidator`, so that the validation looks like `validates :password, not_pwned: true`. `PwnedValidator` now subclasses `NotPwnedValidator` for backwards compatibility with version 1.1.0 but is deprecated.
14
+ - Minor updates
16
15
 
17
- ## 1.1.0 (March 12, 2018) [☰](https://github.com/philnash/pwned/commits/v1.1.0)
16
+ - Adds `Pwned::HashedPassword` class which is initializd with a SHA1 hash to
17
+ query the API with so that the lookup can be done in the background without
18
+ storing passwords. Fixes #19, thanks [@paprikati](https://github.com/paprikati).
18
19
 
19
- * Major updates
20
- * Refactors exception handling with built in Ruby method ([PR #1](https://github.com/philnash/pwned/pull/1) thanks [@kpumuk](https://github.com/kpumuk))
21
- * Passwords must be strings, the initializer will raise a `TypeError` unless `password.is_a? String`. ([dbf7697](https://github.com/philnash/pwned/commit/dbf7697e878d87ac74aed1e715cee19b73473369))
22
- * Added Ruby on Rails validator ([PR #3](https://github.com/philnash/pwned/pull/3) & [PR #6](https://github.com/philnash/pwned/pull/6))
23
- * Added simplified accessors `Pwned.pwned?` and `Pwned.pwned_count` ([PR #4](https://github.com/philnash/pwned/pull/4))
20
+ ## 2.0.2 (May 20, 2020) [☰](https://github.com/philnash/pwned/compare/v2.0.1...v2.0.2)
24
21
 
25
- * Minor updates
26
- * SHA1 is only calculated once
27
- * Frozen string literal to make sure Ruby does not copy strings over and over again
28
- * Removal of `@match_data`, since we only use it to retrieve the counter. Caching the counter instead (all [PR #2](https://github.com/philnash/pwned/pull/2) thanks [@kpumuk](https://github.com/kpumuk))
22
+ - Minor fix
23
+
24
+ - It was found to be possible for reading the lines body of a response to
25
+ result in a `nil` which caused trouble with string concatenation. This
26
+ avoids that scenario. Fixes #18, thanks [@flori](https://github.com/flori).
27
+
28
+ ## 2.0.1 (January 14, 2020) [☰](https://github.com/philnash/pwned/compare/v2.0.0...v2.0.1)
29
+
30
+ - Minor updates
31
+
32
+ - Adds double-splat to ActiveModel::Errors#add calls with options to make Ruby 2.7 happy.
33
+ - Detects presence of Net::HTTPClientException in tests to remove deprecation warning.
34
+
35
+ ## 2.0.0 (October 1, 2019) [☰](https://github.com/philnash/pwned/compare/v1.2.1...v2.0.0)
36
+
37
+ - Major updates
38
+
39
+ - Switches from `open-uri` to `Net::HTTP`. This is a potentially breaking change.
40
+ - `request_options` are now used to configure `Net::HTTP.start`.
41
+ - Rather than using all string keys from `request_options`, HTTP headers are now
42
+ specified in their own `headers` hash. To upgrade, any options intended as
43
+ headers need to be extracted into a `headers` hash, e.g.
44
+
45
+ ```diff
46
+ validates :password, not_pwned: {
47
+ - request_options: { read_timeout: 5, open_timeout: 1, "User-Agent" => "Super fun user agent" }
48
+ + request_options: { read_timeout: 5, open_timeout: 1, headers: { "User-Agent" => "Super fun user agent" } }
49
+ }
50
+
51
+ - password = Pwned::Password.new("password", 'User-Agent' => 'Super fun new user agent')
52
+ + password = Pwned::Password.new("password", headers: { 'User-Agent' => 'Super fun new user agent' }, read_timeout: 10)
53
+ ```
54
+
55
+ - Adds a CLI to let you check passwords on the command line
56
+
57
+ ```bash
58
+ $ pwned password
59
+ Pwned!
60
+ The password has been found in public breaches 3730471 times.
61
+ ```
62
+
63
+ ## 1.2.1 (March 17, 2018) [☰](https://github.com/philnash/pwned/compare/v1.2.0...v1.2.1)
64
+
65
+ - Minor updates
66
+ - Validator no longer raises `TypeError` when password is `nil`
67
+
68
+ ## 1.2.0 (March 15, 2018) [☰](https://github.com/philnash/pwned/compare/v1.1.0...v1.2.0)
69
+
70
+ - Major updates
71
+ - Changes `PwnedValidator` to `NotPwnedValidator`, so that the validation looks like `validates :password, not_pwned: true`. `PwnedValidator` now subclasses `NotPwnedValidator` for backwards compatibility with version 1.1.0 but is deprecated.
72
+
73
+ ## 1.1.0 (March 12, 2018) [☰](https://github.com/philnash/pwned/compare/v1.0.0...v1.1.0)
74
+
75
+ - Major updates
76
+
77
+ - Refactors exception handling with built in Ruby method ([PR #1](https://github.com/philnash/pwned/pull/1) thanks [@kpumuk](https://github.com/kpumuk))
78
+ - Passwords must be strings, the initializer will raise a `TypeError` unless `password.is_a? String`. ([dbf7697](https://github.com/philnash/pwned/commit/dbf7697e878d87ac74aed1e715cee19b73473369))
79
+ - Added Ruby on Rails validator ([PR #3](https://github.com/philnash/pwned/pull/3) & [PR #6](https://github.com/philnash/pwned/pull/6))
80
+ - Added simplified accessors `Pwned.pwned?` and `Pwned.pwned_count` ([PR #4](https://github.com/philnash/pwned/pull/4))
81
+
82
+ - Minor updates
83
+ - SHA1 is only calculated once
84
+ - Frozen string literal to make sure Ruby does not copy strings over and over again
85
+ - Removal of `@match_data`, since we only use it to retrieve the counter. Caching the counter instead (all [PR #2](https://github.com/philnash/pwned/pull/2) thanks [@kpumuk](https://github.com/kpumuk))
29
86
 
30
87
  ## 1.0.0 (March 6, 2018) [☰](https://github.com/philnash/pwned/commits/v1.0.0)
31
88
 
data/README.md CHANGED
@@ -2,9 +2,34 @@
2
2
 
3
3
  An easy, Ruby way to use the Pwned Passwords API.
4
4
 
5
- [![Gem Version](https://badge.fury.io/rb/pwned.svg)](https://rubygems.org/gems/pwned) [![Build Status](https://travis-ci.org/philnash/pwned.svg?branch=master)](https://travis-ci.org/philnash/pwned) [![Maintainability](https://codeclimate.com/github/philnash/pwned/badges/gpa.svg)](https://codeclimate.com/github/philnash/pwned/maintainability) [![Inline docs](https://inch-ci.org/github/philnash/pwned.svg?branch=master)](https://inch-ci.org/github/philnash/pwned)
6
-
7
- [API docs](https://philnash.github.io/pwned/) | [GitHub repo](https://github.com/philnash/pwned)
5
+ [![Gem Version](https://badge.fury.io/rb/pwned.svg)](https://rubygems.org/gems/pwned) ![Build Status](https://github.com/philnash/pwned/workflows/tests/badge.svg) [![Maintainability](https://codeclimate.com/github/philnash/pwned/badges/gpa.svg)](https://codeclimate.com/github/philnash/pwned/maintainability) [![Inline docs](https://inch-ci.org/github/philnash/pwned.svg?branch=master)](https://inch-ci.org/github/philnash/pwned)
6
+
7
+ [API docs](https://www.rubydoc.info/gems/pwned) | [GitHub repo](https://github.com/philnash/pwned)
8
+
9
+ ## Table of Contents
10
+
11
+ - [Pwned](#pwned)
12
+ - [Table of Contents](#table-of-contents)
13
+ - [About](#about)
14
+ - [Installation](#installation)
15
+ - [Usage](#usage)
16
+ - [Plain Ruby](#plain-ruby)
17
+ - [Advanced](#advanced)
18
+ - [ActiveRecord Validator](#activerecord-validator)
19
+ - [I18n](#i18n)
20
+ - [Threshold](#threshold)
21
+ - [Network Error Handling](#network-error-handling)
22
+ - [Custom Request Options](#custom-request-options)
23
+ - [Using Asynchronously](#using-asynchronously)
24
+ - [Devise](#devise)
25
+ - [Rodauth](#rodauth)
26
+ - [Command line](#command-line)
27
+ - [Unpwn](#unpwn)
28
+ - [How Pwned is Pi?](#how-pwned-is-pi)
29
+ - [Development](#development)
30
+ - [Contributing](#contributing)
31
+ - [License](#license)
32
+ - [Code of Conduct](#code-of-conduct)
8
33
 
9
34
  ## About
10
35
 
@@ -14,6 +39,8 @@ Troy Hunt's [Pwned Passwords API V2](https://haveibeenpwned.com/API/v2#PwnedPass
14
39
 
15
40
  The data from this API is provided by [Have I been pwned?](https://haveibeenpwned.com/). Before using the API, please check [the acceptable uses and license of the API](https://haveibeenpwned.com/API/v2#AcceptableUse).
16
41
 
42
+ Here is a blog post I wrote on [how to use this gem in your Ruby applications to make your users' passwords better](https://www.twilio.com/blog/2018/03/better-passwords-in-ruby-applications-pwned-passwords-api.html).
43
+
17
44
  ## Installation
18
45
 
19
46
  Add this line to your application's Gemfile:
@@ -32,6 +59,14 @@ Or install it yourself as:
32
59
 
33
60
  ## Usage
34
61
 
62
+ There are a few ways you can use this gem:
63
+
64
+ 1. [Plain Ruby](#plain-ruby)
65
+ 2. [Rails](#activerecord-validator)
66
+ 3. [Rails and Devise](#devise)
67
+
68
+ ### Plain Ruby
69
+
35
70
  To test a password against the API, instantiate a `Pwned::Password` object and then ask if it is `pwned?`.
36
71
 
37
72
  ```ruby
@@ -72,10 +107,11 @@ Pwned.pwned_count("password")
72
107
 
73
108
  #### Advanced
74
109
 
75
- You can set options and headers to be used with `open-uri` when making the request to the API. HTTP headers must be string keys and the [other options are available in the `OpenURI::OpenRead` module](https://ruby-doc.org/stdlib-2.5.0/libdoc/open-uri/rdoc/OpenURI/OpenRead.html#method-i-open).
110
+ You can set http request options to be used with `Net::HTTP.start` when making the request to the API. These options are
111
+ documented in the [`Net::HTTP.start` documentation](http://ruby-doc.org/stdlib-2.6.3/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start). The `:headers` option defines defines HTTP headers. These headers must be string keys.
76
112
 
77
113
  ```ruby
78
- password = Pwned::Password.new("password", { 'User-Agent' => 'Super fun new user agent' })
114
+ password = Pwned::Password.new("password", headers: { 'User-Agent' => 'Super fun new user agent' }, read_timeout: 10)
79
115
  ```
80
116
 
81
117
  ### ActiveRecord Validator
@@ -113,7 +149,7 @@ class User < ApplicationRecord
113
149
  end
114
150
  ```
115
151
 
116
- #### Network Errors Handling
152
+ #### Network Error Handling
117
153
 
118
154
  By default the record will be treated as valid when we cannot reach the [haveibeenpwned.com](https://haveibeenpwned.com/) servers. This can be changed with the `:on_error` validator parameter:
119
155
 
@@ -145,17 +181,124 @@ end
145
181
 
146
182
  #### Custom Request Options
147
183
 
148
- You can configure network requests made from the validator using `:request_options` (see [OpenURI::OpenRead#open](http://ruby-doc.org/stdlib-2.5.0/libdoc/open-uri/rdoc/OpenURI/OpenRead.html#method-i-open) for the list of available options, string keys represent custom network request headers, e.g. `"User-Agent"`):
184
+ You can configure network requests made from the validator using `:request_options` (see [Net::HTTP.start](http://ruby-doc.org/stdlib-2.6.3/libdoc/net/http/rdoc/Net/HTTP.html#method-c-start) for the list of available options).
185
+ In addition to these options, HTTP headers can be specified with the `:headers` key (e.g. `"User-Agent"`) and proxy can be specified with the `:proxy` key:
149
186
 
150
187
  ```ruby
151
188
  validates :password, not_pwned: {
152
- request_options: { read_timeout: 5, open_timeout: 1, "User-Agent" => "Super fun user agent" }
189
+ request_options: {
190
+ read_timeout: 5,
191
+ open_timeout: 1,
192
+ headers: { "User-Agent" => "Super fun user agent" },
193
+ proxy: "https://username:password@example.com:12345"
194
+ }
153
195
  }
154
196
  ```
155
197
 
156
- ## TODO
198
+ ### Using Asynchronously
199
+
200
+ You may have a use case for hashing the password in advance, and then making the call to the Pwned Passwords API later (for example if you want to enqueue a job without storing the plaintext password). To do this, you can hash the password with the `Pwned.hash_password` method and then initialize the `Pwned::HashPassword` class with the hash, like this:
201
+
202
+ ```ruby
203
+ hashed_password = Pwned.hash_password(password)
204
+ # some time later
205
+ Pwned::HashPassword.new(hashed_password, request_options).pwned?
206
+ ```
207
+
208
+ ### Devise
209
+
210
+ If you are using [Devise](https://github.com/heartcombo/devise) I recommend you use the [devise-pwned_password extension](https://github.com/michaelbanfield/devise-pwned_password) which is now powered by this gem.
211
+
212
+ ### Rodauth
213
+
214
+ If you are using [Rodauth](https://github.com/jeremyevans/rodauth) then you can use the [rodauth-pwned](https://github.com/janko/rodauth-pwned) feature which is powered by this gem.
215
+
216
+ ### Command line
217
+
218
+ The gem provides a command line utility for checking passwords. You can call it from your terminal application like this:
219
+
220
+ ```bash
221
+ $ pwned password
222
+ Pwned!
223
+ The password has been found in public breaches 3645804 times.
224
+ ```
225
+
226
+ If you don't want the password you are checking to be visible, call:
157
227
 
158
- - [ ] Devise plugin
228
+ ```bash
229
+ $ pwned --secret
230
+ ```
231
+
232
+ You will be prompted for the password, but it won't be displayed.
233
+
234
+ ### Unpwn
235
+
236
+ To cut down on unnecessary network requests, [the unpwn project](https://github.com/indirect/unpwn) uses a list of the top one million passwords to check passwords against. Only if a password is not included in the top million is it then checked against the Pwned Passwords API.
237
+
238
+ ## How Pwned is Pi?
239
+
240
+ [@daz](https://github.com/daz) [shared](https://twitter.com/dazonic/status/1074647842046660609) a fantastic example of using this gem to show how many times the digits of Pi have been used as passwords and leaked.
241
+
242
+ ```ruby
243
+ require 'pwned'
244
+
245
+ PI = '3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111'
246
+
247
+ for n in 1..40
248
+ password = Pwned::Password.new PI[0..(n + 1)]
249
+ str = [ n.to_s.rjust(2) ]
250
+ str << (password.pwned? ? '😡' : '😃')
251
+ str << password.pwned_count.to_s.rjust(4)
252
+ str << password.password
253
+
254
+ puts str.join ' '
255
+ end
256
+ ```
257
+
258
+ The results may, or may not, surprise you.
259
+
260
+ ```
261
+ 1 😡 16 3.1
262
+ 2 😡 238 3.14
263
+ 3 😡 34 3.141
264
+ 4 😡 1345 3.1415
265
+ 5 😡 2552 3.14159
266
+ 6 😡 791 3.141592
267
+ 7 😡 9582 3.1415926
268
+ 8 😡 1591 3.14159265
269
+ 9 😡 637 3.141592653
270
+ 10 😡 873 3.1415926535
271
+ 11 😡 137 3.14159265358
272
+ 12 😡 103 3.141592653589
273
+ 13 😡 65 3.1415926535897
274
+ 14 😡 201 3.14159265358979
275
+ 15 😡 41 3.141592653589793
276
+ 16 😡 57 3.1415926535897932
277
+ 17 😡 28 3.14159265358979323
278
+ 18 😡 29 3.141592653589793238
279
+ 19 😡 1 3.1415926535897932384
280
+ 20 😡 7 3.14159265358979323846
281
+ 21 😡 5 3.141592653589793238462
282
+ 22 😡 2 3.1415926535897932384626
283
+ 23 😡 2 3.14159265358979323846264
284
+ 24 😃 0 3.141592653589793238462643
285
+ 25 😡 3 3.1415926535897932384626433
286
+ 26 😃 0 3.14159265358979323846264338
287
+ 27 😃 0 3.141592653589793238462643383
288
+ 28 😃 0 3.1415926535897932384626433832
289
+ 29 😃 0 3.14159265358979323846264338327
290
+ 30 😃 0 3.141592653589793238462643383279
291
+ 31 😃 0 3.1415926535897932384626433832795
292
+ 32 😃 0 3.14159265358979323846264338327950
293
+ 33 😃 0 3.141592653589793238462643383279502
294
+ 34 😃 0 3.1415926535897932384626433832795028
295
+ 35 😃 0 3.14159265358979323846264338327950288
296
+ 36 😃 0 3.141592653589793238462643383279502884
297
+ 37 😃 0 3.1415926535897932384626433832795028841
298
+ 38 😃 0 3.14159265358979323846264338327950288419
299
+ 39 😃 0 3.141592653589793238462643383279502884197
300
+ 40 😃 0 3.1415926535897932384626433832795028841971
301
+ ```
159
302
 
160
303
  ## Development
161
304
 
data/bin/pwned ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pwned"
4
+ require "optparse"
5
+ require "io/console"
6
+
7
+ options = {}
8
+ parser = OptionParser.new do |opts|
9
+ opts.banner = <<-USAGE
10
+ Usage: pwned <password>
11
+
12
+ Tests a password against the Pwned Passwords API using the k-anonymity model,
13
+ which avoids sending the entire password to the service.opts
14
+
15
+ If the password has been found in a publicly available breach then this tool
16
+ will report how many times it has been seen. Otherwise the tool will report that
17
+ the password has not been found in a public breach yet.
18
+
19
+ USAGE
20
+
21
+ opts.version = Pwned::VERSION
22
+
23
+ opts.on("-s", "--secret", "Enter password without displaying characters.\n#{" "* 37}Overrides provided arguments.")
24
+ opts.on_tail("-h", "--help", "Show help.")
25
+ opts.on_tail("-v", "--version", "Show version number.\n\n")
26
+ end
27
+
28
+ parser.parse!(ARGV, into: options)
29
+
30
+ if options[:help]
31
+ puts parser.help
32
+ exit
33
+ end
34
+ if options[:version]
35
+ puts parser.ver
36
+ exit
37
+ end
38
+ password_to_test = ARGV.first
39
+ if options[:secret]
40
+ password_to_test = STDIN.getpass("Password: ")
41
+ end
42
+ if !password_to_test || password_to_test.strip == ""
43
+ puts parser.help
44
+ exit
45
+ end
46
+ password = Pwned::Password.new(password_to_test || ARGV.first)
47
+ if password.pwned?
48
+ puts "Pwned!\nThe password has been found in public breaches #{password.pwned_count} times."
49
+ else
50
+ puts "The password has not been found in a public breach."
51
+ end
52
+
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
@@ -29,10 +31,10 @@ module Pwned
29
31
  # Pwned.pwned?("pwned::password") #=> false
30
32
  #
31
33
  # @param password [String] The password you want to check against the API.
32
- # @param [Hash] request_options Options that can be passed to +open+ when
34
+ # @param [Hash] request_options Options that can be passed to +Net::HTTP.start+ when
33
35
  # calling the API
34
- # @option request_options [String] 'User-Agent' ("Ruby Pwned::Password #{Pwned::VERSION}")
35
- # The user agent used when making an API request.
36
+ # @option request_options [Symbol] :headers ({ "User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}" })
37
+ # HTTP headers to include in the request
36
38
  # @return [Boolean] Whether the password appears in the data breaches or not.
37
39
  # @since 1.1.0
38
40
  def self.pwned?(password, request_options={})
@@ -47,14 +49,28 @@ module Pwned
47
49
  # Pwned.pwned_count("pwned::password") #=> 0
48
50
  #
49
51
  # @param password [String] The password you want to check against the API.
50
- # @param [Hash] request_options Options that can be passed to +open+ when
52
+ # @param [Hash] request_options Options that can be passed to +Net::HTTP.start+ when
51
53
  # calling the API
52
- # @option request_options [String] 'User-Agent' ("Ruby Pwned::Password #{Pwned::VERSION}")
53
- # The user agent used when making an API request.
54
+ # @option request_options [Symbol] :headers ({ "User-Agent" => "Ruby Pwned::Password #{Pwned::VERSION}" })
55
+ # HTTP headers to include in the request
54
56
  # @return [Integer] The number of times the password has appeared in the data
55
57
  # breaches.
56
58
  # @since 1.1.0
57
59
  def self.pwned_count(password, request_options={})
58
60
  Pwned::Password.new(password, request_options).pwned_count
59
61
  end
62
+
63
+ ##
64
+ # Returns the full SHA1 hash of the given password in uppercase. This can be safely passed around your code
65
+ # before making the pwned request (e.g. dropped into a queue table).
66
+ #
67
+ # @example
68
+ # Pwned.hash_password("password") #=> 5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8
69
+ #
70
+ # @param password [String] The password you want to check against the API
71
+ # @return [String] An uppercase SHA1 hash of the password
72
+ # @since 2.1.0
73
+ def self.hash_password(password)
74
+ Digest::SHA1.hexdigest(password).upcase
75
+ end
60
76
  end