-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(authz-keycloak): dynamic scope and resource mapping. #3308
feat(authz-keycloak): dynamic scope and resource mapping. #3308
Conversation
…s even when not using TLS for the server itself.
…e-new access token.
@@ -371,16 +374,16 @@ http { | |||
{% end %} | |||
{% end %} {% -- if enable_ipv6 %} | |||
|
|||
{% if ssl.ssl_trusted_certificate ~= nil then %} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved this here since it was previously only used when APISIX itself was accepting HTTPS from the outside. But even if TLS is disabled, internally, plugins that send requests may still need to be able to use TLS and may have custom CA certs configured.
@@ -42,7 +43,6 @@ local schema = { | |||
enum = {"urn:ietf:params:oauth:grant-type:uma-ticket"}, | |||
minLength = 1, maxLength = 100 | |||
}, | |||
audience = {type = "string", minLength = 1, maxLength = 100}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now called client_id
which is more consistent with the opendid-connect
plugin as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can keep the old field and add a deprecated check?
Lines 463 to 470 in bbbdf58
if upstream.value.enable_websocket then | |
core.log.warn("DEPRECATE: enable websocket in upstream will be removed soon. ", | |
"Please enable it in route/service level.") | |
enable_websocket = true | |
end | |
if upstream.value.pass_host then | |
api_ctx.pass_host = upstream.value.pass_host |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@spacewander Possible, it's just a name. So we keep client_id
as well and prefer it, if given?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Maybe we can add a description on them:
Line 75 in bbbdf58
description = "client IP", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, will change accordingly. Will also add/fix more test cases soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, done.
| policy_enforcement_mode | string | optional | "ENFORCING" | ["ENFORCING", "PERMISSIVE"] | | | ||
| permissions | array[string] | optional | | | Static permission to request, an array of strings each representing a resources and optionally one or more scopes the client is seeking access. | | ||
| lazy_load_paths | boolean | optional | false | | Dynamically resolve the request URI to resource(s) using the resource registration endpoint instead of using the static permission. | | ||
| http_method_as_scope | boolean | optional | false | | Map HTTP request type to scope of same name and add to all permissions requested. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing doc for cache_ttl_seconds
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. Will add.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
apisix/plugins/authz-keycloak.lua
Outdated
@@ -171,13 +222,13 @@ local function authz_keycloak_discover(url, ssl_verify, keepalive, timeout, | |||
keepalive = (keepalive ~= "no") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems a bug in the previous code: the keepalive
and ssl_verify
are boolean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, was copied over from openidc
module. Fixed it and consolidated the HTTP client setup -- which is done in various places -- to apply options like timeout
, keepalive
, ssl_verify
and others consistently.
doc/plugins/authz-keycloak.md
Outdated
| token_endpoint | string | optional | | https://host.domain/auth/realms/foo/protocol/openid-connect/token | A OAuth2-compliant Token Endpoint that supports the `urn:ietf:params:oauth:grant-type:uma-ticket` grant type. Overrides value from discovery, if given. | | ||
| resource_registration_endpoint | string | optional | | https://host.domain/auth/realms/foo/authz/protection/resource_set | A Keycloak Protection API-compliant resource registration endpoint. Overrides value from discovery, if given. | | ||
| grant_type | string | optional | "urn:ietf:params:oauth:grant-type:uma-ticket" | ["urn:ietf:params:oauth:grant-type:uma-ticket"] | | | ||
| client_id | string | required | | | The client identifier of the resource server to which the client is seeking access. <br>This parameter is mandatory when parameter permission is defined. | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This parameter is mandatory when parameter permission is defined.
Look like it should be when parameter lazy_load_paths is defined
? Should require it in the schema and add a test for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so I'm now again requiring one of client_id
or audience
, the latter for better backwards compatibility. Rationale: The plugin always uses the token endpoint to get a decision from Keycloak on the requested permissions. As per https://www.keycloak.org/docs/latest/authorization_services/#_service_obtaining_permissions the audience
parameter (what is either in client_id
or audience
for the plugin) is required when the permission
parameter is set (which the plugin always does).
If lazy_load_paths
is true
, the plugin also needs to obtain an access token for itself from Keycloak. To that end, it most likely also needs client_secret
to be set, but that depends on how Keycloak is configured. Hence, this is still optional.
Also added test cases to check if one of client_id
or audience
is correctly required and schema verification fails if both are absent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see.
end | ||
|
||
local session = authz_keycloak_cache_get("access_tokens", token_endpoint .. ":" | ||
.. client_id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The client_id
could be nil if you don't force when lazy_load_paths is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above, we're now requiring client_id
(or audience
).
@@ -137,7 +224,7 @@ done | |||
"authz-keycloak": { | |||
"token_endpoint": "http://127.0.0.1:8090/auth/realms/University/protocol/openid-connect/token", | |||
"permissions": ["course_resource#view"], | |||
"audience": "course_management", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can leave audience
unchanged in one or two cases so that we know the old configuration is still compatible?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I can add some cases that use it.
BTW, you can split the test into two files, like the https://github.com/apache/apisix/blob/master/t/plugin/limit-conn2.t. Since it is too big now. |
done |
@spacewander I think I've addressed all of the feedback so far. Thanks for reviewing. Let me know any further issues. |
-- Return access_token expires_in value (in seconds). | ||
local function authz_keycloak_access_token_expires_in(conf, expires_in) | ||
return (expires_in or conf.access_token_expires_in or 300) | ||
- 1 - (conf.access_token_expires_leeway or 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will you add this configurations in this PR, or in the future one?
-- Return refresh_token expires_in value (in seconds). | ||
local function authz_keycloak_refresh_token_expires_in(conf, expires_in) | ||
return (expires_in or conf.refresh_token_expires_in or 3600) | ||
- 1 - (conf.refresh_token_expires_leeway or 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can merge this PR first and solve the remain in the next one.
@spacewander Thanks, I will surely continue the work on this plugin and I can add the config options in a future PR. |
I have been busy recently, I will review this PR later. Welcome everyone to review. many thx |
What this PR does / why we need it:
Add code to
authz-keycloak
plugin to optionally resolve requests URI to resource(s) and map HTTP method to scope; fixes #3274.Pre-submission checklist:
Open this PR to get some feedback on the changes. Still need to add tests to cover the new functionality. May still need to polish the JSON schema slightly as well. However, already tested changes in a real deployment to confirm this is actually working.
Also, strictly speaking, this plugin is not backwards compatible. E.g. we now require static permissions to be set when not using the new dynamic resource resolution. We also require
client_id
to be set because it will be used in any sensible configuration scenario. This mostly affects corner cases, so I don't think it represents a real problem.In any case, will likely polish everything a bit more, but would appreciate any feedback already.
@sshniro You should definitely have a look.