Merge pull request #363 from pedroigor/KEYCLOAK-4903
[KEYCLOAK-4903] - Pushed Claims and improvements to PEP
This commit is contained in:
commit
cd944ade4e
5 changed files with 193 additions and 54 deletions
|
@ -104,10 +104,10 @@ include::topics/enforcer-overview.adoc[leveloffset=+1]
|
||||||
|
|
||||||
include::topics/enforcer-keycloak-enforcement-filter.adoc[leveloffset=+2]
|
include::topics/enforcer-keycloak-enforcement-filter.adoc[leveloffset=+2]
|
||||||
|
|
||||||
include::topics/enforcer-keycloak-enforcement-bearer.adoc[leveloffset=+3]
|
include::topics/enforcer-claim-information-point.adoc[leveloffset=+2]
|
||||||
|
|
||||||
include::topics/enforcer-authorization-context.adoc[leveloffset=+3]
|
include::topics/enforcer-authorization-context.adoc[leveloffset=+2]
|
||||||
|
|
||||||
include::topics/enforcer-js-adapter.adoc[leveloffset=+3]
|
include::topics/enforcer-js-adapter.adoc[leveloffset=+2]
|
||||||
|
|
||||||
include::topics/enforcer-https.adoc[leveloffset=+3]
|
include::topics/enforcer-https.adoc[leveloffset=+2]
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
[[_enforcer_claim_information_point]]
|
||||||
|
= Claim Information Point
|
||||||
|
|
||||||
|
A Claim Information Point (CIP) is responsible for resolving claims and pushing these claims to the {project_name} server
|
||||||
|
in order to provide more information about the access context to policies. They can be defined as a configuration option
|
||||||
|
to the policy-enforcer in order to resolve claims from different sources, such as:
|
||||||
|
|
||||||
|
* HTTP Request (parameters, headers, body, headers, etc)
|
||||||
|
* External HTTP Service
|
||||||
|
* Static values defined in configuration
|
||||||
|
* Any other source by implementing the Claim Information Provider SPI
|
||||||
|
|
||||||
|
When pushing claims to the {project_name} server, policies can base decisions not only on who an user is but also by taking
|
||||||
|
context and contents into account, based on who, what, why, when, where, and which for a given transaction. It is all about
|
||||||
|
Contextual-based Authorization and how to use runtime information in order to support fine-grained authorization decisions.
|
||||||
|
|
||||||
|
== Obtaining information from the HTTP Request
|
||||||
|
|
||||||
|
Here are several examples showing how you can extract claims from an HTTP request:
|
||||||
|
|
||||||
|
.keycloak.json
|
||||||
|
```json
|
||||||
|
"policy-enforcer": {
|
||||||
|
"paths": [
|
||||||
|
{
|
||||||
|
"path": "/protected/resource",
|
||||||
|
"claim-information-point": {
|
||||||
|
"claims": {
|
||||||
|
"claim-from-request-parameter": "{request.parameter['a']}",
|
||||||
|
"claim-from-header": "{request.header['b']}",
|
||||||
|
"claim-from-cookie": "{request.cookie['c']}",
|
||||||
|
"claim-from-remoteAddr": "{request.remoteAddr}",
|
||||||
|
"claim-from-method": "{request.method}",
|
||||||
|
"claim-from-uri": "{request.uri}",
|
||||||
|
"claim-from-relativePath": "{request.relativePath}",
|
||||||
|
"claim-from-secure": "{request.secure}",
|
||||||
|
"claim-from-json-body-object": "{request.body['/a/b/c']}",
|
||||||
|
"claim-from-json-body-array": "{request.body['/d/1']}",
|
||||||
|
"claim-from-body": "{request.body}",
|
||||||
|
"claim-from-static-value": "static value",
|
||||||
|
"claim-from-multiple-static-value": ["static", "value"],
|
||||||
|
"param-replace-multiple-placeholder": "Test {keycloak.access_token['/custom_claim/0']} and {request.parameter['a']} "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
== Obtaining information from an External HTTP Service
|
||||||
|
|
||||||
|
Here are several examples showing how you can extract claims from an external HTTP Service:
|
||||||
|
|
||||||
|
.keycloak.json
|
||||||
|
```json
|
||||||
|
"policy-enforcer": {
|
||||||
|
"paths": [
|
||||||
|
{
|
||||||
|
"path": "/protected/resource",
|
||||||
|
"claim-information-point": {
|
||||||
|
"http": {
|
||||||
|
"claims": {
|
||||||
|
"claim-a": "/a",
|
||||||
|
"claim-d": "/d",
|
||||||
|
"claim-d0": "/d/0",
|
||||||
|
"claim-d-all": ["/d/0", "/d/1"]
|
||||||
|
},
|
||||||
|
"url": "http://mycompany/claim-provider",
|
||||||
|
"method": "POST",
|
||||||
|
"headers": {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
"header-b": ["header-b-value1", "header-b-value2"],
|
||||||
|
"Authorization": "Bearer {keycloak.access_token}"
|
||||||
|
},
|
||||||
|
"parameters": {
|
||||||
|
"param-a": ["param-a-value1", "param-a-value2"],
|
||||||
|
"param-subject": "{keycloak.access_token['/sub']}",
|
||||||
|
"param-user-name": "{keycloak.access_token['/preferred_username']}",
|
||||||
|
"param-other-claims": "{keycloak.access_token['/custom_claim']}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
== Static Claims
|
||||||
|
|
||||||
|
.keycloak.json
|
||||||
|
```json
|
||||||
|
"policy-enforcer": {
|
||||||
|
"paths": [
|
||||||
|
{
|
||||||
|
"path": "/protected/resource",
|
||||||
|
"claim-information-point": {
|
||||||
|
"claims": {
|
||||||
|
"claim-from-static-value": "static value",
|
||||||
|
"claim-from-multiple-static-value": ["static", "value"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
== Claim Information Provider SPI
|
||||||
|
|
||||||
|
The Claim Information Provider SPI can be used by developers to support different claim information points in case none of the
|
||||||
|
built-ins providers are enough to address their requirements.
|
||||||
|
|
||||||
|
For example, to implement a new CIP provider you need to implement `org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory`
|
||||||
|
and `ClaimInformationPointProvider` and also provide the file `META-INF/services/org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory`
|
||||||
|
in your application`s classpath.
|
||||||
|
|
||||||
|
Example of `org.keycloak.adapters.authorization.ClaimInformationPointProviderFactory`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class MyClaimInformationPointProviderFactory implements ClaimInformationPointProviderFactory<MyClaimInformationPointProvider> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "my-claims";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(PolicyEnforcer policyEnforcer) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MyClaimInformationPointProvider create(Map<String, Object> config) {
|
||||||
|
return new MyClaimInformationPointProvider(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Every CIP provider must be associated with a name, as defined above in the `MyClaimInformationPointProviderFactory.getName` method. The name
|
||||||
|
will be used to map the configuration from the `claim-information-point` section in the `policy-enforcer` configuration to the implementation.
|
||||||
|
|
||||||
|
When processing requests, the policy enforcer will call the MyClaimInformationPointProviderFactory.create method in order to obtain an
|
||||||
|
instance of MyClaimInformationPointProvider. When called, any configuration defined for this particular CIP provider
|
||||||
|
(via claim-information-point) is passed as a map.
|
||||||
|
|
||||||
|
Example of `ClaimInformationPointProvider`:
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class MyClaimInformationPointProvider implements ClaimInformationPointProvider {
|
||||||
|
|
||||||
|
private final Map<String, Object> config;
|
||||||
|
|
||||||
|
public ClaimsInformationPointProvider(Map<String, Object> config) {
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> resolve(HttpFacade httpFacade) {
|
||||||
|
Map<String, List<String>> claims = new HashMap<>();
|
||||||
|
|
||||||
|
// put whatever claim you want into the map
|
||||||
|
|
||||||
|
return claims;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
|
@ -1,37 +0,0 @@
|
||||||
[[_enforcer_bearer]]
|
|
||||||
= Protecting a Stateless Service Using a Bearer Token
|
|
||||||
|
|
||||||
If the adapter is configured with the `bearer-only` configuration option, the policy enforcer decides whether a request
|
|
||||||
to access a protected resource is allowed or denied based on the permissions of the bearer token.
|
|
||||||
|
|
||||||
. HTTP GET example passing an RPT as a bearer token
|
|
||||||
```bash
|
|
||||||
GET /my-resource-server/my-protected-resource HTTP/1.1
|
|
||||||
Host: host.com
|
|
||||||
Authorization: Bearer ${RPT}
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
In this example, a *keycloak.json* file in your application is similar to the following:
|
|
||||||
|
|
||||||
.Example of WEB-INF/keycloak.json with the bearer-only configuration option
|
|
||||||
```json
|
|
||||||
...
|
|
||||||
"bearer-only" : true,
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
== Authorization Response
|
|
||||||
|
|
||||||
When a client tries to access a resource server with a bearer token that is lacking permissions to access a protected resource, the resource server
|
|
||||||
responds with a *401* status code and a `WWW-Authenticate` header. The value of the `WWW-Authenticate` header depends on the authorization protocol
|
|
||||||
in use by the resource server.
|
|
||||||
|
|
||||||
Here is an example of a response from a resource server that is using UMA as the authorization protocol:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
HTTP/1.1 401 Unauthorized
|
|
||||||
WWW-Authenticate: UMA realm="photoz-restful-api",as_uri="https://${host}:${post}/auth/realms/${realm}",ticket="${PERMISSION_TICKET}"
|
|
||||||
```
|
|
||||||
|
|
||||||
Once a client receives a response from the server, it examines the status code and `WWW-Authenticate` header to obtain an RPT from the {project_name} Server.
|
|
|
@ -1,16 +1,5 @@
|
||||||
[[_enforcer_filter]]
|
[[_enforcer_filter]]
|
||||||
= {project_name} Adapter Policy Enforcer
|
= Configuration
|
||||||
|
|
||||||
You can enforce authorization decisions for your applications if you are using {project_name} OIDC adapters.
|
|
||||||
|
|
||||||
When you enable policy enforcement for your {project_name} application, the corresponding adapter intercepts all requests to your application and enforces the authorization decisions obtained from the server.
|
|
||||||
|
|
||||||
Policy enforcement is strongly linked to your application's paths and the <<_resource_overview, resources>> you created for a resource server using the {project_name} Administration Console. By default,
|
|
||||||
when you create a resource server, {project_name} creates a <<_resource_server_default_config, default configuration>> for your resource server so you can enable policy enforcement quickly.
|
|
||||||
|
|
||||||
The default configuration allows access for all resources in your application provided the authenticated user belongs to the same realm as the resource server being protected.
|
|
||||||
|
|
||||||
== Policy Enforcement Configuration
|
|
||||||
|
|
||||||
To enable policy enforcement for your application, add the following property to your *keycloak.json* file:
|
To enable policy enforcement for your application, add the following property to your *keycloak.json* file:
|
||||||
|
|
||||||
|
@ -156,7 +145,9 @@ Specifies how policies are enforced.
|
||||||
+
|
+
|
||||||
**** *DISABLED*
|
**** *DISABLED*
|
||||||
+
|
+
|
||||||
Disables the evaluation of policies for a path
|
*** *claim-information-point*
|
||||||
|
+
|
||||||
|
Defines a set of one or more claims that must be resolved and pushed to the {project_name} server in order to make these claims available to policies. See <<_enforcer_claim_information_point, Claim Information Point>> for more details.
|
||||||
** *lazy-load-paths*
|
** *lazy-load-paths*
|
||||||
+
|
+
|
||||||
Specifies how the adapter should fetch the server for resources associated with paths in your application. If true, the policy
|
Specifies how the adapter should fetch the server for resources associated with paths in your application. If true, the policy
|
||||||
|
|
|
@ -6,3 +6,23 @@ to implement PEPs for different platforms, environments, and programming languag
|
||||||
and leverages OAuth2 authorization capabilities for fine-grained authorization using a centralized authorization server.
|
and leverages OAuth2 authorization capabilities for fine-grained authorization using a centralized authorization server.
|
||||||
|
|
||||||
image:images/pep-pattern-diagram.png[alt="PEP Overview"]
|
image:images/pep-pattern-diagram.png[alt="PEP Overview"]
|
||||||
|
|
||||||
|
A PEP is responsible for enforcing access decisions from the {project_name} server where these decisions are taken by evaluating the policies
|
||||||
|
associated with a protected resource. It acts as a filter or interceptor in your application in order to check whether or not a particular request
|
||||||
|
to a protected resource can be fulfilled based on the permissions granted by these decisions.
|
||||||
|
|
||||||
|
If you are using any of the {project_name} OIDC adapters, you can easily enable the policy enforcer by adding the following property to your *keycloak.json* file:
|
||||||
|
|
||||||
|
.keycloak.json
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"policy-enforcer": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When you enable the policy enforcer all requests sent your application are intercepted and access to protected resources will be granted
|
||||||
|
depending on the permissions granted by {project_name} to the identity making the request.
|
||||||
|
|
||||||
|
Policy enforcement is strongly linked to your application's paths and the <<_resource_overview, resources>> you created for a resource server using the {project_name} Administration Console. By default,
|
||||||
|
when you create a resource server, {project_name} creates a <<_resource_server_default_config, default configuration>> for your resource server so you can enable policy enforcement quickly.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue