keycloak_rack 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 +7 -0
- data/.github/workflows/main.yml +68 -0
- data/.gitignore +8 -0
- data/.rspec +2 -0
- data/.rubocop.yml +220 -0
- data/.ruby-version +1 -0
- data/.yardopts +7 -0
- data/Appraisals +16 -0
- data/CHANGELOG.md +10 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Gemfile +5 -0
- data/LICENSE +19 -0
- data/README.md +288 -0
- data/Rakefile +10 -0
- data/bin/appraisal +29 -0
- data/bin/console +6 -0
- data/bin/fix-appraisals +14 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/yard +29 -0
- data/bin/yardoc +29 -0
- data/bin/yri +29 -0
- data/gemfiles/rack_only.gemfile +5 -0
- data/gemfiles/rack_only.gemfile.lock +204 -0
- data/gemfiles/rails_6_0.gemfile +9 -0
- data/gemfiles/rails_6_0.gemfile.lock +323 -0
- data/gemfiles/rails_6_1.gemfile +9 -0
- data/gemfiles/rails_6_1.gemfile.lock +326 -0
- data/keycloak_rack.gemspec +56 -0
- data/lib/keycloak_rack.rb +59 -0
- data/lib/keycloak_rack/authenticate.rb +115 -0
- data/lib/keycloak_rack/authorize_realm.rb +53 -0
- data/lib/keycloak_rack/authorize_resource.rb +54 -0
- data/lib/keycloak_rack/config.rb +84 -0
- data/lib/keycloak_rack/container.rb +53 -0
- data/lib/keycloak_rack/decoded_token.rb +191 -0
- data/lib/keycloak_rack/flexible_struct.rb +20 -0
- data/lib/keycloak_rack/http_client.rb +86 -0
- data/lib/keycloak_rack/import.rb +9 -0
- data/lib/keycloak_rack/key_fetcher.rb +20 -0
- data/lib/keycloak_rack/key_resolver.rb +64 -0
- data/lib/keycloak_rack/middleware.rb +132 -0
- data/lib/keycloak_rack/railtie.rb +14 -0
- data/lib/keycloak_rack/read_token.rb +40 -0
- data/lib/keycloak_rack/resource_role_map.rb +8 -0
- data/lib/keycloak_rack/role_map.rb +15 -0
- data/lib/keycloak_rack/session.rb +44 -0
- data/lib/keycloak_rack/skip_authentication.rb +44 -0
- data/lib/keycloak_rack/types.rb +42 -0
- data/lib/keycloak_rack/version.rb +6 -0
- data/lib/keycloak_rack/with_config.rb +15 -0
- data/spec/dummy/.ruby-version +1 -0
- data/spec/dummy/README.md +24 -0
- data/spec/dummy/Rakefile +8 -0
- data/spec/dummy/app/controllers/application_controller.rb +22 -0
- data/spec/dummy/app/controllers/test_controller.rb +9 -0
- data/spec/dummy/config.ru +8 -0
- data/spec/dummy/config/application.rb +52 -0
- data/spec/dummy/config/boot.rb +3 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config/environments/development.rb +51 -0
- data/spec/dummy/config/environments/test.rb +51 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +9 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +10 -0
- data/spec/dummy/config/initializers/cors.rb +17 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +8 -0
- data/spec/dummy/config/initializers/inflections.rb +17 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +11 -0
- data/spec/dummy/config/keycloak.yml +12 -0
- data/spec/dummy/config/locales/en.yml +33 -0
- data/spec/dummy/config/routes.rb +5 -0
- data/spec/dummy/public/robots.txt +1 -0
- data/spec/dummy/tmp/development_secret.txt +1 -0
- data/spec/factories/decoded_token.rb +18 -0
- data/spec/factories/session.rb +21 -0
- data/spec/factories/token_payload.rb +40 -0
- data/spec/keycloak_rack/authorize_realm_spec.rb +15 -0
- data/spec/keycloak_rack/authorize_resource_spec.rb +19 -0
- data/spec/keycloak_rack/decoded_token_spec.rb +31 -0
- data/spec/keycloak_rack/key_resolver_spec.rb +95 -0
- data/spec/keycloak_rack/middleware_spec.rb +172 -0
- data/spec/keycloak_rack/rails_integration_spec.rb +43 -0
- data/spec/keycloak_rack/session_spec.rb +37 -0
- data/spec/keycloak_rack/skip_authentication_spec.rb +55 -0
- data/spec/spec_helper.rb +101 -0
- data/spec/support/contexts/mocked_keycloak.rb +63 -0
- data/spec/support/contexts/mocked_rack_application.rb +41 -0
- data/spec/support/test_key.pem +27 -0
- data/spec/support/token_helper.rb +76 -0
- metadata +616 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KeycloakRack
|
4
|
+
# @abstract
|
5
|
+
class FlexibleStruct < Dry::Struct
|
6
|
+
transform_keys(&:to_sym)
|
7
|
+
|
8
|
+
transform_types do |type|
|
9
|
+
# :nocov:
|
10
|
+
if type.default?
|
11
|
+
type.constructor do |value|
|
12
|
+
value.nil? ? Dry::Types::Undefined : value
|
13
|
+
end
|
14
|
+
else
|
15
|
+
type
|
16
|
+
end
|
17
|
+
# :nocov:
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KeycloakRack
|
4
|
+
# @note Adapted from monadic HTTP client in another project
|
5
|
+
# @api private
|
6
|
+
class HTTPClient
|
7
|
+
include Dry::Monads[:do, :result]
|
8
|
+
|
9
|
+
include Import[config: "keycloak-rack.config", server_url: "keycloak-rack.server_url", x509_store: "keycloak-rack.x509_store"]
|
10
|
+
|
11
|
+
# @param [String] realm_id
|
12
|
+
# @param [String] path
|
13
|
+
# @return [Dry::Monads::Success(Net::HTTPSuccess)] on a successful request
|
14
|
+
# @return [Dry::Monads::Failure(Symbol, String, Net::HTTPResponse)] on a failure
|
15
|
+
def get(realm_id, path)
|
16
|
+
uri = build_uri realm_id, path
|
17
|
+
|
18
|
+
request = Net::HTTP::Get.new(uri)
|
19
|
+
|
20
|
+
call request
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [String] realm_id
|
24
|
+
# @param [String] path
|
25
|
+
# @return [Dry::Monads::Success({ Symbol => Object })] on a successful request
|
26
|
+
# @return [Dry::Monads::Failure(:invalid_response, String, Net::HTTPResponse)] if the JSON fails to parse
|
27
|
+
# @return [Dry::Monads::Failure(Symbol, String, Net::HTTPResponse)] on a failure
|
28
|
+
def get_json(realm_id, path)
|
29
|
+
response = yield get realm_id, path
|
30
|
+
|
31
|
+
parse_json response
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [Net::HTTPRequest] request
|
35
|
+
# @return [Dry::Monads::Success(Net::HTTPSuccess)] on a successful request
|
36
|
+
# @return [Dry::Monads::Failure(Symbol, String, Net::HTTPResponse)] on a failure
|
37
|
+
def call(request)
|
38
|
+
# :nocov:
|
39
|
+
return Failure[:invalid_request, "Not a request: #{request.inspect}", nil] unless request.kind_of?(Net::HTTPRequest)
|
40
|
+
|
41
|
+
uri = request.uri
|
42
|
+
|
43
|
+
use_ssl = uri.scheme != "http"
|
44
|
+
# :nocov:
|
45
|
+
|
46
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: use_ssl, cert_store: x509_store) do |http|
|
47
|
+
response = http.request request
|
48
|
+
|
49
|
+
# :nocov:
|
50
|
+
case response
|
51
|
+
when Net::HTTPSuccess then Success response
|
52
|
+
when Net::HTTPBadRequest then Failure[:bad_request, "Bad Request", response]
|
53
|
+
when Net::HTTPUnauthorized then Failure[:unauthorized, "Unauthorized", response]
|
54
|
+
when Net::HTTPForbidden then Failure[:forbidden, "Forbidden", response]
|
55
|
+
when Net::HTTPNotFound then Failure[:not_found, "Not Found: #{uri}", response]
|
56
|
+
when Net::HTTPGatewayTimeout then Failure[:gateway_timeout, "Gateway Timeout", response]
|
57
|
+
when Net::HTTPClientError then Failure[:client_error, "Client Error: HTTP #{response.code}", response]
|
58
|
+
when Net::HTTPServerError then Failure[:server_error, "Server Error: HTTP #{response.code}", response]
|
59
|
+
else
|
60
|
+
Failure[:unknown_error, "Unknown Error", response]
|
61
|
+
end
|
62
|
+
# :nocov:
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# @param [String] realm_id
|
69
|
+
# @param [String] path
|
70
|
+
# @return [URI]
|
71
|
+
def build_uri(realm_id, path)
|
72
|
+
string_uri = File.join(server_url, "realms", realm_id, path)
|
73
|
+
|
74
|
+
URI(string_uri)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param [Net::HTTPResponse] response
|
78
|
+
# @return [Dry::Monads::Sucess({ Symbol => Object })] the deserialized JSON, should more or less always be a hash
|
79
|
+
# @return [Dry::Monads::Failure(:invalid_response, String, Net::HTTPResponse)] if the JSON fails to parse
|
80
|
+
def parse_json(response)
|
81
|
+
Success JSON.parse response.body, symbolize_names: true
|
82
|
+
rescue JSON::ParserError => e
|
83
|
+
Failure[:invalid_response, "Response was not valid JSON: #{e.message}", response]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KeycloakRack
|
4
|
+
# Fetches the public key for a keycloak installation.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class KeyFetcher
|
8
|
+
include Import[config: "keycloak-rack.config", http_client: "keycloak-rack.http_client"]
|
9
|
+
|
10
|
+
delegate :realm_id, to: :config
|
11
|
+
|
12
|
+
# @return [Dry::Monads::Success({ Symbol => Object })]
|
13
|
+
# @return [Dry::Monads::Failure(Symbol, String)]
|
14
|
+
def find_public_keys
|
15
|
+
http_client.get_json(realm_id, "protocol/openid-connect/certs").or do |(code, reason, response)|
|
16
|
+
Dry::Monads::Result::Failure[:invalid_public_keys, "Could not fetch public keys: #{reason.inspect}"]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KeycloakRack
|
4
|
+
# A caching resolver that wraps around {KeycloakRack::KeyFetcher} to cache its result
|
5
|
+
# for {KeycloakRack::Config#cache_ttl} seconds (default: 1.day)
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class KeyResolver
|
9
|
+
include Import[config: "keycloak-rack.config", fetcher: "keycloak-rack.key_fetcher"]
|
10
|
+
|
11
|
+
delegate :cache_ttl, to: :config
|
12
|
+
|
13
|
+
# @!attribute [r] cached_public_key_retrieved_at
|
14
|
+
# @return [ActiveSupport::TimeWithZone]
|
15
|
+
attr_reader :cached_public_key_retrieved_at
|
16
|
+
|
17
|
+
# @!attribute [r] cached_public_keys
|
18
|
+
# @return [Dry::Monads::Success({ Symbol => <{ Symbol => String }> })]
|
19
|
+
# @return [Dry::Monads::Failure]
|
20
|
+
attr_reader :cached_public_keys
|
21
|
+
|
22
|
+
def initialize(**)
|
23
|
+
super
|
24
|
+
|
25
|
+
@cached_public_keys = Dry::Monads.Failure("nothing fetched yet")
|
26
|
+
@cached_public_key_retrieved_at = 1.year.ago
|
27
|
+
end
|
28
|
+
|
29
|
+
# @see KeycloakRack::PublicKeyResolver#find_public_keys
|
30
|
+
# @return [Dry::Monads::Success({ Symbol => Object })]
|
31
|
+
# @return [Dry::Monads::Failure(Symbol, String)]
|
32
|
+
def find_public_keys
|
33
|
+
fetch! if should_refetch?
|
34
|
+
|
35
|
+
@cached_public_keys
|
36
|
+
end
|
37
|
+
|
38
|
+
def has_failed_fetch?
|
39
|
+
@cached_public_keys.failure?
|
40
|
+
end
|
41
|
+
|
42
|
+
def has_outdated_cache?
|
43
|
+
Time.current > @cached_public_key_expires_at
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [void]
|
47
|
+
def refresh!
|
48
|
+
fetch!
|
49
|
+
end
|
50
|
+
|
51
|
+
def should_refetch?
|
52
|
+
has_failed_fetch? || has_outdated_cache?
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# @return [void]
|
58
|
+
def fetch!
|
59
|
+
@cached_public_keys = fetcher.find_public_keys
|
60
|
+
@cached_public_key_retrieved_at = Time.current
|
61
|
+
@cached_public_key_expires_at = @cached_public_key_retrieved_at + cache_ttl.seconds
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KeycloakRack
|
4
|
+
# Rack middleware that calls {KeycloakRack::Authenticate} to process a keycloak token.
|
5
|
+
#
|
6
|
+
# Upon successful processing, it populates the following values into the rack environment
|
7
|
+
# for consumption later down the stack:
|
8
|
+
#
|
9
|
+
# - `keycloak:session`: An instance of {KeycloakRack::Session} that serves as the primary interface
|
10
|
+
# - `keycloak:authorize_realm`: An instance of {KeycloakRack::AuthorizeRealm} for authorizing realm-level roles
|
11
|
+
# - `keycloak:authorize_resource`: An instance of {KeycloakRack::AuthorizeResource} for authorizing resource-level roles
|
12
|
+
class Middleware
|
13
|
+
include Dry::Monads[:result]
|
14
|
+
|
15
|
+
include Import[authenticate: "keycloak-rack.authenticate", config: "keycloak-rack.config"]
|
16
|
+
|
17
|
+
# @param [#call] app the next component in the rack middleware stack
|
18
|
+
def initialize(app, **options)
|
19
|
+
super(**options)
|
20
|
+
|
21
|
+
@app = app
|
22
|
+
end
|
23
|
+
|
24
|
+
# Process the rack environment and inject the gem's interfaces into it.
|
25
|
+
#
|
26
|
+
# If the authentication is a monadic failure, and {KeycloakRack::Config#halt_on_auth_failure halt_on_auth_failure}
|
27
|
+
# is true, then it will short-circuit with {#authentication_failed}.
|
28
|
+
#
|
29
|
+
# @param [Hash] env the rack environment
|
30
|
+
# @return [Object]
|
31
|
+
def call(env)
|
32
|
+
result = authenticate.call(env)
|
33
|
+
|
34
|
+
return authentication_failed(env, result) if halt?(result)
|
35
|
+
|
36
|
+
session_opts = { skipped: false, auth_result: result }
|
37
|
+
|
38
|
+
case result
|
39
|
+
in Success[:authenticated, decoded_token]
|
40
|
+
session_opts[:token] = decoded_token
|
41
|
+
in Success[:skipped]
|
42
|
+
session_opts[:skipped] = true
|
43
|
+
else
|
44
|
+
# nothing to do
|
45
|
+
end
|
46
|
+
|
47
|
+
env["keycloak:session"] = session = KeycloakRack::Session.new(**session_opts)
|
48
|
+
env["keycloak:authorize_realm"] = session.authorize_realm
|
49
|
+
env["keycloak:authorize_resource"] = session.authorize_resource
|
50
|
+
|
51
|
+
@app.call(env)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
# Build the authentication failure when short-circuiting.
|
57
|
+
#
|
58
|
+
# @note See {#build_failure_headers} and {#build_failure_body} for opportunities
|
59
|
+
# to override.
|
60
|
+
# @param [Hash] env the rack environment
|
61
|
+
# @param [Dry::Monads::Result] monad
|
62
|
+
# @return [(Integer, { String => String }, <String>)] rack response
|
63
|
+
def authentication_failed(env, monad)
|
64
|
+
status = build_failure_status env, monad
|
65
|
+
|
66
|
+
headers = build_failure_headers env, monad
|
67
|
+
|
68
|
+
body = build_failure_body env, monad
|
69
|
+
|
70
|
+
# :nocov:
|
71
|
+
body = body.to_json unless body.kind_of?(String)
|
72
|
+
# :nocov:
|
73
|
+
|
74
|
+
[
|
75
|
+
status,
|
76
|
+
headers,
|
77
|
+
[ body ]
|
78
|
+
]
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_failure_status(env, monad)
|
82
|
+
case monad
|
83
|
+
in Failure[:no_token, _]
|
84
|
+
401
|
85
|
+
in Failure[:expired, String, String, Exception]
|
86
|
+
403
|
87
|
+
in Failure[Symbol, String, String, Exception]
|
88
|
+
400
|
89
|
+
else
|
90
|
+
500
|
91
|
+
# nothing to do
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @todo Make customizable
|
96
|
+
# @param [Hash] env the rack environment
|
97
|
+
# @param [Dry::Monads::Result] monad
|
98
|
+
# @return [{ String => String }]
|
99
|
+
def build_failure_headers(env, monad)
|
100
|
+
{
|
101
|
+
"Content-Type" => "application/json"
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
# @todo Make customizable
|
106
|
+
# @note Currently uses GraphQL error format.
|
107
|
+
# @param [Hash] env the rack environment
|
108
|
+
# @param [Dry::Monads::Result] monad
|
109
|
+
# @return [String, #to_json]
|
110
|
+
def build_failure_body(env, monad)
|
111
|
+
_reason, message, _token, _original_error = monad.failure
|
112
|
+
|
113
|
+
{
|
114
|
+
errors: [
|
115
|
+
{
|
116
|
+
message: message,
|
117
|
+
extensions: {
|
118
|
+
code: "UNAUTHENTICATED"
|
119
|
+
}
|
120
|
+
}
|
121
|
+
]
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
# @param [Dry::Monads::Result] result
|
126
|
+
def halt?(result)
|
127
|
+
return false unless result.failure?
|
128
|
+
|
129
|
+
config.halt_on_auth_failure?
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KeycloakRack
|
4
|
+
# Railtie that gets autoloaded when Rails is detected in the environment.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class Railtie < Rails::Railtie
|
8
|
+
railtie_name :keycloak_rack
|
9
|
+
|
10
|
+
initializer("keycloak_rack.insert_middleware") do |app|
|
11
|
+
app.config.middleware.use(KeycloakRack::Middleware)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KeycloakRack
|
4
|
+
# Read the bearer token from the `Authorization` token.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class ReadToken
|
8
|
+
include Dry::Monads[:result]
|
9
|
+
|
10
|
+
include Import[config: "keycloak-rack.config"]
|
11
|
+
|
12
|
+
# The pattern to match bearer tokens with.
|
13
|
+
BEARER_TOKEN = /\ABearer (?<token>.+)\z/i.freeze
|
14
|
+
|
15
|
+
# @param [Hash, #[]] env
|
16
|
+
# @return [Dry::Monads::Success(String)] when a token is found
|
17
|
+
# @return [Dry::Monads::Success(nil)] when a token is not found, but unauthenticated requests are allowed
|
18
|
+
# @return [Dry::Monads::Failure(:no_token, String)]
|
19
|
+
def call(env)
|
20
|
+
found_token = read_from env
|
21
|
+
|
22
|
+
return Success(found_token) if found_token.present?
|
23
|
+
|
24
|
+
return Success(nil) if config.allow_anonymous?
|
25
|
+
|
26
|
+
Failure[:no_token, "No JWT provided"]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# @param [Hash] env the rack environment
|
32
|
+
# @option env [String] "HTTP_AUTHORIZATION" the Authorization header
|
33
|
+
# @return [String, nil]
|
34
|
+
def read_from(env)
|
35
|
+
match = BEARER_TOKEN.match env["HTTP_AUTHORIZATION"]
|
36
|
+
|
37
|
+
match&.[](:token)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KeycloakRack
|
4
|
+
# PORO to interface with Keycloak roles.
|
5
|
+
class RoleMap < KeycloakRack::FlexibleStruct
|
6
|
+
# @!attribute [r] roles
|
7
|
+
# @return [<String>]
|
8
|
+
attribute :roles, Types::StringList
|
9
|
+
|
10
|
+
# @param [#to_s] name
|
11
|
+
def has_role?(name)
|
12
|
+
name.to_s.in? roles
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module KeycloakRack
|
4
|
+
# This serves as the primary interface for interacting with Rack and Rails applications,
|
5
|
+
# and an instance gets mounted into `keycloak:session` when the middleware processes.
|
6
|
+
class Session
|
7
|
+
extend Dry::Initializer
|
8
|
+
|
9
|
+
include Dry::Matcher.for(:authenticate!, with: Dry::Matcher::ResultMatcher)
|
10
|
+
|
11
|
+
option :auth_result, Types.Instance(Dry::Monads::Result)
|
12
|
+
option :skipped, Types::Bool, default: proc { false }
|
13
|
+
option :token, Types.Instance(KeycloakRack::DecodedToken).optional, optional: true
|
14
|
+
option :authorize_realm, Types.Interface(:call), default: proc { KeycloakRack::AuthorizeRealm.new self }
|
15
|
+
option :authorize_resource, Types.Interface(:call), default: proc { KeycloakRack::AuthorizeResource.new self }
|
16
|
+
|
17
|
+
delegate :has_realm_role?, :has_resource_role?, to: :token, allow_nil: true
|
18
|
+
|
19
|
+
alias skipped? skipped
|
20
|
+
|
21
|
+
# @return [Dry::Monads::Result]
|
22
|
+
def authenticate!
|
23
|
+
auth_result
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Dry::Monads::Result]
|
27
|
+
def authorize_realm!(*args)
|
28
|
+
authorize_realm.call(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Dry::Monads::Result]
|
32
|
+
def authorize_resource!(*args)
|
33
|
+
authorize_resource.call(*args)
|
34
|
+
end
|
35
|
+
|
36
|
+
def authenticated?
|
37
|
+
auth_result.success? && token.present?
|
38
|
+
end
|
39
|
+
|
40
|
+
def anonymous?
|
41
|
+
auth_result.success? && token.blank?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|