pwned 1.2.1 → 2.2.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 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