app_identity 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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