app_identity 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 30f775dedcf26d6e5951cc7af9440ee560a76529946610300284bdc727d17972
4
+ data.tar.gz: 1017621f7d5474e23e8823a3d568b3dca11baef8e42a8f43441512205ab54620
5
+ SHA512:
6
+ metadata.gz: 5f2a942c09c6aa58a25c97552659d7848c2eea717bc94a752b28812ca951234931ad4e233abb7e765b5ce7b5b9574fe050e314ed69a0083fd18cdbe707f9dc9d
7
+ data.tar.gz: 7b38ec69bad5c20c2360495267944c9172f61475df4cfb8413c241b9c4a821f09ecf1a33a09b6f2a49156ee28a5882af83b630b20e055693e70d5084d277f311
data/.rdoc_options ADDED
@@ -0,0 +1,28 @@
1
+ --- !ruby/object:RDoc::Options
2
+ encoding: UTF-8
3
+ static_path: []
4
+ rdoc_include:
5
+ - "."
6
+ charset: UTF-8
7
+ exclude:
8
+ - Manifest.txt\z
9
+ - ~\z
10
+ - \.orig\z
11
+ - \.rej\z
12
+ - \.bak\z
13
+ - \.gemspec\z
14
+ hyperlink_all: false
15
+ line_numbers: false
16
+ locale:
17
+ locale_dir: locale
18
+ locale_name:
19
+ main_page:
20
+ markup: markdown
21
+ output_decoration: true
22
+ page_dir:
23
+ show_hash: false
24
+ tab_width: 8
25
+ template_stylesheets: []
26
+ title:
27
+ visibility: :protected
28
+ webcvs:
data/Changelog.md ADDED
@@ -0,0 +1,5 @@
1
+ # App Identity for Ruby Changelog
2
+
3
+ ## 1.0.0 / 2022-09-07
4
+
5
+ - Initial release.
data/Contributing.md ADDED
@@ -0,0 +1,70 @@
1
+ # Contributing
2
+
3
+ We value contributions to AppIdentity for Ruby—bug reports, discussions, feature
4
+ requests, and code contributions. New features should be proposed and
5
+ [discussed][] prior to implementation, and release of any new feature may be
6
+ delayed until implemented in the three reference implementations.
7
+
8
+ Before contributing patches, please read the [Licence.md](licence.md).
9
+
10
+ App Identity is governed under the Kinetic Commerce Open Source [Code of
11
+ Conduct][].
12
+
13
+ ## Code Guidelines
14
+
15
+ Our usual code contribution guidelines apply:
16
+
17
+ - Code changes _will not_ be accepted without tests. The test suite is written
18
+ with [minitest][].
19
+ - Match our coding style. We use [standard Ruby][] to assist with this.
20
+ - Use a thoughtfully-named topic branch that contains your change. Rebase your
21
+ commits into logical chunks as necessary.
22
+ - Use [quality commit messages][].
23
+ - Certain things must not be changed except as part of the release process. These
24
+ are:
25
+ - the version number in `lib/app_identity.rb`
26
+ - `Gemfile` (this is a stub file)
27
+ - `app_identity.gemspec` (this is a generated file)
28
+ - Submit a pull request with your changes.
29
+ - New or changed behaviours require new or updated documentation.
30
+ - New dependencies are discouraged.
31
+
32
+ There are code quality checks performed in GitHub Actions that must pass for any
33
+ pull request to be accepted.
34
+
35
+ ## Test Dependencies
36
+
37
+ `app_identity` uses Ryan Davis’s [Hoe][] to manage the release process, and it
38
+ adds a number of useful rake tasks.
39
+
40
+ ```console
41
+ $ rake
42
+ Run options: --seed 45847
43
+
44
+ # Running:
45
+
46
+ ..............................
47
+
48
+ Finished in 0.005884s, 5098.5724 runs/s, 10197.1448 assertions/s.
49
+
50
+ 30 runs, 60 assertions, 0 failures, 0 errors, 0 skips
51
+ rm -rf doc
52
+ rm -r pkg
53
+ ```
54
+
55
+ We have provided the simplest possible Gemfile pointing to the (generated)
56
+ `app_identity.gemspec` file. This will permit you to use `bundle install` to get
57
+ the development dependencies.
58
+
59
+ ```console
60
+ $ bundle install
61
+
62
+ Bundle complete!
63
+ ```
64
+
65
+ [code of conduct]: https://github.com/KineticCafe/code-of-conduct
66
+ [discussed]: https://github.com/KineticCafe/app_identity/discussions
67
+ [hoe]: https://github.com/seattlerb/hoe
68
+ [minitest]: https://github.com/seattlerb/minitest
69
+ [quality commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
70
+ [standard ruby]: https://github.com/testdouble/standard
data/Licence.md ADDED
@@ -0,0 +1,25 @@
1
+ # Licence
2
+
3
+ App Identity for Ruby is copyright © 2022 Kinetic Commerce and contributors
4
+ and is licensed under [Apache Licence, version 2.0][apache-licence-20].
5
+
6
+ ## Developer Certificate of Origin
7
+
8
+ All contributors **must** certify they are able and willing to provide their
9
+ contributions under the terms of this project's licenses with the certification
10
+ of the [Developer Certificate of Origin (Version 1.1)][dco].
11
+
12
+ Such certification is provided by ensuring that the following line must be
13
+ included as the last line of a commit message for every commit contributed:
14
+
15
+ Signed-off-by: FirstName LastName <email@example.org>
16
+
17
+ The `Signed-off-by` line can be automatically added by git with the `-s` or
18
+ `--signoff` option on `git commit`:
19
+
20
+ ```sh
21
+ git commit --signoff
22
+ ```
23
+
24
+ [apache-licence-20]: licences/APAHCE-2.0.txt
25
+ [dco]: licenses/dco.txt
data/Manifest.txt ADDED
@@ -0,0 +1,30 @@
1
+ .rdoc_options
2
+ Changelog.md
3
+ Contributing.md
4
+ Licence.md
5
+ Manifest.txt
6
+ README.md
7
+ Rakefile
8
+ bin/app-identity-suite-ruby
9
+ lib/app_identity.rb
10
+ lib/app_identity/app.rb
11
+ lib/app_identity/error.rb
12
+ lib/app_identity/faraday_middleware.rb
13
+ lib/app_identity/internal.rb
14
+ lib/app_identity/rack_middleware.rb
15
+ lib/app_identity/validation.rb
16
+ lib/app_identity/versions.rb
17
+ licences/APACHE-2.0.txt
18
+ licences/DCO.txt
19
+ spec.md
20
+ support/app_identity/suite.rb
21
+ support/app_identity/suite/generator.rb
22
+ support/app_identity/suite/optional.json
23
+ support/app_identity/suite/program.rb
24
+ support/app_identity/suite/required.json
25
+ support/app_identity/suite/runner.rb
26
+ support/app_identity/support.rb
27
+ test/minitest_helper.rb
28
+ test/test_app_identity.rb
29
+ test/test_app_identity_app.rb
30
+ test/test_app_identity_rack_middleware.rb
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # AppIdentity for Ruby
2
+
3
+ - code :: https://github.com/KineticCafe/app-identity/tree/main/ruby/
4
+ - issues :: https://github.com/KineticCafe/app-identity/issues
5
+
6
+ ## Description
7
+
8
+ AppIdentity is a Ruby implementation of the Kinetic Commerce application
9
+ identity proof algorithm as described in its [spec][].
10
+
11
+ ## Synopsis
12
+
13
+ ```ruby
14
+ app = AppIdentity::App.new(id: id, secret: secret, version: 2)
15
+ proof = AppIdentity.generate_proof!(app)
16
+ AppIdentity.verify_proof!(proof, app)
17
+ ```
18
+
19
+ In a Rails application, proof verification would use
20
+ `AppIdentity::RackMiddleware.`
21
+
22
+ ```ruby
23
+ require 'app_identity/rack_middleware'
24
+
25
+ config.middleware.use AppIdentity::RackMiddleware,
26
+ header: "app-identity-proof",
27
+ finder: ->(proof) { IdentityApplication.find(proof[:id]) }
28
+ ```
29
+
30
+ There is a Faraday Middleware for providing proof generation for clients.
31
+
32
+ ```ruby
33
+ Faraday.new(url: url) do |conn|
34
+ conn.request :app_identity,dentity_app: app,
35
+ header: 'app-proof-identity'
36
+ end
37
+ ```
38
+
39
+ ## Installation
40
+
41
+ Add `app_identity` to your Gemfile:
42
+
43
+ ```ruby
44
+ gem 'app_identity', '~> 1.0'
45
+ ```
46
+
47
+ ## Semantic Versioning
48
+
49
+ `AppIdentity` uses a [Semantic Versioning][] scheme with one significant change:
50
+
51
+ - When PATCH is zero (`0`), it will be omitted from version references.
52
+
53
+ Additionally, the major version will generally be reserved for specification
54
+ revisions.
55
+
56
+ ## Contributing
57
+
58
+ AppIdentity for Ruby [welcomes contributions](Contributing.md). This project,
59
+ all Kinetic Commerce [open source projects][], is under the Kinetic Commerce
60
+ Open Source [Code of Conduct][kccoc].
61
+
62
+ AppIdentity for Ruby is licensed under the Apache Licence, version 2.0 and
63
+ requires certification via a Developer Certificate of Origin. See
64
+ [Licence.md](Licence.md) for more details.
65
+
66
+ [contributing]: Contributing.md
67
+ [kccoc]: https://github.com/KineticCafe/code-of-conduct
68
+ [open source projects]: https://github.com/KineticCafe
69
+ [semantic versioning]: http://semver.org/
70
+ [spec]: https://github.com/KineticCafe/app-identity/blob/main/spec/README.md
data/Rakefile ADDED
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems"
4
+ require "hoe"
5
+ require "rake/clean"
6
+
7
+ Hoe.plugin :doofus
8
+ Hoe.plugin :gemspec2
9
+ Hoe.plugin :git2
10
+ Hoe.plugin :minitest
11
+
12
+ Hoe.spec "app_identity" do
13
+ developer("Austin Ziegler", "aziegler@kineticcommerce.com")
14
+ developer("Kinetic Commerce", "dev@kineticcommerce.com")
15
+
16
+ self.history_file = "Changelog.md"
17
+ self.readme_file = "README.md"
18
+
19
+ license "MIT"
20
+
21
+ spec_extras[:metadata] = ->(val) { val["rubygems_mfa_required"] = "true" }
22
+
23
+ extra_deps << ["optimist", "~> 3.0"]
24
+ extra_dev_deps << ["hoe-doofus", "~> 1.0"]
25
+ extra_dev_deps << ["hoe-gemspec2", "~> 1.1"]
26
+ extra_dev_deps << ["hoe-git2", "~> 1.7"]
27
+ extra_dev_deps << ["minitest", "~> 5.4"]
28
+ extra_dev_deps << ["minitest-autotest", "~> 1.0"]
29
+ extra_dev_deps << ["minitest-bisect", "~> 1.2"]
30
+ extra_dev_deps << ["minitest-focus", "~> 1.1"]
31
+ extra_dev_deps << ["minitest-pretty_diff", "~> 0.1"]
32
+ extra_dev_deps << ["rack-test", "~> 0.6"]
33
+ extra_dev_deps << ["rake", ">= 10.0", "< 14.0"]
34
+ extra_dev_deps << ["rdoc", "~> 6.4"]
35
+ extra_dev_deps << ["simplecov", "~> 0.7"]
36
+ extra_dev_deps << ["standard", "~> 1.0"]
37
+ end
38
+
39
+ namespace :console do
40
+ task :pry do
41
+ exec "pry -Ilib -rapp_identity"
42
+ end
43
+
44
+ task :irb do
45
+ exec "irb -Ilib -rapp_identity"
46
+ end
47
+ end
48
+
49
+ desc "Open a console with AppIdentity loaded"
50
+ task console: "console:irb"
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ root = File.expand_path("../../", __FILE__)
4
+
5
+ if File.exist?(File.join(root, "app_identity.gemspec"))
6
+ $LOAD_PATH.unshift(File.join(root, "lib"), File.join(root, "support"))
7
+ end
8
+
9
+ require "optimist"
10
+ require "app_identity/suite"
11
+
12
+ exit 1 unless AppIdentity::Suite::Program.run(name: File.basename(__FILE__))
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ostruct"
4
+
5
+ require_relative "error"
6
+ require_relative "validation"
7
+
8
+ # The class used by the App Identity proof generation and verification
9
+ # algorithms. This will typically be constructed from another object, structure,
10
+ # or hash, such as from a static configuration file or a database record.
11
+ #
12
+ # AppIdentity::App objects are created frozen, and certain operations may
13
+ # provide a modified duplicate.
14
+ class AppIdentity::App
15
+ def self.new(input) # :nodoc:
16
+ if input.is_a?(AppIdentity::App) && !input.verified
17
+ input
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ include AppIdentity::Validation
24
+
25
+ # The AppIdentity App unique identifier. Validation of the `id` value will
26
+ # convert non-string IDs using #to_s.
27
+ #
28
+ # If using integer IDs, it is recommended that the `id` value be provided as
29
+ # some form of extended string value, such as that provided by Rails [global
30
+ # ID](https://github.com/rails/globalid). Such representations are _also_
31
+ # recommended if the ID is a compound value.
32
+ #
33
+ # `id` values _must not_ contain a colon (`:`) character.
34
+ attr_reader :id
35
+
36
+ # The App Identity app secret value. This value is used _as provided_ with no
37
+ # encoding or decoding. As this is a sensitive value, it may be provided
38
+ # as a 0-arity closure proc.
39
+ #
40
+ # For security purposes, this is always stored as a 0-arity closure proc.
41
+ attr_reader :secret
42
+
43
+ # The positive integer version of the AppIdentity algorithm to use. Will be
44
+ # validated to be a supported version for app creation, and not an explicitly
45
+ # disallowed version during proof validation.
46
+ #
47
+ # A string `version` must convert cleanly to an integer value, meaning that
48
+ # `"3.5"` is not a valid value.
49
+ #
50
+ # AppIdentity algorithm versions are strictly upgradeable. That is, a version
51
+ # 1 app can verify version 1, 2, 3, or 4 proofs. However, a version 2 app will
52
+ # _never_ validate a version 1 proof.
53
+ #
54
+ # <table>
55
+ # <thead>
56
+ # <tr>
57
+ # <th rowspan=2>Version</th>
58
+ # <th rowspan=2>Nonce</th>
59
+ # <th rowspan=2>Digest Algorithm</th>
60
+ # <th colspan=4>Can Verify</th>
61
+ # </tr>
62
+ # <tr><th>1</th><th>2</th><th>3</th><th>4</th></tr>
63
+ # </thead>
64
+ # <tbody>
65
+ # <tr><th>1</th><td>random</td><td>SHA 256</td><td>✅</td><td>✅</td><td>✅</td><td>✅</td></tr>
66
+ # <tr><th>2</th><td>timestamp ± fuzz</td><td>SHA 256</td><td>⛔️</td><td>✅</td><td>✅</td><td>✅</td></tr>
67
+ # <tr><th>3</th><td>timestamp ± fuzz</td><td>SHA 384</td><td>⛔️</td><td>⛔️</td><td>✅</td><td>✅</td></tr>
68
+ # <tr><th>4</th><td>timestamp ± fuzz</td><td>SHA 512</td><td>⛔️</td><td>⛔️</td><td>⛔️</td><td>✅</td></tr>
69
+ # </tbody>
70
+ # </table>
71
+ attr_reader :version
72
+
73
+ # An optional configuration value for validation of an App Identity proof.
74
+ #
75
+ # If not provided, the default value when required is `{fuzz: 600}`,
76
+ # specifying that the timestamp may not differ from the current time by more
77
+ # than ±600 seconds (±10 minutes). Depending on the nature of the app being
78
+ # verified and the expected network conditions, a shorter time period than 600
79
+ # seconds is recommended.
80
+ #
81
+ # The App Identity version 1 algorithm does not use `config`.
82
+ attr_reader :config
83
+
84
+ # The original object used to construct this App Identity object.
85
+ attr_reader :source
86
+
87
+ # Whether this app was used in the successful verification of a proof.
88
+ attr_reader :verified
89
+
90
+ # Constructs an AppIdentity::App from a provided object or zero-arity
91
+ # callable that returns an initialization object. These values should be
92
+ # treated as immutable objects.
93
+ #
94
+ # The object must respond to `#id`, `#secret`, `#version`, and `#config` or
95
+ # have indexable keys (via `#[]`) of `id`, `secret`, `version`, and `config`
96
+ # as either Symbol or String values. That is, the `id` should be retrievable
97
+ # in one of the following ways:
98
+ #
99
+ # ```ruby
100
+ # input.id
101
+ # input[:id]
102
+ # input["id"]
103
+ # ```
104
+ #
105
+ # If the input parameter is a callable, it will be called with no
106
+ # parameters to produce an input object.
107
+ #
108
+ # The AppIdentity::App is frozen on creation.
109
+ #
110
+ # ```ruby
111
+ # AppIdentity::App.new({id: 1, secret: "secret", version: 1})
112
+ #
113
+ # AppIdentity::App.new(->() { {id: 1, secret: "secret", version: 1} })
114
+ # ```
115
+ #
116
+ # If the provided `input` is already an App and is not #verified, the existing
117
+ # app will be returned instead of creating a new application.
118
+ def initialize(input)
119
+ input = input.call if input.respond_to?(:call)
120
+
121
+ @id = get(input, :id)
122
+ @secret = fwrap(get(input, :secret).dup)
123
+ @version = get(input, :version)
124
+ @config = get(input, :config)
125
+ @source = input
126
+ @verified = false
127
+
128
+ validate!
129
+ freeze
130
+ end
131
+
132
+ # If the current App is not `verified`, then return a copy of the current App
133
+ # with the verified flag set to `true`.
134
+ def verify
135
+ verified ? self : dup.tap { |v| v.instance_variable_set(:@verified, true) }.freeze
136
+ end
137
+
138
+ # If the current App is `verified`, then return a copy of the current App
139
+ # with the verified flag set to `false`.
140
+ def unverify
141
+ verified ? dup.tap { |v| v.instance_variable_set(:@verified, false) }.freeze : self
142
+ end
143
+
144
+ # Generate a nonce for this application. Optionally provide a version number
145
+ # override to generate a compatible (upgraded) nonce version.
146
+ def generate_nonce(version = nil)
147
+ version ||= self.version
148
+
149
+ unless self.version <= version
150
+ raise "app version #{self.version} is not compatible with requested version #{version}"
151
+ end
152
+
153
+ AppIdentity::Versions[version].generate_nonce
154
+ end
155
+
156
+ def to_h # :nodoc:
157
+ {config: config, id: id, secret: secret.call, version: version}
158
+ end
159
+
160
+ alias_method :as_json, :to_h
161
+
162
+ def to_s # :nodoc:
163
+ inspect
164
+ end
165
+
166
+ def to_json(*args, **kwargs) # :nodoc:
167
+ as_json.to_json(*args, **kwargs)
168
+ end
169
+
170
+ def hash # :nodoc:
171
+ [AppIdentityApp::App, id, version, config, secret]
172
+ end
173
+
174
+ def inspect # :nodoc:
175
+ "#<#{self.class} id: #{id} version: #{version} config: #{config} verified: #{verified}>"
176
+ end
177
+
178
+ def ==(other) # :nodoc:
179
+ other.is_a?(self.class) &&
180
+ id == other.id &&
181
+ version == other.version &&
182
+ config == other.config &&
183
+ verified == other.verified &&
184
+ secret.call == other.secret.call
185
+ end
186
+
187
+ private
188
+
189
+ def get(input, key)
190
+ case input
191
+ when Hash, Struct, OpenStruct
192
+ input[key] || input[key.to_s]
193
+ else
194
+ if input.respond_to?(key)
195
+ input.__send__(key)
196
+ elsif input.respond_to?(:[]) && !input.is_a?(Array)
197
+ input[key] || input[key.to_s]
198
+ else
199
+ raise AppIdentity::Error, "app cannot be created from input (missing value #{key.inspect})"
200
+ end
201
+ end
202
+ end
203
+
204
+ def fwrap(value)
205
+ value.respond_to?(:call) ? value : -> { value }
206
+ end
207
+
208
+ def validate!
209
+ @id = validate_id(@id)
210
+ @secret = fwrap(validate_secret(@secret))
211
+ @version = validate_version(@version)
212
+ @config = validate_config(@config)
213
+ end
214
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # An exception that will be raised by AppIdentity.generate_proof!,
4
+ # AppIdentity.parse_proof!, or AppIdentity.verify_proof! when those functions
5
+ # fail. The error message reported here is *always* generic.
6
+ #
7
+ # This can also be raised by AppIdentity::RackMiddleware on configuration
8
+ # failure.
9
+ class AppIdentity::Error < ::StandardError
10
+ attr_reader :message # :nodoc:
11
+
12
+ def initialize(type) # :nodoc:
13
+ @message = resolve(type)
14
+ end
15
+
16
+ private
17
+
18
+ def resolve(type)
19
+ case type
20
+ when String
21
+ type
22
+ when :verify_proof
23
+ "Error verifying proof"
24
+ when :generate_proof
25
+ "Error generating proof"
26
+ when :parse_proof
27
+ "Error parsing proof"
28
+ when :disallowed_configuration_error
29
+ "error in disallowed version configuration"
30
+ when :plug_missing_apps_or_finder
31
+ "AppIdentity::RackMiddleware configuration error: one of `apps` or `finder` options is required"
32
+ when :plug_headers_required
33
+ "AppIdentity::RackMiddleware configuration error: `headers` option is required"
34
+ when :plug_header_invalid
35
+ "AppIdentity::RackMiddleware configuration error: `headers` value is invalid"
36
+ when :plug_on_failure_invalid
37
+ "AppIdentity::RackMiddleware configuration error: `on_failure` value is invalid"
38
+ when :plug_disallowed_invalid
39
+ "AppIdentity::RackMiddleware configuration error: `disallowed` value is invalid"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(Faraday::Middleware)
4
+ require "app_identity"
5
+
6
+ # A Faraday middleware that generates an app identity proof header for
7
+ # a request.
8
+ #
9
+ # The `options` provided has the following parameters:
10
+ #
11
+ # - `app`: (required) An AppIdentity::App or object that can be coerced into
12
+ # an AppIdentity::app with AppIdentity#new.
13
+ #
14
+ # - `disallowed`: A list of algorithm versions that are not allowed when
15
+ # processing received identity proofs. See AppIdentity::Versions.allowed?.
16
+ #
17
+ # - `header`: (required) The header to use for sending the app identity proof.
18
+ #
19
+ # - `on_failure`: (optional) The action to take when an app identity proof
20
+ # cannot be generated for any reason. May be one of the following values:
21
+ #
22
+ # - `:fail`: Throws an exception. This is the default if `on_failure` is
23
+ # not specified.
24
+ #
25
+ # - `:pass`: Sets the header to the empty value returned. The request will
26
+ # probably fail on the receiving server side.
27
+ #
28
+ # - `:skip`: Does not add the header, as if the request were not made
29
+ # using an application.
30
+ #
31
+ # `on_failure` may also be provided a callable object that expects three
32
+ # parameters:
33
+ #
34
+ # - `env`: The Faraday middleware `env` value;
35
+ # - `app`: The identity app value provided to the middleware; and
36
+ # - `header`: The header name provided to the middleware.
37
+ #
38
+ # The callable may return either the `env`, `:fail`, `:skip`, or `:pass`. Any
39
+ # other value will be treated as `:fail`.
40
+ class AppIdentity::FaradayMiddleware < Faraday::Middleware
41
+ def initialize(app, options = {}) # :nodoc:
42
+ super(app)
43
+
44
+ @identity_app = AppIdentity::App.new(options.fetch(:app))
45
+ @header = options.fetch(:header).downcase
46
+ @on_failure = options.fetch(:on_failure, :fail)
47
+ @disallowed = options.fetch(:disallowed, nil)
48
+ end
49
+
50
+ def call(env) # :nodoc:
51
+ proof = AppIdentity.generate_proof(@identity_app, disallowed: @disallowed)
52
+
53
+ if proof.nil?
54
+ handle_failure(@on_failure)
55
+ else
56
+ env[:request_headers][@header] = proof
57
+ end
58
+
59
+ @app.call(env)
60
+ end
61
+
62
+ private
63
+
64
+ def handle_failure(on_failure)
65
+ case on_failure
66
+ when :skip
67
+ nil
68
+ when :pass
69
+ env[:request_headers][@header] = ""
70
+ when :fail
71
+ raise AppIdentity::Error, "unable to generate proof for app #{@identity_app.id}"
72
+ else
73
+ if on_failure.respond_to?(:call)
74
+ result = on_failure.call(env, @identity_app, @header)
75
+
76
+ case result
77
+ when :skip, :pass, :fail
78
+ return handle_failure(result)
79
+ else
80
+ if result.eql?(env)
81
+ return
82
+ else
83
+ return handle_failure(:fail)
84
+ end
85
+ end
86
+ end
87
+
88
+ handle_failure(:fail)
89
+ end
90
+ end
91
+ end
92
+
93
+ Faraday::Request.register_middleware app_identity: -> { AppIdentity::FaradayMiddleware }
94
+ end