darrrr 0.0.2
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/LICENSE +21 -0
- data/README.md +203 -0
- data/Rakefile +42 -0
- data/lib/darrrr.rb +9 -0
- data/lib/github/delegated_account_recovery.rb +178 -0
- data/lib/github/delegated_account_recovery/account_provider.rb +140 -0
- data/lib/github/delegated_account_recovery/constants.rb +19 -0
- data/lib/github/delegated_account_recovery/crypto_helper.rb +57 -0
- data/lib/github/delegated_account_recovery/cryptors/default/default_encryptor.rb +66 -0
- data/lib/github/delegated_account_recovery/cryptors/default/encrypted_data.rb +92 -0
- data/lib/github/delegated_account_recovery/cryptors/default/encrypted_data_io.rb +12 -0
- data/lib/github/delegated_account_recovery/provider.rb +125 -0
- data/lib/github/delegated_account_recovery/recovery_provider.rb +118 -0
- data/lib/github/delegated_account_recovery/recovery_token.rb +113 -0
- data/lib/github/delegated_account_recovery/serialization/recovery_token_reader.rb +22 -0
- data/lib/github/delegated_account_recovery/serialization/recovery_token_writer.rb +22 -0
- data/lib/github/delegated_account_recovery/version.rb +5 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b73c12978f19a57c566f6cdc1e3caf2409307b89
|
4
|
+
data.tar.gz: 84ea2347b3d30a53489cfafad46b7aa4d5d4dc13
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5c224e64fc58c14ee6dc13272274d74f8439d20d73781770b007d13db567dfed10760a739737bf07922e0ac8f04496cac578a4facb58409167329dd3626031f6
|
7
|
+
data.tar.gz: f2800490548b13ae9d5da99bcce76552f508b51975f2cf864427802ed895288c734eae0f30044c8ade4351fe41eacd5af8bf6f1b2b1a1f12e69426d38d3f1fb8
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2017 GitHub
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
[](https://travis-ci.org/github/darrrr) [](https://codeclimate.com/github/github/darrrr)
|
2
|
+
|
3
|
+
The Delegated Account Recovery Rigid Reusable Ruby (aka D.a.r.r.r.r. or "Darrrr") library is meant to be used as the fully-complete plumbing in your Rack application when implementing the [Delegated Account Recovery specification](https://github.com/facebook/DelegatedRecoverySpecification). This library is currently used for the implementation at [GitHub](https://githubengineering.com/recover-accounts-elsewhere/).
|
4
|
+
|
5
|
+
Along with a fully featured library, a proof of concept application is provided in this repo.
|
6
|
+
|
7
|
+
## Configuration
|
8
|
+
|
9
|
+
An account provider (e.g. GitHub) is someone who stores a token with someone else (a recovery provider e.g. Facebook) in order to grant access to an account.
|
10
|
+
|
11
|
+
In `config/initializers` or any location that is run during application setup, add a file:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
Darrrr.authority = "http://localhost:9292"
|
15
|
+
Darrrr.privacy_policy = "#{Darrrr.authority}/articles/github-privacy-statement/"
|
16
|
+
Darrrr.icon_152px = "#{Darrrr.authority}/icon.png"
|
17
|
+
|
18
|
+
# See script/setup for instructions on how to generate keys
|
19
|
+
Darrrr::AccountProvider.configure do |config|
|
20
|
+
config.signing_private_key = ENV["ACCOUNT_PROVIDER_PRIVATE_KEY"]
|
21
|
+
config.symmetric_key = ENV["TOKEN_DATA_AES_KEY"]
|
22
|
+
config.tokensign_pubkeys_secp256r1 = [ENV["ACCOUNT_PROVIDER_PUBLIC_KEY"]]
|
23
|
+
config.save_token_return = "#{Darrrr.authority}/account-provider/save-token-return"
|
24
|
+
config.recover_account_return = "#{Darrrr.authority}/account-provider/recover-account-return"
|
25
|
+
end
|
26
|
+
|
27
|
+
Darrrr::RecoveryProvider.configure do |config|
|
28
|
+
config.signing_private_key = ENV["RECOVERY_PROVIDER_PRIVATE_KEY"]
|
29
|
+
config.countersign_pubkeys_secp256r1 = [ENV["RECOVERY_PROVIDER_PUBLIC_KEY"]]
|
30
|
+
config.token_max_size = 8192
|
31
|
+
config.save_token = "#{Darrrr.authority}/recovery-provider/save-token"
|
32
|
+
config.recover_account = "#{Darrrr.authority}/recovery-provider/recover-account"
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
The delegated recovery spec depends on publicly available endpoints serving standard configs. These responses can be cached but are not by default. To configure your cache store, provide the reference:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
Darrrr.cache = Dalli::Client.new('localhost:11211', options)
|
40
|
+
```
|
41
|
+
|
42
|
+
The spec disallows `http` URIs for basic security, but sometimes we don't have this setup locally.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Darrrr.allow_unsafe_urls = true
|
46
|
+
```
|
47
|
+
|
48
|
+
## Provider registration
|
49
|
+
|
50
|
+
In order to allow a site to act as a provider, it must be "registered" on boot to prevent unauthorized providers from managing tokens.
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
# Only configure this if you are acting as a recovery provider
|
54
|
+
Darrrr.register_account_provider("https://github.com")
|
55
|
+
|
56
|
+
# Only configure this if you are acting as an account provider
|
57
|
+
Darrrr.register_recovery_provider("https://www.facebook.com")
|
58
|
+
```
|
59
|
+
|
60
|
+
## Custom crypto
|
61
|
+
|
62
|
+
Create a module that responds to `Module.sign`, `Module.verify`, `Module.decrypt`, and `Module.encrypt`. You can use the template below. I recommend leaving the `#verify` method as is unless you have a compelling reason to override it.
|
63
|
+
|
64
|
+
### Global config
|
65
|
+
|
66
|
+
Set `Darrrr.custom_encryptor = MyCustomEncryptor`
|
67
|
+
|
68
|
+
### On-demand
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
Darrrr.with_encryptor(MyCustomEncryptor) do
|
72
|
+
# perform DAR actions using MyCustomEncryptor as the crypto provider
|
73
|
+
token = Darrrr.this_account_provider.generate_recovery_token(data: "foo", audience: recovery_provider)
|
74
|
+
end
|
75
|
+
```
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
module MyCustomEncryptor
|
79
|
+
class << self
|
80
|
+
# Encrypts the data in an opaque way
|
81
|
+
#
|
82
|
+
# data: the secret to be encrypted
|
83
|
+
#
|
84
|
+
# returns a byte array representation of the data
|
85
|
+
def encrypt(data)
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
# Decrypts the data
|
90
|
+
#
|
91
|
+
# ciphertext: the byte array to be decrypted
|
92
|
+
#
|
93
|
+
# returns a string
|
94
|
+
def decrypt(ciphertext)
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
# payload: binary serialized recovery token (to_binary_s).
|
99
|
+
#
|
100
|
+
# key: the private EC key used to sign the token
|
101
|
+
#
|
102
|
+
# returns signature in ASN.1 DER r + s sequence
|
103
|
+
def sign(payload, key)
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
# payload: token in binary form
|
108
|
+
# signature: signature of the binary token
|
109
|
+
# key: the EC public key used to verify the signature
|
110
|
+
#
|
111
|
+
# returns true if signature validates the payload
|
112
|
+
def verify(payload, signature, key)
|
113
|
+
# typically, the default verify function should be used to ensure compatibility
|
114
|
+
Darrrr::DefaultEncryptor.verify(payload, signature, key)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
```
|
119
|
+
|
120
|
+
## Example implementation
|
121
|
+
|
122
|
+
I strongly suggest you read the specification, specifically section 3.1 (save-token) and 3.5 (recover account) as they contain the most dangerous operations.
|
123
|
+
|
124
|
+
**NOTE:** this is NOT meant to be a complete implementation, it is just the starting point. Crucial aspects such as authentication, audit logging, out of band notifications, and account provider persistence are not implemented.
|
125
|
+
|
126
|
+
* [Account Provider](controllers/account_provider_controller.rb) (save-token-return, recover-account-return)
|
127
|
+
* [Recovery Provider](controllers/recovery_provider_controller.rb) (save-token, recover-account)
|
128
|
+
* [Configuration endpoint](controllers/well_known_config_controller.rb) (`/.well-known/delegated-account-recovery/configuration`)
|
129
|
+
|
130
|
+
Specifically, the gem exposes the following APIs for manipulating tokens.
|
131
|
+
* Account Provider
|
132
|
+
* [Generating](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/account_provider.rb#L49) a token
|
133
|
+
* Signing ([`#seal`](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/crypto_helper.rb#L13)) a token
|
134
|
+
* Verifying ([`#unseal`](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/crypto_helper.rb#L30)) a countersigned token
|
135
|
+
* Recovery Provider
|
136
|
+
* Verifying ([`#unseal`](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/crypto_helper.rb#L30)) a token
|
137
|
+
* [Countersigning](https://github.com/github/darrrr/blob/faafda5b1773e077c9c10b55b46216f97d13cd3b/lib/github/delegated_account_recovery/recovery_provider.rb#L60) a token
|
138
|
+
|
139
|
+
### Development
|
140
|
+
|
141
|
+
Local development assumes a Mac OS environment with [homebrew](https://brew.sh/) available. Postgres and phantom JS will be installed.
|
142
|
+
|
143
|
+
Run `./script/bootstrap` then run `./script/server`
|
144
|
+
|
145
|
+
* Visit `http://localhost:9292/account-provider`
|
146
|
+
* (Optionally) Record the random number for verification
|
147
|
+
* Click "connect to http://localhost:9292"
|
148
|
+
* You'll see some debug information on the page.
|
149
|
+
* Click "setup recovery".
|
150
|
+
* If recovery setup was successful, click "Recovery Setup Successful"
|
151
|
+
* Click the "recover now?" link
|
152
|
+
* You'll see an intermediate page, where more debug information is presented. Click "recover token"
|
153
|
+
* You should be sent back to your host
|
154
|
+
* And see something like `Recovered data: <the secret from step 1>`
|
155
|
+
|
156
|
+
### Tests
|
157
|
+
|
158
|
+
Run `./script/test` to run all tests.
|
159
|
+
|
160
|
+
## Deploying to heroku
|
161
|
+
|
162
|
+
Use `heroku config:set` to set the environment variables listed in [script/setup](/script/setup). Additionally, run:
|
163
|
+
|
164
|
+
```
|
165
|
+
heroku config:set HOST_URL=$(heroku info -s | grep web_url | cut -d= -f2)
|
166
|
+
```
|
167
|
+
|
168
|
+
Push your app to heroku:
|
169
|
+
|
170
|
+
```
|
171
|
+
git push heroku <branch-name>:master
|
172
|
+
```
|
173
|
+
|
174
|
+
Migrate the database:
|
175
|
+
|
176
|
+
```
|
177
|
+
heroku run rake db:migrate
|
178
|
+
```
|
179
|
+
|
180
|
+
Use the app!
|
181
|
+
|
182
|
+
```
|
183
|
+
heroku restart
|
184
|
+
heroku open
|
185
|
+
```
|
186
|
+
|
187
|
+
## Roadmap
|
188
|
+
|
189
|
+
* Add support for `token-status` endpoints as defined by the spec
|
190
|
+
* Add async API as defined by the spec
|
191
|
+
* Implement token binding as part of the async API
|
192
|
+
|
193
|
+
## Don't want to run `./script` entries?
|
194
|
+
|
195
|
+
See `script/setup` for the environment variables that need to be set.
|
196
|
+
|
197
|
+
## Contributions
|
198
|
+
|
199
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
200
|
+
|
201
|
+
## License
|
202
|
+
|
203
|
+
`darrrr` is licensed under the [MIT license](LICENSE.md).
|
data/Rakefile
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
require_relative "app"
|
8
|
+
require_relative "lib/darrrr"
|
9
|
+
require "sinatra/activerecord/rake"
|
10
|
+
|
11
|
+
namespace :db do
|
12
|
+
task :load_config do
|
13
|
+
require "./app"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
unless ENV["RACK_ENV"] == "production"
|
19
|
+
require 'rspec/core/rake_task'
|
20
|
+
desc "Run RSpec"
|
21
|
+
RSpec::Core::RakeTask.new do |t|
|
22
|
+
t.verbose = false
|
23
|
+
t.rspec_opts = "--format progress"
|
24
|
+
end
|
25
|
+
|
26
|
+
task default: :spec
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rdoc/task'
|
31
|
+
rescue LoadError
|
32
|
+
require 'rdoc/rdoc'
|
33
|
+
require 'rake/rdoctask'
|
34
|
+
RDoc::Task = Rake::RDocTask
|
35
|
+
end
|
36
|
+
|
37
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
38
|
+
rdoc.rdoc_dir = 'rdoc'
|
39
|
+
rdoc.title = 'SecureHeaders'
|
40
|
+
rdoc.options << '--line-numbers'
|
41
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
42
|
+
end
|
data/lib/darrrr.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bindata"
|
4
|
+
require "openssl"
|
5
|
+
|
6
|
+
require_relative "delegated_account_recovery/constants"
|
7
|
+
require_relative "delegated_account_recovery/crypto_helper"
|
8
|
+
require_relative "delegated_account_recovery/recovery_token"
|
9
|
+
require_relative "delegated_account_recovery/provider"
|
10
|
+
require_relative "delegated_account_recovery/account_provider"
|
11
|
+
require_relative "delegated_account_recovery/recovery_provider"
|
12
|
+
require_relative "delegated_account_recovery/serialization/recovery_token_writer"
|
13
|
+
require_relative "delegated_account_recovery/serialization/recovery_token_reader"
|
14
|
+
require_relative "delegated_account_recovery/cryptors/default/default_encryptor"
|
15
|
+
require_relative "delegated_account_recovery/cryptors/default/encrypted_data"
|
16
|
+
require_relative "delegated_account_recovery/cryptors/default/encrypted_data_io"
|
17
|
+
|
18
|
+
module GitHub
|
19
|
+
module DelegatedAccountRecovery
|
20
|
+
# Represents a binary serialization error
|
21
|
+
class RecoveryTokenSerializationError < StandardError; end
|
22
|
+
|
23
|
+
# Represents invalid data within a valid token
|
24
|
+
# (e.g. wrong `version` number, invalid token `type`)
|
25
|
+
class TokenFormatError < StandardError; end
|
26
|
+
|
27
|
+
# Represents all crypto errors
|
28
|
+
# (e.g. invalid keys, invalid signature, decrypt failures)
|
29
|
+
class CryptoError < StandardError; end
|
30
|
+
|
31
|
+
# Represents providers supplying invalid configurations
|
32
|
+
# (e.g. non-https URLs, missing required fields, http errors)
|
33
|
+
class ProviderConfigError < StandardError; end
|
34
|
+
|
35
|
+
# Represents an invalid countersigned recovery token.
|
36
|
+
# (e.g. invalid signature, invalid nested token, unregistered provider, stale tokens)
|
37
|
+
class CountersignedTokenError < StandardError
|
38
|
+
attr_reader :key
|
39
|
+
def initialize(message, key)
|
40
|
+
super(message)
|
41
|
+
@key = key
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Represents an invalid recovery token.
|
46
|
+
# (e.g. invalid signature, unregistered provider, stale tokens)
|
47
|
+
class RecoveryTokenError < StandardError; end
|
48
|
+
|
49
|
+
# Represents a call to to `recovery_provider` or `account_provider` that
|
50
|
+
# has not been registered.
|
51
|
+
class UnknownProviderError < ArgumentError; end
|
52
|
+
|
53
|
+
include Constants
|
54
|
+
|
55
|
+
class << self
|
56
|
+
REQUIRED_CRYPTO_OPS = [:sign, :verify, :encrypt, :decrypt].freeze
|
57
|
+
# recovery provider data is only loaded (and cached) upon use.
|
58
|
+
attr_accessor :recovery_providers, :account_providers, :cache, :allow_unsafe_urls,
|
59
|
+
:privacy_policy, :icon_152px, :authority
|
60
|
+
|
61
|
+
# Find and load remote recovery provider configuration data.
|
62
|
+
#
|
63
|
+
# provider_origin: the origin that contains the config data in a well-known
|
64
|
+
# location.
|
65
|
+
def recovery_provider(provider_origin)
|
66
|
+
unless self.recovery_providers
|
67
|
+
raise "No recovery providers configured"
|
68
|
+
end
|
69
|
+
if self.recovery_providers.include?(provider_origin)
|
70
|
+
RecoveryProvider.new(provider_origin).load
|
71
|
+
else
|
72
|
+
raise UnknownProviderError, "Unknown recovery provider: #{provider_origin}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Permit an origin to act as a recovery provider.
|
77
|
+
#
|
78
|
+
# provider_origin: the origin to permit
|
79
|
+
def register_recovery_provider(provider_origin)
|
80
|
+
self.recovery_providers ||= []
|
81
|
+
self.recovery_providers << provider_origin
|
82
|
+
end
|
83
|
+
|
84
|
+
# Find and load remote account provider configuration data.
|
85
|
+
#
|
86
|
+
# provider_origin: the origin that contains the config data in a well-known
|
87
|
+
# location.
|
88
|
+
def account_provider(provider_origin)
|
89
|
+
unless self.account_providers
|
90
|
+
raise "No account providers configured"
|
91
|
+
end
|
92
|
+
if self.account_providers.include?(provider_origin)
|
93
|
+
AccountProvider.new(provider_origin).load
|
94
|
+
else
|
95
|
+
raise UnknownProviderError, "Unknown account provider: #{provider_origin}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Permit an origin to act as an account provider.
|
100
|
+
#
|
101
|
+
# account_origin: the origin to permit
|
102
|
+
def register_account_provider(account_origin)
|
103
|
+
self.account_providers ||= []
|
104
|
+
self.account_providers << account_origin
|
105
|
+
end
|
106
|
+
|
107
|
+
# Provide a reference to the account provider configuration for this web app
|
108
|
+
def this_account_provider
|
109
|
+
AccountProvider.this
|
110
|
+
end
|
111
|
+
|
112
|
+
# Provide a reference to the recovery provider configuration for this web app
|
113
|
+
def this_recovery_provider
|
114
|
+
RecoveryProvider.this
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns a hash of all configuration values, recovery and account provider.
|
118
|
+
def account_and_recovery_provider_config
|
119
|
+
provider_data = Darrrr.this_account_provider.try(:to_h) || {}
|
120
|
+
|
121
|
+
if Darrrr.this_recovery_provider
|
122
|
+
provider_data.merge!(recovery_provider_config) do |key, lhs, rhs|
|
123
|
+
unless lhs == rhs
|
124
|
+
raise ArgumentError, "inconsistent config value detected #{key}: #{lhs} != #{rhs}"
|
125
|
+
end
|
126
|
+
|
127
|
+
lhs
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
provider_data
|
132
|
+
end
|
133
|
+
|
134
|
+
# returns the account provider information in hash form
|
135
|
+
def account_provider_config
|
136
|
+
this_account_provider.to_h
|
137
|
+
end
|
138
|
+
|
139
|
+
# returns the account provider information in hash form
|
140
|
+
def recovery_provider_config
|
141
|
+
this_recovery_provider.to_h
|
142
|
+
end
|
143
|
+
|
144
|
+
def with_encryptor(encryptor)
|
145
|
+
raise ArgumentError, "A block must be supplied" unless block_given?
|
146
|
+
unless valid_encryptor?(encryptor)
|
147
|
+
raise ArgumentError, "custom encryption class must respond to all of #{REQUIRED_CRYPTO_OPS}"
|
148
|
+
end
|
149
|
+
|
150
|
+
Thread.current[:darrrr_encryptor] = encryptor
|
151
|
+
yield
|
152
|
+
ensure
|
153
|
+
Thread.current[:darrrr_encryptor] = nil
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns the crypto API to be used. A thread local instance overrides the
|
157
|
+
# globally configured value which overrides the default encryptor.
|
158
|
+
def encryptor
|
159
|
+
Thread.current[:darrrr_encryptor] || @encryptor || DefaultEncryptor
|
160
|
+
end
|
161
|
+
|
162
|
+
# Overrides the global `encryptor` API to use
|
163
|
+
#
|
164
|
+
# encryptor: a class/module that responds to all +REQUIRED_CRYPTO_OPS+.
|
165
|
+
def custom_encryptor=(encryptor)
|
166
|
+
if valid_encryptor?(encryptor)
|
167
|
+
@encryptor = encryptor
|
168
|
+
else
|
169
|
+
raise ArgumentError, "custom encryption class must respond to all of #{REQUIRED_CRYPTO_OPS}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
private def valid_encryptor?(encryptor)
|
174
|
+
REQUIRED_CRYPTO_OPS.all? {|m| encryptor.respond_to?(m)}
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|