Merge pull request #15 from pedroigor/master
More screenshots. Reviewing getting started tutorials.
14
SUMMARY.adoc
|
@ -3,22 +3,14 @@
|
||||||
. link:topics/overview/overview.adoc[Overview]
|
. link:topics/overview/overview.adoc[Overview]
|
||||||
.. link:topics/overview/architecture.adoc[Architecture]
|
.. link:topics/overview/architecture.adoc[Architecture]
|
||||||
.. link:topics/overview/terminology.adoc[Terminology]
|
.. link:topics/overview/terminology.adoc[Terminology]
|
||||||
. link:topics/getting-started/getting-started.adoc[Getting Started]
|
|
||||||
.. link:topics/getting-started/hello-world.adoc[Hello Authorization World]
|
|
||||||
.. link:topics/getting-started/hello-world-servlet-authz.adoc[Securing a Servlet Application]
|
|
||||||
. link:topics/resource-server/overview.adoc[Managing Resource Servers]
|
. link:topics/resource-server/overview.adoc[Managing Resource Servers]
|
||||||
.. link:topics/resource-server/view.adoc[Viewing Resource Servers]
|
|
||||||
.. link:topics/resource-server/create.adoc[Creating Resource Servers]
|
|
||||||
.. link:topics/resource-server/defining-authz-scopes.adoc[Defining the AuthZ Services Scopes]
|
|
||||||
.. link:topics/resource-server/export-configuration.adoc[Exporting Resource Server Configuration]
|
|
||||||
. link:topics/resource/overview.adoc[Managing Resources]
|
. link:topics/resource/overview.adoc[Managing Resources]
|
||||||
.. link:topics/resource/view.adoc[Viewing Resources]
|
.. link:topics/resource/view.adoc[Viewing Resources]
|
||||||
.. link:topics/resource/create.adoc[Creating Resources]
|
.. link:topics/resource/create.adoc[Creating Resources]
|
||||||
. link:topics/permission/overview.adoc[Managing Permissions]
|
. link:topics/permission/overview.adoc[Managing Permissions]
|
||||||
.. link:topics/permission/view.adoc[Viewing Permissions]
|
|
||||||
.. link:topics/permission/create-resource.adoc[Creating Resource-based Permissions]
|
.. link:topics/permission/create-resource.adoc[Creating Resource-based Permissions]
|
||||||
.. link:topics/permission/create-scope.adoc[Creating Scope-based Permissions]
|
.. link:topics/permission/create-scope.adoc[Creating Scope-based Permissions]
|
||||||
.. link:topics/permission/policy-decision-strategy.adoc[Policy Decision Strategies]
|
.. link:topics/permission/decision-strategy.adoc[Policy Decision Strategies]
|
||||||
. link:topics/policy/overview.adoc[Managing Policies]
|
. link:topics/policy/overview.adoc[Managing Policies]
|
||||||
.. link:topics/policy/user-policy.adoc[User-Based Policy]
|
.. link:topics/policy/user-policy.adoc[User-Based Policy]
|
||||||
.. link:topics/policy/role-policy.adoc[Role-Based Policy]
|
.. link:topics/policy/role-policy.adoc[Role-Based Policy]
|
||||||
|
@ -43,7 +35,5 @@
|
||||||
... link:topics/service/entitlement/entitlement-api-aapi.adoc[Requesting Entitlements]
|
... link:topics/service/entitlement/entitlement-api-aapi.adoc[Requesting Entitlements]
|
||||||
.. link:topics/service/client-api.adoc[Authorization Client Java API]
|
.. link:topics/service/client-api.adoc[Authorization Client Java API]
|
||||||
. link:topics/enforcer/overview.adoc[Policy Enforcers]
|
. link:topics/enforcer/overview.adoc[Policy Enforcers]
|
||||||
.. link:topics/enforcer/keycloak-enforcement-filter.adoc[Keycloak Servlet Policy Enforcer]
|
.. link:topics/enforcer/keycloak-enforcement-filter.adoc[Keycloak Adapter Policy Enforcer]
|
||||||
.. link:topics/enforcer/entitlement-bearer-enforcement-filter.adoc[Entitlement Bearer Token Servlet Policy Enforcer]
|
|
||||||
.. link:topics/enforcer/uma-bearer-enforcement-filter.adoc[UMA Bearer Token Servlet Policy Enforcer]
|
|
||||||
. link:topics/example/overview.adoc[Examples]
|
. link:topics/example/overview.adoc[Examples]
|
Before Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 64 KiB |
BIN
images/permission/create-resource.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
images/permission/create-scope.png
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
images/permission/view.png
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
images/policy/create-aggregated.png
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
images/policy/create-drools.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
images/policy/create-js.png
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
images/policy/create-role.png
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
images/policy/create-time.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
images/policy/create-user.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
images/policy/view.png
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
images/resource-server/create.png
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
images/resource-server/manage.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
images/resource-server/view.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
images/resource/create.png
Normal file
After Width: | Height: | Size: 70 KiB |
BIN
images/resource/view.png
Normal file
After Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 22 KiB |
|
@ -1,50 +1,23 @@
|
||||||
== Keycloak Adapter Enforcement Filter
|
== Keycloak Adapter Policy Enforcer
|
||||||
|
|
||||||
The *org.keycloak.authorization.policy.enforcer.servlet.KeycloakAdapterEnforcementFilter* can be used to protect applications
|
You can enforce authorization decisions to your applications if you are using {{book.project.name}} OIDC Adapters.
|
||||||
using the {{book.project.name}} OpenID Connect Adapters.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
Before configuring this filter, make sure your application is properly configure to authenticate against a {{book.project.name}} Server. To use this filter,
|
|
||||||
you need the Keycloak OIDC Adapter properly configured and authenticating your users.
|
|
||||||
|
|
||||||
To configure it, change your web application descriptor (WEB-INF/web.xml) as follows:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<filter>
|
|
||||||
<filter-name>Keycloak Authorization Enforcer</filter-name>
|
|
||||||
<filter-class>org.keycloak.authorization.policy.enforcer.servlet.KeycloakAdapterEnforcementFilter</filter-class>
|
|
||||||
</filter>
|
|
||||||
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>Keycloak Authorization Enforcer</filter-name>
|
|
||||||
<url-pattern>/*</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
```
|
|
||||||
|
|
||||||
=== Configuration
|
=== Configuration
|
||||||
|
|
||||||
The _KeycloakAdapterEnforcementFilter_ can be configured by a simple JSON file. The file can be as simple as:
|
To enable policy enforcement to your application, add the following property to your *keycloak.json* file:
|
||||||
|
|
||||||
|
.keycloak.json
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"client": {
|
"policy-enforcer": {}
|
||||||
"configurationUrl": "http://localhost:8080/auth/realms/{realm_name}/authz/uma_configuration",
|
|
||||||
"clientId": "{client_id}",
|
|
||||||
"clientSecret": "{client_secret}"
|
|
||||||
},
|
|
||||||
"enforcer": {}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
Or a little more verbose if you want to manually define the resources being protected:
|
Or a little more verbose if you want to manually define the resources being protected:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"client": {
|
"policy-enforcer": {
|
||||||
"configurationUrl": "http://localhost:8080/auth/realms/{realm_name}/authz/uma_configuration",
|
"user-managed-access" : {},
|
||||||
"clientId": "{client_id}",
|
|
||||||
"clientSecret": "{client_secret}"
|
|
||||||
},
|
|
||||||
"enforcer": {
|
|
||||||
"paths": [
|
"paths": [
|
||||||
{
|
{
|
||||||
"path" : "/someUri/*",
|
"path" : "/someUri/*",
|
||||||
|
@ -81,42 +54,29 @@ Or a little more verbose if you want to manually define the resources being prot
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The file with the enforcer configuration must be under your application's classpath as *keycloak-authz.json".
|
|
||||||
|
|
||||||
Here is a description of each configuration option:
|
Here is a description of each configuration option:
|
||||||
|
|
||||||
* *client*
|
* *policy-enforcer*
|
||||||
+
|
+
|
||||||
Specify the information about the resource server being protected
|
Specify the configuration options that define how policies are actually enforced and optionally the paths you want to protect. If empty, the policy enforcer will query the server
|
||||||
+
|
for all resources associated with the resource server being protected. In this case, you need to make sure the resources are properly configured with a link:../resource/create.adoc#_uri[URI] property that matches the paths
|
||||||
** *configurationUrl*
|
|
||||||
+
|
|
||||||
A URL pointing to a configuration endpoint. This is important for the automatic discovery of the services invoked during the authorization process.
|
|
||||||
+
|
|
||||||
** *clientId*
|
|
||||||
+
|
|
||||||
The id of the client acting as a resource server.
|
|
||||||
+
|
|
||||||
** *clientSecret*
|
|
||||||
+
|
|
||||||
The client secret.
|
|
||||||
+
|
|
||||||
* *enforcer*
|
|
||||||
+
|
|
||||||
Specify the configuration options to define how policies are actually enforced and optionally the paths you want to protect. If empty, the policy enforcer will query the server
|
|
||||||
for all resources associated with the resource server being protected. In this case, you need to make sure the resources are properly configured with a *uri* property that matches the paths
|
|
||||||
you want to protect.
|
you want to protect.
|
||||||
+
|
+
|
||||||
|
** *user-managed-access*
|
||||||
|
+
|
||||||
|
Tells the adapter to use the UMA protocol. If set, the adapter will ask the server for permission tickets and return them to clients accordingly with UMA specification. If not set,
|
||||||
|
the adapter will just rely on the RPT sent to the server to actually enforce permissions.
|
||||||
|
+
|
||||||
** *paths*
|
** *paths*
|
||||||
+
|
+
|
||||||
Specify the paths to protect.
|
Specify the paths to protect.
|
||||||
+
|
+
|
||||||
*** *name*
|
*** *name*
|
||||||
+
|
+
|
||||||
The name of a resource in the server that must be associated with a given path. If provided, the policy enforcer will ignore the resource's *uri* property and use the path you provided.
|
The name of a resource in the server that must be associated with a given path. When used in conjunction with a *path*, the policy enforcer will ignore the resource's *URI* property and will use the path you provided instead.
|
||||||
*** *path*
|
*** *path*
|
||||||
+
|
+
|
||||||
A URI relative to the application's context path. If this option is provided, the policy enforcer will query the server for a resource with a *uri* with the same value.
|
A URI relative to the application's context path. If this option is provided, the policy enforcer will query the server for a resource with a *URI* with the same value. This option is REQUIRED.
|
||||||
Right now, we support some very basic logic for path matching. Examples of valid paths are:
|
Right now, we support some very basic logic for path matching. Examples of valid paths are:
|
||||||
+
|
+
|
||||||
**** Wildcards: `/*`
|
**** Wildcards: `/*`
|
||||||
|
@ -135,22 +95,3 @@ The name of the HTTP method.
|
||||||
**** *scopes*
|
**** *scopes*
|
||||||
+
|
+
|
||||||
An array of strings with the scopes associated with the method.
|
An array of strings with the scopes associated with the method.
|
||||||
|
|
||||||
=== Container Specific Configuration
|
|
||||||
|
|
||||||
==== WildFly 10.0.0.Final
|
|
||||||
|
|
||||||
In order to make the filter available to your application at runtime, you must create a *META-INF/jboss-deployment-structure.xml* at the application root directory.
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<jboss-deployment-structure>
|
|
||||||
<deployment>
|
|
||||||
<dependencies>
|
|
||||||
<module name="org.keycloak.keycloak-authz-servlet-enforcer" services="import"/>
|
|
||||||
<module name="org.jboss.resteasy.resteasy-jackson2-provider" services="import"/>
|
|
||||||
</dependencies>
|
|
||||||
<exclusions>
|
|
||||||
<module name="org.jboss.resteasy.resteasy-jackson-provider"/>
|
|
||||||
</exclusions>
|
|
||||||
</deployment>
|
|
||||||
</jboss-deployment-structure>
|
|
|
@ -5,8 +5,3 @@ to implement PEPs for different platforms, environments and using different prog
|
||||||
fully RESTFUL-based, leveraging OAuth2 authorization capabilities to also support fine-grained authorization using a centralized authorization server.
|
fully RESTFUL-based, leveraging OAuth2 authorization capabilities to also support 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"]
|
||||||
|
|
||||||
=== Download and Installation
|
|
||||||
|
|
||||||
The avaliable policy enforcers are distributed just like any other {{book.project.name}} Adapter. Please check this https://keycloak.gitbooks.io/securing-client-applications-guide/content/[documentation]
|
|
||||||
for more details about how to obtain and install these adapters accordingly with the platform you are using.
|
|
|
@ -1,16 +0,0 @@
|
||||||
== Getting Started
|
|
||||||
|
|
||||||
The purpose of this guide is to get you up and running as quickly as possible so that you can play with and test drive various authorization features that {{book.project.name}} has. It will help
|
|
||||||
you to understand the basic steps to enable fine-grained authorization to an application and how to interact with the Authorization Services provided by {{book.project.name}}.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
This guide assume that you are able to install and boot a {{book.project.name}} Server. For more information, please follow the intrusctions https://keycloak.gitbooks.io/getting-started-tutorials/content/[here].
|
|
||||||
|
|
||||||
All the guides are based on the *{{book.project.name}} Demo Distribution*. Please download it before going further with any guide.
|
|
||||||
|
|
||||||
Make sure you have a {{book.project.name}} instance up and running on http://localhost:8080/auth[http://localhost:8080/auth]. If everything is OK, you should be able to login to the
|
|
||||||
_Administration Console_ and get a page like that:
|
|
||||||
|
|
||||||
image:../../images/gs-keycloak-console-page.png[alt="Keycloak Administration Console"]
|
|
||||||
|
|
||||||
Source code for all examples can be obtained from *${KEYCLOAK_DEMO_SERVER_DIR}/examples/authz/*.
|
|
|
@ -1,454 +0,0 @@
|
||||||
== Securing a Servlet Application
|
|
||||||
|
|
||||||
This guide will show you how to:
|
|
||||||
|
|
||||||
* Create a realm with the necessary configuration to enable fine-grained authorization to a servlet application
|
|
||||||
* Create a resource server and the resources that must be protected
|
|
||||||
* Create permissions, authorization policies and how to apply them to your protected resources
|
|
||||||
* Configure the {{book.project.name}} Authorization Enforcer Filter to your servlet application
|
|
||||||
|
|
||||||
The application we are using in this guide is one of the examples provided by the {{book.project.name}} Demo Distribution. You can find all the source code
|
|
||||||
under *examples/authz/servlet-authz/*.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
Before going further, make sure you followed all the instructions in the link:../getting-started/getting-started.html[Getting Started] guide.
|
|
||||||
|
|
||||||
=== About the Servlet Application
|
|
||||||
|
|
||||||
The application we are about to create is a very simple. In a nutshell, it implements the following security requirements:
|
|
||||||
|
|
||||||
* An _Administration Area_ that only administrators can access
|
|
||||||
* An _User Premium Area_ that only users with a premium plan can access
|
|
||||||
* A dynamic menu that is generated depending on the permissions granted an user
|
|
||||||
|
|
||||||
image:../../images/servlet-authz-app-structure.png[alt="Servlet Authz Application Structure"]
|
|
||||||
|
|
||||||
=== Creating the Servlet Authz Realm
|
|
||||||
|
|
||||||
For this guide, we are going to create a *servlet-authz* realm. Just import the following JSON file to create the new realm:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"realm": "servlet-authz",
|
|
||||||
"enabled": true,
|
|
||||||
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
|
|
||||||
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
|
||||||
"requiredCredentials": [
|
|
||||||
"password"
|
|
||||||
],
|
|
||||||
"users": [
|
|
||||||
{
|
|
||||||
"username": "alice",
|
|
||||||
"enabled": true,
|
|
||||||
"credentials": [
|
|
||||||
{
|
|
||||||
"type": "password",
|
|
||||||
"value": "alice"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"realmRoles": [
|
|
||||||
"user"
|
|
||||||
],
|
|
||||||
"clientRoles": {
|
|
||||||
"servlet-authz-app": [
|
|
||||||
"uma_authorization",
|
|
||||||
"kc_entitlement"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"username": "jdoe",
|
|
||||||
"enabled": true,
|
|
||||||
"credentials": [
|
|
||||||
{
|
|
||||||
"type": "password",
|
|
||||||
"value": "jdoe"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"realmRoles": [
|
|
||||||
"user",
|
|
||||||
"user_premium"
|
|
||||||
],
|
|
||||||
"clientRoles": {
|
|
||||||
"servlet-authz-app": [
|
|
||||||
"uma_authorization",
|
|
||||||
"kc_entitlement"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"username": "admin",
|
|
||||||
"enabled": true,
|
|
||||||
"credentials": [
|
|
||||||
{
|
|
||||||
"type": "password",
|
|
||||||
"value": "admin"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"realmRoles": [
|
|
||||||
"user",
|
|
||||||
"admin"
|
|
||||||
],
|
|
||||||
"clientRoles": {
|
|
||||||
"realm-management": [
|
|
||||||
"realm-admin"
|
|
||||||
],
|
|
||||||
"servlet-authz-app": [
|
|
||||||
"uma_authorization",
|
|
||||||
"kc_entitlement"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"username": "service-account-servlet-authz-app",
|
|
||||||
"enabled": true,
|
|
||||||
"serviceAccountClientId": "servlet-authz-app",
|
|
||||||
"realmRoles": [
|
|
||||||
"uma_protection"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"roles": {
|
|
||||||
"realm": [
|
|
||||||
{
|
|
||||||
"name": "user",
|
|
||||||
"description": "User privileges"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "admin",
|
|
||||||
"description": "Administrator privileges"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "user_premium",
|
|
||||||
"description": "User Premium privileges"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "uma_protection",
|
|
||||||
"description": "Allows access to the Protection API"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"clients": [
|
|
||||||
{
|
|
||||||
"clientId": "servlet-authz-app",
|
|
||||||
"enabled": true,
|
|
||||||
"publicClient": false,
|
|
||||||
"baseUrl": "/servlet-authz-app",
|
|
||||||
"adminUrl": "/servlet-authz-app",
|
|
||||||
"bearerOnly": false,
|
|
||||||
"serviceAccountsEnabled": true,
|
|
||||||
"redirectUris": [
|
|
||||||
"/servlet-authz-app/*"
|
|
||||||
],
|
|
||||||
"secret": "secret"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
=== Creating a Resource Server and Protecting Resources
|
|
||||||
|
|
||||||
Now that we have the *servlet-authz* realm properly configured, we need to enable the *servlet-authz-app* as a resource server. For that, click on the *Authorization* in the left menu bar.
|
|
||||||
|
|
||||||
image:../../images/gs-keycloak-authz-page.png[alt="Keycloak Authorization Page"]
|
|
||||||
|
|
||||||
To create a resource server you can click on the *Create* button.
|
|
||||||
|
|
||||||
image:../../images/gs-keycloak-authz-create-rs-page.png[alt="Create Resource Server"]
|
|
||||||
|
|
||||||
From that page you can create a resource server by manually filling that form or you can just import a JSON file with the configuration you want. For this guide, we'll just import a JSON file as follows:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"clientId": "servlet-authz-app",
|
|
||||||
"allowRemoteResourceManagement": true,
|
|
||||||
"allowEntitlements": true,
|
|
||||||
"policyEnforcementMode": "ENFORCING",
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"name": "Admin Resource",
|
|
||||||
"uri": "/protected/admin/*",
|
|
||||||
"type": "http://servlet-authz/protected/admin",
|
|
||||||
"scopes": [
|
|
||||||
{
|
|
||||||
"name": "urn:servlet-authz:protected:admin:access"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Protected Resource",
|
|
||||||
"uri": "/*",
|
|
||||||
"type": "http://servlet-authz/protected/resource",
|
|
||||||
"scopes": [
|
|
||||||
{
|
|
||||||
"name": "urn:servlet-authz:protected:resource:access"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Premium Resource",
|
|
||||||
"uri": "/protected/premium/*",
|
|
||||||
"type": "urn:servlet-authz:protected:resource",
|
|
||||||
"scopes": [
|
|
||||||
{
|
|
||||||
"name": "urn:servlet-authz:protected:premium:access"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Main Page",
|
|
||||||
"type": "urn:servlet-authz:protected:resource",
|
|
||||||
"scopes": [
|
|
||||||
{
|
|
||||||
"name": "urn:servlet-authz:page:main:actionForAdmin"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "urn:servlet-authz:page:main:actionForUser"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "urn:servlet-authz:page:main:actionForPremiumUser"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"policies": [
|
|
||||||
{
|
|
||||||
"name": "Any Admin Policy",
|
|
||||||
"description": "Defines that adminsitrators can do something",
|
|
||||||
"type": "role",
|
|
||||||
"config": {
|
|
||||||
"roles": "[\"admin\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Any User Policy",
|
|
||||||
"description": "Defines that any user can do something",
|
|
||||||
"type": "role",
|
|
||||||
"config": {
|
|
||||||
"roles": "[\"user\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Only Premium User Policy",
|
|
||||||
"description": "Defines that only premium users can do something",
|
|
||||||
"type": "role",
|
|
||||||
"logic": "POSITIVE",
|
|
||||||
"config": {
|
|
||||||
"roles": "[\"user_premium\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "All Users Policy",
|
|
||||||
"description": "Defines that all users can do something",
|
|
||||||
"type": "aggregate",
|
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
|
||||||
"config": {
|
|
||||||
"applyPolicies": "[\"Any User Policy\",\"Any Admin Policy\",\"Only Premium User Policy\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Premium Resource Permission",
|
|
||||||
"description": "A policy that defines access to premium resources",
|
|
||||||
"type": "resource",
|
|
||||||
"decisionStrategy": "UNANIMOUS",
|
|
||||||
"config": {
|
|
||||||
"resources": "[\"Premium Resource\"]",
|
|
||||||
"applyPolicies": "[\"Only Premium User Policy\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Administrative Resource Permission",
|
|
||||||
"description": "A policy that defines access to administrative resources",
|
|
||||||
"type": "resource",
|
|
||||||
"decisionStrategy": "UNANIMOUS",
|
|
||||||
"config": {
|
|
||||||
"resources": "[\"Admin Resource\"]",
|
|
||||||
"applyPolicies": "[\"Any Admin Policy\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Protected Resource Permission",
|
|
||||||
"description": "A policy that defines access to any protected resource",
|
|
||||||
"type": "resource",
|
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
|
||||||
"config": {
|
|
||||||
"resources": "[\"Protected Resource\"]",
|
|
||||||
"applyPolicies": "[\"All Users Policy\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Action 1 on Main Page Resource Permission",
|
|
||||||
"description": "A policy that defines access to action 1 on the main page",
|
|
||||||
"type": "scope",
|
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
|
||||||
"config": {
|
|
||||||
"scopes": "[\"urn:servlet-authz:page:main:actionForAdmin\"]",
|
|
||||||
"applyPolicies": "[\"Any Admin Policy\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Action 2 on Main Page Resource Permission",
|
|
||||||
"description": "A policy that defines access to action 2 on the main page",
|
|
||||||
"type": "scope",
|
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
|
||||||
"config": {
|
|
||||||
"scopes": "[\"urn:servlet-authz:page:main:actionForUser\"]",
|
|
||||||
"applyPolicies": "[\"Any User Policy\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Action 3 on Main Page Resource Permission",
|
|
||||||
"description": "A policy that defines access to action 3 on the main page",
|
|
||||||
"type": "scope",
|
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
|
||||||
"config": {
|
|
||||||
"scopes": "[\"urn:servlet-authz:page:main:actionForPremiumUser\"]",
|
|
||||||
"applyPolicies": "[\"Only Premium User Policy\"]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
All this configuration can also be done using the {{book.project.name}} Administration Console. We are using the import tool just for demonstration purposes
|
|
||||||
|
|
||||||
=== A Quick Overview of the Permissions and Policies
|
|
||||||
|
|
||||||
The resource server configuration tells a lot about what we are really protecting. It is basically describing the security requirements we have discussed earlier.
|
|
||||||
|
|
||||||
Fist of all, we define four resources:
|
|
||||||
|
|
||||||
* *Admin Resource*
|
|
||||||
* *Protected Resource*
|
|
||||||
* *Premium Resource*
|
|
||||||
* *Main Page*
|
|
||||||
|
|
||||||
As the name implies, each of these resources are related with the requirements we previously discussed, representing the different areas or group of resources we want to protect.
|
|
||||||
|
|
||||||
As you may notice, each of these resources (except Main Page) defines an *uri* property. This property represents the path we want to protect and maps directly, or indirectly by using a pattern,
|
|
||||||
to the resources served by the application.
|
|
||||||
|
|
||||||
Let's take the _Protected Resource_ as an example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "Protected Resource",
|
|
||||||
"uri": "/*",
|
|
||||||
"type": "http://servlet-authz/protected/resource",
|
|
||||||
"scopes": [
|
|
||||||
{
|
|
||||||
"name": "urn:servlet-authz:protected:resource:access"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
```
|
|
||||||
This resource represents all resources in the application, as you can see from the pattern used in the *uri* property. It also defines a single scope/action to indicate that users, if granted, can access this resource.
|
|
||||||
|
|
||||||
Now, let's see what are the permissions and authorization policies configured to this resource:
|
|
||||||
|
|
||||||
```json
|
|
||||||
...
|
|
||||||
{
|
|
||||||
"name": "Protected Resource Permission",
|
|
||||||
"description": "A policy that defines access to any protected resource",
|
|
||||||
"type": "resource",
|
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
|
||||||
"config": {
|
|
||||||
"resources": "[\"Protected Resource\"]",
|
|
||||||
"applyPolicies": "[\"All Users Policy\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
The definition above is a permission that links the _Protected Resource_ with the policies we want to apply. In this case, we are applying a single _All Users Policy_.
|
|
||||||
|
|
||||||
Policies define the conditions to be meet in order to access something. In this case, the _All Users Policy_ is composed of two other policies, a special policy type called link:../policy/aggregated-policy.html[Aggregated Policies].
|
|
||||||
|
|
||||||
```json
|
|
||||||
...
|
|
||||||
{
|
|
||||||
"name": "All Users Policy",
|
|
||||||
"description": "Defines that all users can do something",
|
|
||||||
"type": "aggregate",
|
|
||||||
"decisionStrategy": "AFFIRMATIVE",
|
|
||||||
"config": {
|
|
||||||
"applyPolicies": "[\"Any User Policy\",\"Any Admin Policy\",\"Only Premium User Policy\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
{{book.project.name}} provides a few built-in policy types (and their respective policy providers) implementing different access control mechanisms (RBAC, GBAC, Rule-based, Time-based, etc) that you can use to build your own policies and permissions.
|
|
||||||
|
|
||||||
Aggregated policies are very useful in order to group related policies together and make policy management less painful.
|
|
||||||
|
|
||||||
=== Configuring the Keycloak Enforcement Filter
|
|
||||||
|
|
||||||
Now that we have all the configuration for our resource server in place, we can configure the authorization enforcement filter to actually protect resources.
|
|
||||||
|
|
||||||
Just like any other Servlet Filter, you just need to include the following configuration in your web application descriptor (WEB-INF/web.xml):
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<filter>
|
|
||||||
<filter-name>Keycloak Authorization Enforcer</filter-name>
|
|
||||||
<filter-class>org.keycloak.authorization.policy.enforcer.servlet.KeycloakAdapterEnforcementFilter</filter-class>
|
|
||||||
</filter>
|
|
||||||
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>Keycloak Authorization Enforcer</filter-name>
|
|
||||||
<url-pattern>/*</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
```
|
|
||||||
The *org.keycloak.authorization.policy.enforcer.servlet.KeycloakAdapterEnforcementFilter* is shipped with the {{book.project.name}} OIDC Adapters distribution. In order to make it available to
|
|
||||||
your application at runtime, you must create a *META-INF/jboss-deployment-structure.xml* at the application root directory. If you are using maven, this file can be placed under *src/main/webapp/META-INF/jboss-deployment-structure.xml*:
|
|
||||||
|
|
||||||
```xml
|
|
||||||
<jboss-deployment-structure>
|
|
||||||
<deployment>
|
|
||||||
<dependencies>
|
|
||||||
<module name="org.keycloak.keycloak-authz-servlet-enforcer" services="import"/>
|
|
||||||
<module name="org.jboss.resteasy.resteasy-jackson2-provider" services="import"/>
|
|
||||||
</dependencies>
|
|
||||||
<exclusions>
|
|
||||||
<module name="org.jboss.resteasy.resteasy-jackson-provider"/>
|
|
||||||
</exclusions>
|
|
||||||
</deployment>
|
|
||||||
</jboss-deployment-structure>
|
|
||||||
```
|
|
||||||
|
|
||||||
For last, you need to create a *META-INF/keycloak-authz.json* and put it in your application's classpath. If you are using Maven, this file goes inside *src/main/resources*:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"client": {
|
|
||||||
"configurationUrl": "http://localhost:8080/auth/realms/servlet-authz/authz/uma_configuration",
|
|
||||||
"clientId": "servlet-authz-app",
|
|
||||||
"clientSecret": "secret"
|
|
||||||
},
|
|
||||||
"enforcer": {}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
=== Running and Using the Application
|
|
||||||
|
|
||||||
All the source code for this application is located at *${KEYCLOAK_DEMO_SERVER_DIR}/examples/authz/servlet-authz/*.
|
|
||||||
|
|
||||||
There you can execute the following command to _deploy_ the application to the running server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mvn clean package wildfly:deploy
|
|
||||||
```
|
|
||||||
That should be enough to get the application properly packaged and deployed to a _running_ {{book.project.name}} server.
|
|
||||||
|
|
||||||
If the application was properly deployed, you can try to access it at http://localhost:8080/auth[http://localhost:8080/servlet-authz-app] and use the following credentials to login into the application:
|
|
||||||
|
|
||||||
* username: alice / password: alice (regular user)
|
|
||||||
* username: jdoe / password: jdoe (premium user)
|
|
||||||
* username: admin / password: admin (administrator)
|
|
||||||
|
|
||||||
To _undeploy_ the application, please execute the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
mvn wildfly:undeploy
|
|
||||||
```
|
|
|
@ -1,126 +0,0 @@
|
||||||
== Obtaining Permissions using UMA
|
|
||||||
|
|
||||||
One of the main features provided by {{book.project.name}} {{book.project.module}} is support for https://docs.kantarainitiative.org/uma/rec-uma-core.html[UMA] specification. UMA defines :
|
|
||||||
|
|
||||||
"`... how resource owners can control protected-resource access by clients operated by arbitrary requesting parties, where the resources reside on any number of resource servers, and where a centralized authorization server governs access based on resource owner policies.`"
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
For now, {{book.project.name}} focus on API security and doesn't fully implement UMA. We don't support, yet, authorization flows where the user can manage their own resources and policies, share resources, and so forth.
|
|
||||||
|
|
||||||
=== Obtaining a Protection API Token (PAT)
|
|
||||||
The authorization process begins with a special type of token: the *Permission Ticket*. A permission ticket can only be obtained by a resource server using a OAuth2 access token with the scope
|
|
||||||
*uma_protection*. In UMA, the access token with the scope *uma_protection* is called a Protection API Token or PAT.
|
|
||||||
|
|
||||||
When we created the *hello-world-authz* realm, we also granted the scope *uma_protection* to the *hello-world-authz-service* client application. Considering that this application is also a *confidential* client,
|
|
||||||
you can obtain a PAT as follows:
|
|
||||||
|
|
||||||
```curl
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpwYXNzd29yZA==" \
|
|
||||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
||||||
-d 'grant_type=client_credentials' \
|
|
||||||
"http://localhost:8080/auth/realms/hello-world-authz/protocol/openid-connect/token"
|
|
||||||
```
|
|
||||||
|
|
||||||
As a result, you will get the following response from the server:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"access_token": ${access_token},
|
|
||||||
"expires_in": 300,
|
|
||||||
"refresh_expires_in": 1800,
|
|
||||||
"refresh_token": ${refresh_token},
|
|
||||||
"token_type": "bearer",
|
|
||||||
"id_token": ${id_token},
|
|
||||||
"not-before-policy": 0,
|
|
||||||
"session_state": "ccea4a55-9aec-4024-b11c-44f6f168439e"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here, the ${access_token} represents a PAT, which is basically an OAuth2 access token with the scope *uma_protection*. With this token, you can now obtain a permission ticket from the
|
|
||||||
Protection API.
|
|
||||||
|
|
||||||
=== Obtaining a Permission Ticket
|
|
||||||
|
|
||||||
Once you have a PAT, you can use it to access the Protection API in order to obtain a permission ticket as follows:
|
|
||||||
|
|
||||||
```json
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: Bearer ${access_token}" \
|
|
||||||
-d '
|
|
||||||
{
|
|
||||||
"resource_set_id" : "Hello World Resource"
|
|
||||||
}
|
|
||||||
' \
|
|
||||||
"http://localhost:8080/auth/realms/hello-world-authz/authz/protection/permission"
|
|
||||||
```
|
|
||||||
|
|
||||||
As a result, you will get the following response from the server:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"ticket": ${permission_ticket}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here, we are asking the Protection API for a permission ticket representing the same extent of requested access that the resource server registered. In this case, the access request to
|
|
||||||
the _Hello World Resource_ resource.
|
|
||||||
|
|
||||||
=== Obtaining an Authorization API Token
|
|
||||||
|
|
||||||
The final step is obtain a *Requesting Party Token* or *RPT* with a set of authorization data, that will be used to actually gain access to protected resources at the resource server.
|
|
||||||
|
|
||||||
A RPT is obtained from the link:../service/authorization-api.html[Authorization API]. Just like the Protection API and Permission Tickets, the Authorization API also requires a special
|
|
||||||
OAuth2 access token to obtain a RPT. In UMA, this access token is called a *Authorization API Token* or *AAT*, which contains a scope *uma_authorization* in it.
|
|
||||||
|
|
||||||
The *uma_authorization* scope indicates that an user consented to allow a given client application to access authorization data on his behalf. When we created the *hello-world-authz* realm,
|
|
||||||
we also defined a client role to the users:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"clientRoles" : {
|
|
||||||
"hello-world-authz-service" : [ "uma_authorization" ]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
To obtain an AAT you can send a request to {{book.project.name}} as follows:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpwYXNzd29yZA==" \
|
|
||||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
|
||||||
-d 'username=alice&password=password&grant_type=password' \
|
|
||||||
"http://localhost:8080/auth/realms/hello-world-authz/protocol/openid-connect/token"
|
|
||||||
```
|
|
||||||
As a result, you will get the following response from the server:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"access_token": ${access_token},
|
|
||||||
"expires_in": 300,
|
|
||||||
"refresh_expires_in": 1800,
|
|
||||||
"refresh_token": ${refresh_token},
|
|
||||||
"token_type": "bearer",
|
|
||||||
"id_token": ${id_token},
|
|
||||||
"not-before-policy": 0,
|
|
||||||
"session_state": "3cad2afc-855b-47b7-8e4d-a21c66e312fb"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
=== Obtaining a Requesting Party Token or RPT
|
|
||||||
|
|
||||||
Now that we have a AAT we can call the Authorization API and ask for a RPT.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST
|
|
||||||
-H "Authorization: Bearer ${AAT}" -d '{
|
|
||||||
"ticket" : ${permission_ticket}
|
|
||||||
}' "http://localhost:8080/auth/realms/hello-world-authz/authz/authorize"
|
|
||||||
```
|
|
||||||
|
|
||||||
As a result, you will get the follow response from the server:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{"rpt":"${rpt}"}
|
|
||||||
```
|
|
||||||
|
|
||||||
=== Obtaining Permissions using the Entitlement API
|
|
|
@ -1,355 +0,0 @@
|
||||||
== Hello Authorization World
|
|
||||||
|
|
||||||
This guide will show you how to:
|
|
||||||
|
|
||||||
* Create a realm with the necessary configuration to enable fine-grained authorization to your applications
|
|
||||||
* Create a resource server and the resources that must be protected
|
|
||||||
* Create permissions, authorization policies and how to apply them to your protected resources
|
|
||||||
* Access the {{book.project.name}} {{book.project.module}} and enforce authorization decisions
|
|
||||||
|
|
||||||
The purpose of this guide is to give you a generic overview of {{book.project.name}} {{book.project.module}} so you can understand
|
|
||||||
some core concepts and start protecting your applications and services despite the platform they are running on.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
Before going further, make sure you followed all the instructions in the link:../getting-started/getting-started.html[Getting Started] guide.
|
|
||||||
|
|
||||||
=== Creating the Hello World AuthZ Realm
|
|
||||||
|
|
||||||
For this guide, we are going to create a *hello-world-authz* realm. Just import the following JSON file to create the new realm:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"realm" : "hello-world-authz",
|
|
||||||
"enabled" : true,
|
|
||||||
"privateKey" : "MIIEpQIBAAKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABAoIBAAwa4wVnKBOIS6srmYPfBTDNsTBBCEjxiYEErmn7JhoWxQ1DCPUxyxU6F177/q9Idqoj1FFOCtEO9P6/9+ym470HQmEQkR2Xxd1d3HOZy9oKuCro3ZbTDkVxY0JnlyxZz4MihGFxDH2e4MArfHy0sAgYbdIU+x2pWKGWSMzDd/TMSOExhc/sIQAg6ljbPCLLXCPQFAncoHRyGPrkRZs6UTZi5SJuCglVa2/3G+0drDdPuA83/mwsZfIBqQgbGbFgtq5T5C6CKMkPOQ42Rcclm7kEr6riTkJRo23EO1iOJVpxzI0tbxZsJAsW7zeqv0wWRyUgVfQAje6OdsNexp5aCtECgYEA6nMHCQ9xXvufCyzpIbYGxdAGqH6m1AR5gXerHqRiGNx+8UUt/E9cy/HTOhmZDK/eC4BT9tImeF01l1oSU/+wGKfux0SeAQchBhhq8GD6jmrtgczKAfZHp0Zrht7o9qu9KE7ZNWRmY1foJN9yNYmzY6qqHEy+zNo9amcqT7UZKO8CgYEA35sp9fMpMqkJE+NEJ9Ph/t2081BEkC0DYIuETZRSi+Ek5AliWTyEkg+oisTbWzi6fMQHS7W+M1SQP6djksLQNPP+353DKgup5gtKS+K/y2xNd7fSsNmkjW1bdJJpID7WzwwmwdahHxpcnFFuEXi5FkG3Vqmtd3cD0TYL33JlRy0CgYEA0+a3eybsDy9Zpp4m8IM3R98nxW8DlimdMLlafs2QpGvWiHdAgwWwF90wTxkHzgG+raKFQVbb0npcj7mnSyiUnxRZqt2H+eHZpUq4jR76F3LpzCGui2tvg+8QDMy4vwqmYyIxDCL8r9mqRnl3HpChBPoh2oY7BahTTjKEeZpzbR0CgYEAoNnVjX+mGzNNvGi4Fo5s/BIwoPcU20IGM+Uo/0W7O7Rx/Thi7x6BnzB0ZZ7GzRA51paNSQEsGXCzc5bOIjzR2cXLisDKK+zIAxwMDhrHLWZzM7OgdGeb38DTEUBhLzkE/VwYZUgoD1+/TxOkwhy9yCzt3gGhL1cF//GJCOwZvuECgYEAgsO4rdYScgCpsyePnHsFk+YtqtdORnmttF3JFcL3w2QneXuRwg2uW2Kfz8CVphrR9eOU0tiw38w6QTHIVeyRY8qqlHtiXj6dEYz7frh/k4hI29HwFx43rRpnAnN8kBEJYBYdbjaQ35Wsqkfu1tvHJ+6fxSwvQu/TVdGp0OfilAY=",
|
|
||||||
"publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB",
|
|
||||||
"certificate" : "MIICsTCCAZkCBgFVETX4AzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFIZWxsbyBXb3JsZCBBdXRoWjAeFw0xNjA2MDIxMzAxMzdaFw0yNjA2MDIxMzAzMTdaMBwxGjAYBgNVBAMMEUhlbGxvIFdvcmxkIEF1dGhaMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQANm5gIT/c50lwjawM686gNXpppLA928WsCOn9NIIWjSKekP8Bf9S73kf7vWcsEppm5B8rRyRxolXmzwghv74L7uVDg8Injjgj+XbPVQP+cJqWpSaMZHF7UfWe0/4M945Xcbmsl5q+m9PmrPG0AaaZhqXHcp4ehB1H+awyRqiERpJUuwZNycw2+2kjDADpsFf8hZVUd1F6ReYyOkqUyUjbL+jYTC7ZBNa7Ok+w6HCXWgkgVATAgQXJRM3w14IOc5MH/vfMCrCl/eNQLbjGl9y7u8PKwh3MXHDO2OLqtg6hOTSrOGUPJZGmGtUAl+2/R7FzoWkML/BNe2hjsL6UJwg91",
|
|
||||||
"requiredCredentials" : [ "password" ],
|
|
||||||
"roles" : {
|
|
||||||
"realm" : [
|
|
||||||
{
|
|
||||||
"name" : "uma_protection", "kc_entitlements"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"users" :
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"username" : "alice",
|
|
||||||
"enabled" : true,
|
|
||||||
"credentials" : [ {
|
|
||||||
"type" : "password",
|
|
||||||
"value" : "password"
|
|
||||||
} ],
|
|
||||||
"clientRoles" : {
|
|
||||||
"hello-world-authz-service" : [ "uma_authorization", "kc_entitlement" ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"username" : "jdoe",
|
|
||||||
"enabled" : true,
|
|
||||||
"credentials" : [ {
|
|
||||||
"type" : "password",
|
|
||||||
"value" : "password"
|
|
||||||
} ],
|
|
||||||
"clientRoles" : {
|
|
||||||
"hello-world-authz-service" : [ "uma_authorization", "kc_entitlement" ]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"username" : "service-account-hello-world-authz-service",
|
|
||||||
"enabled" : true,
|
|
||||||
"serviceAccountClientId" : "hello-world-authz-service",
|
|
||||||
"realmRoles" : [ "uma_protection"]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"clients" : [
|
|
||||||
{
|
|
||||||
"clientId" : "hello-world-authz-service",
|
|
||||||
"secret" : "secret",
|
|
||||||
"serviceAccountsEnabled" : true,
|
|
||||||
"enabled" : true,
|
|
||||||
"redirectUris" : [ "http://localhost:8080/hello-world-authz-service" ],
|
|
||||||
"directAccessGrantsEnabled" : true,
|
|
||||||
"publicClient" : false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
The realm *hello-world-authz* consists of:
|
|
||||||
|
|
||||||
** Two users: _alice_ and _jdoe_
|
|
||||||
** One client application: _hello-world-authz-service_
|
|
||||||
** One global role: _uma_protection_
|
|
||||||
** One client role: _uma_authorization_
|
|
||||||
|
|
||||||
The _hello-world-authz-service_ is the application with the resources we want to protect. In other words, it will act as a link:../overview/terminology.html[Resource Server].
|
|
||||||
|
|
||||||
In {{book.project.name}} a resource server is just a regular client application with some specific characteristics. It _must_ be a *confidential* client application as defined by:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"publicClient" : false
|
|
||||||
```
|
|
||||||
|
|
||||||
It must have a *client_id*, *client_secret* and *Service Account* enabled:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"clients" : [
|
|
||||||
{
|
|
||||||
"clientId" : "hello-world-authz-service",
|
|
||||||
"secret" : "password",
|
|
||||||
"serviceAccountsEnabled" : true,
|
|
||||||
...
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
And finally, an user mapping to the client's service account:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"username" : "service-account-my-resource-server",
|
|
||||||
"enabled" : true,
|
|
||||||
"serviceAccountClientId" : "hello-world-authz-service",
|
|
||||||
"realmRoles" : [ "uma_protection"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In the latter case, we are also granting the *uma_protection* role to the client's service account. As you'll see, that will be necessary in order to get access to the link:../service/protection-api.html[Protection API].
|
|
||||||
|
|
||||||
=== Creating a Resource Server and Protecting Resources
|
|
||||||
|
|
||||||
Now that we have the *hello-world-authz* realm properly configured, we need to enable the *hello-world-authz-service* as a resource server. For that, click on the *Authorization* in the left menu bar.
|
|
||||||
|
|
||||||
image:../../images/gs-keycloak-authz-page.png[alt="Keycloak Authorization Page"]
|
|
||||||
|
|
||||||
To create a resource server you can click on the *Create* button.
|
|
||||||
|
|
||||||
image:../../images/gs-keycloak-authz-create-rs-page.png[alt="Create Resource Server"]
|
|
||||||
|
|
||||||
From that page you can create a resource server by manually filling that form or you can just import a JSON file with the configuration you want. For this guide, we'll just import a JSON file as follows:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"clientId": "hello-world-authz-service",
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"name": "Hello World Resource"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"policies": [
|
|
||||||
{
|
|
||||||
"name": "Only Special Users Policy",
|
|
||||||
"type": "user",
|
|
||||||
"logic": "POSITIVE",
|
|
||||||
"config": {
|
|
||||||
"users": "[\"alice\"]"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Hello World Resource Permission",
|
|
||||||
"type": "resource",
|
|
||||||
"config": {
|
|
||||||
"resources": "[\"Hello World Resource\"]",
|
|
||||||
"applyPolicies": "[\"Only Special Users Policy\"]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
All this configuration can also be done using the {{book.project.name}} Administration Console. We are using the import tool just for demonstration purposes
|
|
||||||
|
|
||||||
After importing the JSON file above, you would see a page like that:
|
|
||||||
|
|
||||||
image:../../images/gs-authz-hello-rs-created-page.png[alt="Resource Server Successfully Created"]
|
|
||||||
|
|
||||||
You may take some time now exploring the resource server we just created. But first, let's understand what we just created.
|
|
||||||
|
|
||||||
The resource server was created based on the *hello-world-authz-service* client application, as you can see from the following configuration:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"clientId": "hello-world-authz-service",
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
What we did was basically tell {{book.project.name}} that we want that client application acting as a resource server, so we can start creating the resources we want to protect as well the permissions
|
|
||||||
and authorization policies we want to use to actually protect its resources.
|
|
||||||
|
|
||||||
The purpose of this guide is keep things simple to get you started, so our newly created resource server has a single protected resource, as defined by the following configuration:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
...
|
|
||||||
"resources": [
|
|
||||||
{
|
|
||||||
"name": "Hello World Resource"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The *Hello World Resource* represents a set of one or more resources we want to protect. It can map to a single or to multiple resources in an application.
|
|
||||||
|
|
||||||
In order to protect it, we need to create the authorization policies and permissions we want to apply. Policies define the conditions that must be satisfied to grant a permission. Where a
|
|
||||||
permission is the link between a resource and the policies(or conditions) we want to enforce when someone wants to access a resource.
|
|
||||||
|
|
||||||
In this example, we have a single policy *Only Special Users Policy*. This policy tells that only the specified users are allowed to access _something_ (we don't know what, yet. That is up to the permission).
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
{{book.project.name}} provides a few link:../policy/overview.html[policy types] that you can start using out-of-the-box. There are policies for ABAC, RBAC, time constraints or even rules written using JavaScript or JBoss Drools.
|
|
||||||
|
|
||||||
The last step when protecting a resource is to define a permission. For that, we have defined a *Hello World Resource Permission* that links the resource we want to protect, _Hello World Resource_, with the
|
|
||||||
policy we want to apply to that resource, _Only Special Users Policy_.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "Hello World Resource Permission",
|
|
||||||
"type": "resource",
|
|
||||||
"config": {
|
|
||||||
"resources": "[\"Hello World Resource\"]",
|
|
||||||
"applyPolicies": "[\"Only Special Users Policy\"]"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
== Obtaining Permissions using the Entitlement API
|
|
||||||
|
|
||||||
In {{book.project.name}}, authorization data is represented by a special security token called a *Requesting Party* Token or *RPT*. This token consists of all the permissions granted
|
|
||||||
to an user as a result of the evaluation of the permissions and authorization policies associated with the resource being requested. In this guide, we'll see how to obtain a RPT using the link:../service/entitlement-api.html[Entitlement API].
|
|
||||||
|
|
||||||
Now that the *hello-world-authz* realm is properly configured with the resources we want to protected and their corresponding permissions and authorization policies, we can ask the server for what an user
|
|
||||||
is entitled to do. In other words, what are the permissions the user has.
|
|
||||||
|
|
||||||
In this case, for the sake of simplicity, our resource server *hello-world-authz-service* is also acting as a client application. Which means that the user allowed this application to access his
|
|
||||||
authorization data and obtain RPTs on his behalf.
|
|
||||||
|
|
||||||
The first step is obtain a EAT as follows:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST
|
|
||||||
-H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpwYXNzd29yZA=="
|
|
||||||
-H "Content-Type: application/x-www-form-urlencoded"
|
|
||||||
-d 'username=alice&password=password&grant_type=password'
|
|
||||||
"http://localhost:8080/auth/realms/hello-world-authz/protocol/openid-connect/token"
|
|
||||||
```
|
|
||||||
|
|
||||||
Here we are using the _Resource Owner Password Credentials Grant Type_ (Direct Access Grant in Keycloak terminology), as defined by OAuth2 specification, to obtain an EAT on behalf of _alice_. As a result, you
|
|
||||||
would get a response from the server as follows:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"access_token": ${EAT},
|
|
||||||
"expires_in": 300,
|
|
||||||
"refresh_expires_in": 1800,
|
|
||||||
"refresh_token": ${refresh_token},
|
|
||||||
"token_type": "bearer",
|
|
||||||
"id_token": ${id_token},
|
|
||||||
"not-before-policy": 0,
|
|
||||||
"session_state": "1ad4d54c-7758-4698-92d3-d57d821f130b"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
Resource Owner Password Credentials Grant Type is only used here for demonstration purposes. In the real world, you'll usually obtain an access token using a more secure grant type such as _authorization_code_.
|
|
||||||
The idea here is demonstrate that you need to obtain an access token with the scope *kc_entitlement* prior to access the Entitlement API and ask for a RPT.
|
|
||||||
|
|
||||||
Finally, the last step is obtain a RPT from the Entitlement API as follows:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X GET \
|
|
||||||
-H "Authorization: Bearer ${EAT}" \
|
|
||||||
"http://localhost:8080/auth/realms/hello-world-authz/authz/entitlement/hello-world-authz-service"
|
|
||||||
```
|
|
||||||
|
|
||||||
As result, you'll get a response from the server as follows:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"rpt": ${RPT}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
By default, {{book.project.name}} issues a RPT consisting of permissions for every single resource protected/managed by the resource server. If you want to limit the permissions to only a specific
|
|
||||||
set of resources you can request a RPT as follows:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-H "Authorization: Bearer %{EAT}" \
|
|
||||||
-d '{
|
|
||||||
"permissions" : [
|
|
||||||
{
|
|
||||||
"resource_set_name" : "Hello World Resource"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}' \
|
|
||||||
"http://localhost:8080/auth/realms/hello-world-authz/authz/entitlement/hello-world-authz-service"
|
|
||||||
```
|
|
||||||
|
|
||||||
Let's see now what happens when the user does not have access to a protected resource at the resource server. For that, let's obtain a new EAT but now using _jdoe_ credentials.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -X POST
|
|
||||||
-H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpwYXNzd29yZA=="
|
|
||||||
-H "Content-Type: application/x-www-form-urlencoded"
|
|
||||||
-d 'username=jdoe&password=password&grant_type=password'
|
|
||||||
"http://localhost:8080/auth/realms/hello-world-authz/protocol/openid-connect/token"
|
|
||||||
```
|
|
||||||
|
|
||||||
Just like we did with _alice_, the server will return an EAT that we can use to obtain a RPT. But _jdoe_ is not supposed to access the protected resource, so the server is going to give you a response as follows:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"error_description": "Authorization denied.",
|
|
||||||
"error": "not_authorized"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The reason for that is that the _Hello World Resource_ is protected by a _Only Special Users Policy_ that says that only _alice_ is allowed to access the resource. You can play around now by changing that policy to
|
|
||||||
include _jdoe_ as a valid user and see the results, or you can create new policies and change the permission to use them.
|
|
||||||
|
|
||||||
== Enforcing Authorization Decisions
|
|
||||||
|
|
||||||
A RPT is basically a https://tools.ietf.org/html/rfc7519[JSON Web Token (JWT)] digitally signed using https://www.rfc-editor.org/rfc/rfc7515.txt[JSON Web Signature (JWS)]. Its lifetime is the same as with the OAuth2 access token (EAT) that was used to obtain it.
|
|
||||||
|
|
||||||
When you decode a RPT you will see something like that:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"resource_set_id": "152251e6-f4cf-4464-8d91-f1b7960fa5fc",
|
|
||||||
"resource_set_name": "Hello World Resource"
|
|
||||||
"scopes": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"accessToken": ${EAT},
|
|
||||||
"jti": "d6109a09-78fd-4998-bf89-95730dfd0892-1464906679405",
|
|
||||||
"exp": 1464906971,
|
|
||||||
"nbf": 0,
|
|
||||||
"iat": 1464906671,
|
|
||||||
"sub": "f1888f4d-5172-4359-be0c-af338505d86c",
|
|
||||||
"typ": "kc_ett",
|
|
||||||
"azp": "hello-world-authz-service"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The *permissions* claim consists of all the permissions granted by the server. There is also a *accessToken* property holding the EAT that was used to issue the RPT.
|
|
||||||
|
|
||||||
The RPT provides everything you need to enforce authorization decisions at the resource server side. That can be easily accomplished by:
|
|
||||||
|
|
||||||
* Validating the RPT signature (based on realm's public key)
|
|
||||||
* Checking the token validity
|
|
||||||
* Decoding the RPT and extracting the permissions
|
|
||||||
* Checking if a request sent to the resource server trying to access a protected resource matches any permissions within the RPT
|
|
||||||
|
|
||||||
You can even use the information within a RPT to protect resources within a page like buttons or any other visual component.
|
|
||||||
|
|
||||||
Another way to validate a RPT and obtain the granted permissions is using the link:../service/protection/token-introspection.html[Token Introspection Endpoint].
|
|
|
@ -4,7 +4,7 @@ Before going further, it is important to understand some terms and concepts intr
|
||||||
|
|
||||||
==== Resource Server
|
==== Resource Server
|
||||||
|
|
||||||
If you are familiar with OAuth2, a Resource Server is the server hosting the protected resources, and which is capable of accepting and responding to protected resource requests.
|
If you are familiar with OAuth2, a Resource Server is the server hosting the protected resources and capable of accepting and responding to protected resource requests.
|
||||||
|
|
||||||
Resource servers usually rely on some kind of information to decide whether access to a protected resource should be granted or not. For RESTful-based resource servers,
|
Resource servers usually rely on some kind of information to decide whether access to a protected resource should be granted or not. For RESTful-based resource servers,
|
||||||
that information is usually obtained from a security token, usually sent as a bearer token on every single request to the server. For web applications that rely on a session to
|
that information is usually obtained from a security token, usually sent as a bearer token on every single request to the server. For web applications that rely on a session to
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
== Creating Resource-based Permissions
|
== Creating Resource-based Permissions
|
||||||
|
|
||||||
A Resource-based permissionsdefines a set of one or more resources to protect using a set of one or more authorization policies.
|
A Resource-based permission defines a set of one or more resources to protect using a set of one or more authorization policies.
|
||||||
|
|
||||||
To create a new permission select the option *Resource-based* in the dropdown located in the right upper corner of the permission listing. In {{book.project.name}}, a resource-based permission consists of the following information:
|
To create a new permission select the option *Resource-based* in the dropdown located in the right upper corner of the permission listing.
|
||||||
|
|
||||||
|
.Add Resource-Based Permission
|
||||||
|
image:../../images/permission/create-resource.png[alt="Add Resource-Based Permission"]
|
||||||
|
|
||||||
|
=== Configuration
|
||||||
|
|
||||||
* *Name*
|
* *Name*
|
||||||
+
|
+
|
||||||
|
@ -19,7 +24,7 @@ Specifies if this permission would be applied to all resources with a given type
|
||||||
+
|
+
|
||||||
** Resource Type
|
** Resource Type
|
||||||
+
|
+
|
||||||
Defines the resource type to protect. When defined, this permissions is going to be evaluated for all resources matching the type
|
Defines the resource type to protect. When defined, this permission is going to be evaluated for all resources matching the type
|
||||||
+
|
+
|
||||||
* *Resources*
|
* *Resources*
|
||||||
+
|
+
|
||||||
|
@ -31,4 +36,4 @@ Defines a set of one or more policies to associate with a permission
|
||||||
|
|
||||||
* *Decision Strategy*
|
* *Decision Strategy*
|
||||||
+
|
+
|
||||||
The decision strategy for this permission
|
The link:decision-strategy.html[Decision Strategy] for this permission
|
|
@ -1,10 +1,15 @@
|
||||||
== Creating Resource-based Permissions
|
== Creating Resource-based Permissions
|
||||||
|
|
||||||
A Scope-based permission defines a set of one or more scopes to protect using a set of one or more authorization policies. Unlike the resource-based permissions, this permission type
|
A Scope-based permission defines a set of one or more scopes to protect using a set of one or more authorization policies. Unlike the resource-based permissions, this permission type
|
||||||
allows you to create permissions not only for a resource, but also for the scopes associated with it, providing more granularity when defining the permissions that rule your resources and the
|
allows you to create permissions not only for a resource, but also for the scopes associated with it, providing more granularity when defining the permissions that govern your resources and the
|
||||||
actions that can be performed on them.
|
actions that can be performed on them.
|
||||||
|
|
||||||
To create a new permission select the option *Scope-based* in the dropdown located in the right upper corner of the permission listing. In {{book.project.name}}, a scope-based permission consists of the following information:
|
To create a new permission select the option *Scope-based* in the dropdown located in the right upper corner of the permission listing.
|
||||||
|
|
||||||
|
.Add Scope-Based Permission
|
||||||
|
image:../../images/permission/create-scope.png[alt="Add Scope-Based Permission"]
|
||||||
|
|
||||||
|
=== Configuration
|
||||||
|
|
||||||
* *Name*
|
* *Name*
|
||||||
+
|
+
|
||||||
|
@ -29,4 +34,4 @@ Defines a set of one or more policies to associate with a permission
|
||||||
|
|
||||||
* *Decision Strategy*
|
* *Decision Strategy*
|
||||||
+
|
+
|
||||||
The decision strategy for this permission
|
The link:decision-strategy.html[Decision Strategy] for this permission
|
|
@ -1,9 +1,16 @@
|
||||||
== Managing Permissions
|
== Managing Permissions
|
||||||
|
|
||||||
|
A permission associates the object being protected and the policies that must be evaluated in order to decide whether access should be granted or not.
|
||||||
|
|
||||||
After creating the resources you want to protect and the policies you want to use to actually protect these resources,
|
After creating the resources you want to protect and the policies you want to use to actually protect these resources,
|
||||||
you can start managing the permissions. For that, clicking on the *Permissions* tab when editing a resource server.
|
you can start managing permissions. For that, clicking on the *Permissions* tab when editing a resource server.
|
||||||
|
|
||||||
|
.Permissions
|
||||||
|
image:../../images/permission/view.png[alt="Permissions"]
|
||||||
|
|
||||||
Permissions can be created to protect two main types of objects:
|
Permissions can be created to protect two main types of objects:
|
||||||
|
|
||||||
* *Resource*
|
* *Resource*
|
||||||
* *Scope*
|
* *Scope*
|
||||||
|
|
||||||
|
To create a permission, select the permission type you want to create from the dropdown located in the right upper corner of the permission listing.
|
|
@ -1,4 +0,0 @@
|
||||||
== Viewing Permissions
|
|
||||||
|
|
||||||
You can view all permissions associated with a resource server by clicking on the *Permission* tab when editing a resource server. On this tab, you'll find the list of permissions
|
|
||||||
and options to create and edit a permission.
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
When designing your policies, you can use an UI to simulate authorization requests and check how your policies are being evaluated.
|
When designing your policies, you can use an UI to simulate authorization requests and check how your policies are being evaluated.
|
||||||
|
|
||||||
The *Policy Evaluation Tool* can be accessed by clicking the *Evaluate* tab when editing a resource server. There you may provide different inputs to simulate real authorization requests and check the effect of your policies.
|
The *Policy Evaluation Tool* can be accessed by clicking the `Evaluate` tab when editing a resource server. There you may provide different inputs to simulate real authorization requests and check the effect of your policies.
|
||||||
|
|
||||||
image:../../images/policy-evaluation-tool.png[alt="Policy Evaluation Tool"]
|
image:../../images/policy-evaluation-tool.png[alt="Policy Evaluation Tool"]
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,20 @@
|
||||||
As mentioned before, Keycloak allows you to build a policy of policies, a concept that we call *Aggregated Policies*. Policy aggregation allows you to reuse existing policies to build more complex ones and keep your permissions even more decoupled from the
|
As mentioned before, Keycloak allows you to build a policy of policies, a concept that we call *Aggregated Policies*. Policy aggregation allows you to reuse existing policies to build more complex ones and keep your permissions even more decoupled from the
|
||||||
policies that are actually evaluated during the processing of authorization requests.
|
policies that are actually evaluated during the processing of authorization requests.
|
||||||
|
|
||||||
|
To create a new policy select the option *Aggregated* in the dropdown located in the right upper corner of the permission listing.
|
||||||
|
|
||||||
|
.Add Aggregated Policy
|
||||||
|
image:../../images/policy/create-aggregated.png[alt="Add Aggregated Policy"]
|
||||||
|
|
||||||
|
Let's suppose you have a resource called _Confidential Resource_ that can be accessed only by users from the _keycloak.org_ domain and within a IP address.
|
||||||
|
You may want to create a single policy with both conditions. However, you may want to reuse the domain part of this policy to apply to permissions despite the network.
|
||||||
|
|
||||||
|
In this case, Keycloak allows you to create separated policies for both domain and network conditions and create another one based on
|
||||||
|
them. You can easily achieve that by using an *Aggregated Policy* that combines other policies and then apply this aggregated policy to any permission you want.
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
When creating aggregated policies, make sure you don't introduce a circular reference/dependency between policies. If you do so, Keycloak will not let you create or update the policy.
|
||||||
|
|
||||||
=== Configuration
|
=== Configuration
|
||||||
|
|
||||||
* *Name*
|
* *Name*
|
||||||
|
@ -24,17 +38,7 @@ The decision strategy for this permission
|
||||||
+
|
+
|
||||||
* *Logic*
|
* *Logic*
|
||||||
+
|
+
|
||||||
The logic of this plicy
|
The link:logic.html[logic] of this policy
|
||||||
|
|
||||||
Let's suppose you have a resource called _Confidential Resource_ that can be accessed only by users with a _confidential_ role and from
|
|
||||||
a well known network. You may want to create a single policy, let's say using Javascript or Drools, with both conditions. However, you may want to reuse the _confidential_ role
|
|
||||||
part of this policy to apply to other permissions despite the network.
|
|
||||||
|
|
||||||
In this case, Keycloak allows you to create separate policies for both _confidential_ role and network conditions and create another one based on
|
|
||||||
them. You can easily achieve that by using a *Aggregated Policy* that combines other policies and then apply this aggregated policy to any permission you want.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
When creating aggregated policies, make sure you don't introduce a circular reference/dependency between policies. If you do so, Keycloak will not let you create or update the policy.
|
|
||||||
|
|
||||||
=== Decision Strategy for Aggregated Policies
|
=== Decision Strategy for Aggregated Policies
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
This type of policy allows you to define conditions for your permissions using Drools. It is one of the _Rule-Based_ policy types
|
This type of policy allows you to define conditions for your permissions using Drools. It is one of the _Rule-Based_ policy types
|
||||||
supported by {{book.project.name}}, providing a lot of flexibility to write any policy based on the link::/policy/evaluation-api.adoc[Evaluation API].
|
supported by {{book.project.name}}, providing a lot of flexibility to write any policy based on the link::/policy/evaluation-api.adoc[Evaluation API].
|
||||||
|
|
||||||
|
To create a new policy select the option *Drools* in the dropdown located in the right upper corner of the permission listing.
|
||||||
|
|
||||||
|
.Add Drools Policy
|
||||||
|
image:../../images/policy/create-drools.png[alt="Add Drools Policy"]
|
||||||
|
|
||||||
=== Configuration
|
=== Configuration
|
||||||
|
|
||||||
* *Name*
|
* *Name*
|
||||||
|
@ -44,7 +49,7 @@ Specifies an interval for scanning for artifact updates
|
||||||
+
|
+
|
||||||
* *Logic*
|
* *Logic*
|
||||||
+
|
+
|
||||||
The logic of this plicy
|
The link:logic.html[logic] of this policy
|
||||||
|
|
||||||
=== Examples
|
=== Examples
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ From this interface, policies can obtain:
|
||||||
* The authenticated *Identity*
|
* The authenticated *Identity*
|
||||||
* Information about the execution context and runtime environment
|
* Information about the execution context and runtime environment
|
||||||
|
|
||||||
Before evaluating policyes, {{book.project.name}} builds an *EvaluationContext* based on:
|
Before evaluating policies, {{book.project.name}} builds an *EvaluationContext* based on:
|
||||||
|
|
||||||
* All claims obtained from the OAuth2 Access Token that was sent along with the authorization request
|
* All claims obtained from the OAuth2 Access Token that was sent along with the authorization request
|
||||||
+
|
+
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
This type of policy allows you to define conditions for your permissions using JavaScript. It is one of the _Rule-Based_ policy types
|
This type of policy allows you to define conditions for your permissions using JavaScript. It is one of the _Rule-Based_ policy types
|
||||||
supported by {{book.project.name}}, providing lot of flexibility to write any policy based on the link::/policy/evaluation-api.adoc[Evaluation API].
|
supported by {{book.project.name}}, providing lot of flexibility to write any policy based on the link::/policy/evaluation-api.adoc[Evaluation API].
|
||||||
|
|
||||||
|
To create a new policy select the option *JavaScript* in the dropdown located in the right upper corner of the permission listing.
|
||||||
|
|
||||||
|
.Add JavaScript Policy
|
||||||
|
image:../../images/policy/create-js.png[alt="Add JavaScript Policy"]
|
||||||
|
|
||||||
=== Configuration
|
=== Configuration
|
||||||
|
|
||||||
* *Name*
|
* *Name*
|
||||||
|
@ -20,7 +25,7 @@ The JavaScript code providing the conditions for this policy
|
||||||
+
|
+
|
||||||
* *Logic*
|
* *Logic*
|
||||||
+
|
+
|
||||||
The logic of this plicy
|
The link:logic.html[logic] of this policy
|
||||||
|
|
||||||
=== Examples
|
=== Examples
|
||||||
|
|
||||||
|
@ -46,5 +51,18 @@ if (identity.hasRole('keycloak_user')) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Or even a mix of different access control mechanisms:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var context = $evaluation.getContext();
|
||||||
|
var identity = context.getIdentity();
|
||||||
|
var attributes = identity.getAttributes();
|
||||||
|
var email = attributes.getValue('email').asString(0);
|
||||||
|
|
||||||
|
if (identity.hasRole('admin') || email.endsWith('@keycloak.org')) {
|
||||||
|
$evaluation.grant();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
When writing your own rules, keep in mind that the *$evaluation* object is just a object implementing *org.keycloak.authorization.policy.evaluation.Evaluation*. For more details about what you can access from this interface,
|
When writing your own rules, keep in mind that the *$evaluation* object is just a object implementing *org.keycloak.authorization.policy.evaluation.Evaluation*. For more details about what you can access from this interface,
|
||||||
please take a look at link::/policy/evaluation-api.adoc[Evaluation API].
|
please take a look at link::/policy/evaluation-api.adoc[Evaluation API].
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
|
|
||||||
As mentioned before, policies define the conditions that must satisfied before granting access to an object.
|
As mentioned before, policies define the conditions that must satisfied before granting access to an object.
|
||||||
|
|
||||||
You can view all policies associated with a resource server by clicking on the *Policy* tab when editing a resource server. On this tab, you'll find the list of policies
|
You can view all policies associated with a resource server by clicking on the *Policy* tab when editing a resource server.
|
||||||
and options to create and edit a policy.
|
|
||||||
|
|
||||||
To create a new policy, select a policy type in the dropdown located in the right upper corner of the policy listing
|
.Policies
|
||||||
|
image:../../images/policy/view.png[alt="Policies"]
|
||||||
|
|
||||||
|
On this tab, you'll find the list of policies and options to create and edit a policy.
|
||||||
|
|
||||||
|
To create a new policy, select a policy type in the dropdown located in the right upper corner of the policy listing.
|
|
@ -3,6 +3,11 @@
|
||||||
This type of policy allows you to define conditions for your permissions where only a set of one or more roles is allowed
|
This type of policy allows you to define conditions for your permissions where only a set of one or more roles is allowed
|
||||||
to access an object.
|
to access an object.
|
||||||
|
|
||||||
|
To create a new policy select the option *Role-Based* in the dropdown located in the right upper corner of the permission listing.
|
||||||
|
|
||||||
|
.Add Role-Based Policy
|
||||||
|
image:../../images/policy/create-role.png[alt="Add Role-Based Policy"]
|
||||||
|
|
||||||
=== Configuration
|
=== Configuration
|
||||||
|
|
||||||
* *Name*
|
* *Name*
|
||||||
|
@ -20,4 +25,4 @@ Specifies which role(s) are allowed by this policy
|
||||||
+
|
+
|
||||||
* *Logic*
|
* *Logic*
|
||||||
+
|
+
|
||||||
The logic of this plicy
|
The link:logic.html[logic] of this policy
|
|
@ -2,6 +2,13 @@
|
||||||
|
|
||||||
This type of policy allows you to define time conditions for your permissions.
|
This type of policy allows you to define time conditions for your permissions.
|
||||||
|
|
||||||
|
To create a new policy select the option *Time* in the dropdown located in the right upper corner of the permission listing.
|
||||||
|
|
||||||
|
.Add Time Policy
|
||||||
|
image:../../images/policy/create-time.png[alt="Add Time Policy"]
|
||||||
|
|
||||||
|
=== Configuration
|
||||||
|
|
||||||
* *Name*
|
* *Name*
|
||||||
+
|
+
|
||||||
A human-readable and unique string describing the policy. We strongly suggest you to use names that are closely related with your business and security requirements, so you
|
A human-readable and unique string describing the policy. We strongly suggest you to use names that are closely related with your business and security requirements, so you
|
||||||
|
@ -22,4 +29,4 @@ Defines the time after which the policy MUST NOT be granted. Only granted if cur
|
||||||
+
|
+
|
||||||
* *Logic*
|
* *Logic*
|
||||||
+
|
+
|
||||||
The logic of this plicy
|
The link:logic.html[logic] of this policy
|
|
@ -3,6 +3,11 @@
|
||||||
This type of policy allows you to define conditions for your permissions where only a set of one or more users is allowed
|
This type of policy allows you to define conditions for your permissions where only a set of one or more users is allowed
|
||||||
to access an object.
|
to access an object.
|
||||||
|
|
||||||
|
To create a new policy select the option *User-Based* in the dropdown located in the right upper corner of the permission listing.
|
||||||
|
|
||||||
|
.Add User-Based Policy
|
||||||
|
image:../../images/policy/create-user.png[alt="Add User-Based Policy"]
|
||||||
|
|
||||||
=== Configuration
|
=== Configuration
|
||||||
|
|
||||||
* *Name*
|
* *Name*
|
||||||
|
@ -20,4 +25,4 @@ Specifies which user(s) are allowed by this policy
|
||||||
+
|
+
|
||||||
* *Logic*
|
* *Logic*
|
||||||
+
|
+
|
||||||
The logic of this plicy
|
The link:logic.html[logic] of this policy
|
|
@ -1,40 +0,0 @@
|
||||||
== Creating Resource Servers
|
|
||||||
|
|
||||||
You can create a resource server by clicking on *Authorization* in the left menu bar. As a result, you'll be redirected
|
|
||||||
to the link::/resource-server/view.adoc[Viewing Resource Servers] page.
|
|
||||||
|
|
||||||
On that page, click on *Create* button in the right upper corner of the table.
|
|
||||||
|
|
||||||
A resource server is basically a *confidential* client application in {{book.project.name}}. To enable a client application as a resource server you need to make sure:
|
|
||||||
|
|
||||||
* Your client application is configured as a *confidential* client.
|
|
||||||
* Your client application is configured with *Service Accounts*.
|
|
||||||
|
|
||||||
=== Creating the Resource Server Manually
|
|
||||||
|
|
||||||
If you have any client application with the characteristics above, they will be available for selection in the *Client* field. Just select a client application and
|
|
||||||
click on *Save*.
|
|
||||||
|
|
||||||
The default values for the other fields should be enough to get you started with. You can also change these options later when editing a resource server.
|
|
||||||
|
|
||||||
=== Importing a JSON file with the Resource Server Configuration
|
|
||||||
|
|
||||||
When creating a resource server, you can also import a JSON file with all the necessary configuration. For that, click on *Select File* and choose a JSON file in your filesystem
|
|
||||||
with the resource server configuration.
|
|
||||||
|
|
||||||
To actually import the file, click on *Upload*. If your configuration file is OK and successfully imported you'll be redirected to the Resource Server Details Page.
|
|
||||||
|
|
||||||
=== Policy Enforcement Modes
|
|
||||||
|
|
||||||
Resource servers provide a *Policy Enforcement Mode* configuration option that dictates how policies are enforced when processing authorization requests sent to the server.
|
|
||||||
|
|
||||||
* *Enforcing*
|
|
||||||
+
|
|
||||||
This is the default mode. Requests are denied by default even when there is no policy associated with a given resource.
|
|
||||||
+
|
|
||||||
* *Permissive*
|
|
||||||
+
|
|
||||||
Requests are allowed even when there is no policy associated with a given resource.
|
|
||||||
* *Disabled*
|
|
||||||
+
|
|
||||||
Completely disables the evaluation of policies and allow access to any resource.
|
|
|
@ -1,6 +0,0 @@
|
||||||
== Defining the uma_protection scope
|
|
||||||
|
|
||||||
In order to integrate with the server, you need to grant the role link:../service/protection-api.html[uma_protection] to the client application that you enabled as a
|
|
||||||
resource server.
|
|
||||||
|
|
||||||
When using the UMA protocol, this scope is also important to allow the resource server to ask the server for permission tickets.
|
|
|
@ -1,6 +0,0 @@
|
||||||
== Exporting Resource Server Configuration
|
|
||||||
|
|
||||||
After creating a resource server, you can export all its configuration to a JSON file. For that, on the *Resource Server Details* page
|
|
||||||
click on *Export* button under *Export Settings*.
|
|
||||||
|
|
||||||
Once exported, you can use the configuration file to recreate your resource server or even share it with others in your team.
|
|
|
@ -1,3 +1,62 @@
|
||||||
== Managing Resource Servers
|
== Managing Resource Servers
|
||||||
|
|
||||||
This section describes the administration functions for managing resource servers.
|
If you are familiar with OAuth2, a Resource Server is the server hosting the protected resources and capable of accepting and responding to protected resource requests.
|
||||||
|
|
||||||
|
Resource servers usually rely on some kind of information to decide whether access to a protected resource should be granted or not. For RESTful-based resource servers,
|
||||||
|
that information is usually obtained from a security token, usually sent as a bearer token on every single request to the server. For web applications that rely on a session to
|
||||||
|
authenticate their users, that information is usually stored into user's session and retrieved from there on every single request.
|
||||||
|
|
||||||
|
You can see the list of resource servers by clicking on the `Authorization` left menu item.
|
||||||
|
|
||||||
|
.Resource Servers
|
||||||
|
image:../../images/resource-server/view.png[alt="Resource Servers"]
|
||||||
|
|
||||||
|
To create a new resource server just click on the `Create` button.
|
||||||
|
|
||||||
|
.Add Resource Server
|
||||||
|
image:../../images/resource-server/create.png[alt="Add Resource Server"]
|
||||||
|
|
||||||
|
Resource servers can be created manually or automatically based on the configuration within a JSON file.
|
||||||
|
|
||||||
|
When manually creating resource servers, the only required field is `Client`. This field allows you to choose an *existing* client applications that can be enabled as a resource server. In order to
|
||||||
|
make a client application available to this field, you need to make sure the client application is configured as follows:
|
||||||
|
|
||||||
|
* It must be a *confidential* client
|
||||||
|
* It must have a *Service Account*
|
||||||
|
|
||||||
|
[NOTE]
|
||||||
|
For more information about how to configure confidential clients and service accounts, please take a look at https://keycloak.gitbooks.io/server-adminstration-guide/[Server Administration Guide].
|
||||||
|
|
||||||
|
However, if you want to import an existing resource server configuration, you can click on the `Import JSON File` button and upload a JSON file holding the resource server configuration.
|
||||||
|
|
||||||
|
In any case, once you fill in the required fields you can click the `Save` button to create the resource server. This will bring you to the `Resource Server Settings` page.
|
||||||
|
|
||||||
|
.Resource Server Settings
|
||||||
|
image:../../images/resource-server/manage.png[alt="Resource Management"]
|
||||||
|
|
||||||
|
Let’s walk through each configuration item on this page.
|
||||||
|
|
||||||
|
* *Policy Enforcement Mode*
|
||||||
|
+
|
||||||
|
Dictates how policies are enforced when processing authorization requests sent to the server.
|
||||||
|
+
|
||||||
|
** *Enforcing*
|
||||||
|
+
|
||||||
|
This is the default mode. Requests are denied by default even when there is no policy associated with a given resource.
|
||||||
|
+
|
||||||
|
** *Permissive*
|
||||||
|
+
|
||||||
|
Requests are allowed even when there is no policy associated with a given resource.
|
||||||
|
** *Disabled*
|
||||||
|
+
|
||||||
|
Completely disables the evaluation of policies and allow access to any resource.
|
||||||
|
+
|
||||||
|
* *Allow Remote Resource Management*
|
||||||
|
+
|
||||||
|
Should resources be managed remotely by the resource server? If false, resources can only be managed from this admin console.
|
||||||
|
|
||||||
|
+
|
||||||
|
* *Export Settings*
|
||||||
|
+
|
||||||
|
In this section you can export all settings to a JSON file. It provides a single `Export` button that you can click to
|
||||||
|
download a JSON file containing every single configuration defined for a resource server: protected resources, scopes, permissions and policies.
|
|
@ -1,14 +0,0 @@
|
||||||
== Defining uma_authorization and kc_entitlement scopes
|
|
||||||
|
|
||||||
In order to allow client applications to obtain authorization tokens from the server, you need to create two roles:
|
|
||||||
|
|
||||||
* *uma_authorization*
|
|
||||||
+
|
|
||||||
This role grants access to client application to ask the server for authorization tokens.
|
|
||||||
|
|
||||||
* *kc_entitlement*
|
|
||||||
+
|
|
||||||
This role grants access to client application to ask the server for entitlements.
|
|
||||||
|
|
||||||
Once they are created for a client application, you must associate these roles with your users. These steps are necessary to tell Keycloak
|
|
||||||
that the client application is allowed to obtain authorization data on behalf of your users.
|
|
|
@ -1,3 +0,0 @@
|
||||||
== Viewing Resource Servers
|
|
||||||
|
|
||||||
You can view all existing resource servers by clicking on *Authorization* in the left menu bar.
|
|
|
@ -3,7 +3,12 @@
|
||||||
Create a resource is pretty straight forward and generic. The main thing you should care about is the granularity of the resources you create. In other words, resources can
|
Create a resource is pretty straight forward and generic. The main thing you should care about is the granularity of the resources you create. In other words, resources can
|
||||||
be created to represent a set of one or more resources and the way you define them is crucial to start managing permissions.
|
be created to represent a set of one or more resources and the way you define them is crucial to start managing permissions.
|
||||||
|
|
||||||
To create a new resource click on *Create* button in the right upper corner of the resource listing. In {{book.project.name}}, a resource defines a small set of information that is common to different types of resources, such as:
|
To create a new resource click on *Create* button in the right upper corner of the resource listing.
|
||||||
|
|
||||||
|
.Add Resource
|
||||||
|
image:../../images/resource/create.png[alt="Add Resource"]
|
||||||
|
|
||||||
|
In {{book.project.name}}, a resource defines a small set of information that is common to different types of resources, such as:
|
||||||
|
|
||||||
* *Name*
|
* *Name*
|
||||||
+
|
+
|
||||||
|
@ -13,7 +18,8 @@ A human-readable and unique string describing a set of one or more resources.
|
||||||
+
|
+
|
||||||
A string uniquely identifying the type of a set of one or more resources. Usually, the type is a URN that can be used to
|
A string uniquely identifying the type of a set of one or more resources. Usually, the type is a URN that can be used to
|
||||||
group different resource instances.
|
group different resource instances.
|
||||||
+
|
|
||||||
|
[[_uri]]
|
||||||
* *URI*
|
* *URI*
|
||||||
+
|
+
|
||||||
A URI that provides the location/address for the resource. For HTTP resources, the URI
|
A URI that provides the location/address for the resource. For HTTP resources, the URI
|
||||||
|
@ -40,5 +46,5 @@ When using the Protection API, resource servers can be implemented to manage res
|
||||||
specify the user identifier to configure a resource as belonging to a specific user.
|
specify the user identifier to configure a resource as belonging to a specific user.
|
||||||
|
|
||||||
[NOTE]
|
[NOTE]
|
||||||
{{book.project.name}} provides to resource server completly control over its resources. In the future, we should be able to
|
{{book.project.name}} provides to resource servers completely control over its resources. In the future, we should be able to
|
||||||
allow users to control their own resources, approve authorization requests and manage permissions. Specially when using the UMA protocol.
|
allow users to control their own resources as well, approve authorization requests and manage permissions. Specially when using the UMA protocol.
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
When you click on *Resource* tab, you'll be redirect to a page listing all the resources associated with a resource server.
|
When you click on *Resource* tab, you'll be redirect to a page listing all the resources associated with a resource server.
|
||||||
|
|
||||||
|
.Resources
|
||||||
|
image:../../images/resource/view.png[alt="Resources"]
|
||||||
|
|
||||||
The resource list provides some very useful information about the protected resources, such as:
|
The resource list provides some very useful information about the protected resources, such as:
|
||||||
|
|
||||||
* Type
|
* Type
|
||||||
|
@ -10,4 +13,4 @@ The resource list provides some very useful information about the protected reso
|
||||||
* Associated scopes, if any
|
* Associated scopes, if any
|
||||||
* Associated permissions
|
* Associated permissions
|
||||||
|
|
||||||
From there you can also create a permission right away by clicking on *Create Permission* button for the resources you want to create the permission.
|
From there you can also create a permission right away by clicking on `Create Permission` button for the resources you want to create the permission.
|
|
@ -20,7 +20,7 @@ curl -X POST
|
||||||
}' "http://localhost:8080/auth/realms/hello-world-authz/authz/authorize"
|
}' "http://localhost:8080/auth/realms/hello-world-authz/authz/authorize"
|
||||||
```
|
```
|
||||||
|
|
||||||
As a result, you will get the follow response from the server:
|
As a result, you will get the following response from the server:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"rpt":"${RPT}"}
|
{"rpt":"${RPT}"}
|
||||||
|
@ -29,20 +29,20 @@ As a result, you will get the follow response from the server:
|
||||||
=== Requesting Party Token or RPT
|
=== Requesting Party Token or RPT
|
||||||
|
|
||||||
A RPT is basically a https://tools.ietf.org/html/rfc7519[JSON Web Token (JWT)] digitally signed using https://www.rfc-editor.org/rfc/rfc7515.txt[JSON Web Signature (JWS)].
|
A RPT is basically a https://tools.ietf.org/html/rfc7519[JSON Web Token (JWT)] digitally signed using https://www.rfc-editor.org/rfc/rfc7515.txt[JSON Web Signature (JWS)].
|
||||||
Its lifetime is the same as with the OAuth2 access token (AAT) that was used to obtain it.
|
The token is built based on the AAT sent by the client during the authorization process.
|
||||||
|
|
||||||
When you decode a RPT you will see something like that:
|
When you decode a RPT you will see something like that:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"authorization": {
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"resource_set_id": "152251e6-f4cf-4464-8d91-f1b7960fa5fc",
|
"resource_set_id": "d2fe9843-6462-4bfc-baba-b5787bb6e0e7",
|
||||||
"resource_set_name": "Hello World Resource"
|
"resource_set_name": "Hello World Resource"
|
||||||
"scopes": []
|
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"accessToken": ${AAT},
|
},
|
||||||
"jti": "d6109a09-78fd-4998-bf89-95730dfd0892-1464906679405",
|
"jti": "d6109a09-78fd-4998-bf89-95730dfd0892-1464906679405",
|
||||||
"exp": 1464906971,
|
"exp": 1464906971,
|
||||||
"nbf": 0,
|
"nbf": 0,
|
||||||
|
@ -53,5 +53,5 @@ When you decode a RPT you will see something like that:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The *permissions* claim consists of all the permissions granted by the server. There is also a *accessToken* property holding the AAT that was used to issue the RPT.
|
From this token you can obtain all permissions granted by the server from the *permissions* claim.
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
An *Authorization API Token* or *AAT* is a special OAuth2 Access Token with the scope *uma_authorization*.
|
An *Authorization API Token* or *AAT* is a special OAuth2 Access Token with the scope *uma_authorization*.
|
||||||
|
|
||||||
Client applications can obtain an AAT from {{book.project.name}} just like any other OAuth2 Access Token. Usually, client applications are going to obtain AATs after the user is successfuly
|
Client applications can obtain an AAT from {{book.project.name}} just like any other OAuth2 Access Token. Usually, client applications obtain AATs after the user is successfully
|
||||||
authenticated in {{book.project.name}}. By default the _authorizaton_code_ grant type is used to authenticate users and issue OAuth2 Access Token to the client application acting on their behalf.
|
authenticated in {{book.project.name}}. By default the _authorizaton_code_ grant type is used to authenticate users and issue an OAuth2 Access Token to the client application acting on their behalf.
|
||||||
|
|
||||||
For demonstrations purposes, the example below uses Resource Owner Password Credentials Grant Type to ask for a AAT.
|
For demonstrations purposes, the example below uses Resource Owner Password Credentials Grant Type to ask for a AAT.
|
||||||
|
|
||||||
|
@ -32,5 +32,5 @@ As a result, you will get the following response from the server:
|
||||||
|
|
||||||
== About the uma_authorization scope
|
== About the uma_authorization scope
|
||||||
|
|
||||||
The *uma_authorization* scope indicates that an user consented access to his authorization data to a client application. You can create this
|
The *uma_authorization* scope can be created just like any other _realm role_. Or even as a _client role_. Once you created it, just grant this role to
|
||||||
scope a _client role_ and map it to your users.
|
the users of your realm.
|
||||||
|
|
|
@ -20,25 +20,29 @@ The client configuration is defined in a JSON file as follows:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"client": {
|
"realm": "hello-world-authz",
|
||||||
"configurationUrl": "http://localhost:8080/auth/realms/${realm_name}/.well-known/uma-configuration",
|
"auth-server-url" : "http://localhost:8080/auth",
|
||||||
"clientId": ${clientId},
|
"resource" : "hello-world-authz-service",
|
||||||
"clientSecret": ${clientSecret}
|
"credentials": {
|
||||||
|
"secret": "secret"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* *configurationUrl*
|
* *realm*
|
||||||
+
|
+
|
||||||
Use this property to specify and URL pointing to the Authorization Services Discovery URL.
|
Name of the realm. This is REQUIRED.
|
||||||
|
|
||||||
|
* *auth-server-url*
|
||||||
+
|
+
|
||||||
* *clientId*
|
The base URL of the Keycloak server. All other Keycloak pages and REST service endpoints are derived from this. It is usually of the form https://host:port/auth. This is REQUIRED.
|
||||||
|
|
||||||
|
* *resource*
|
||||||
+
|
+
|
||||||
The identifier of the client application configured as a resource server
|
The client-id of the application. Each application has a client-id that is used to identify the application. This is REQUIRED.
|
||||||
+
|
|
||||||
* *clientSecret*
|
* *credentials*
|
||||||
+
|
Specify the credentials of the application. This is an object notation where the key is the credential type and the value is the value of the credential type. Currently only secret/password is supported. This is REQUIRED.
|
||||||
The credential of the client application configured as a resource server
|
|
||||||
|
|
||||||
=== Obtaining User Entitlements
|
=== Obtaining User Entitlements
|
||||||
|
|
||||||
|
@ -48,23 +52,12 @@ Here is an example about how to obtain user entitlements:
|
||||||
// create a new instance based on the configuration define at keycloak-authz.json
|
// create a new instance based on the configuration define at keycloak-authz.json
|
||||||
AuthzClient authzClient = AuthzClient.create();
|
AuthzClient authzClient = AuthzClient.create();
|
||||||
|
|
||||||
// query the server for a resource with a given name
|
|
||||||
Set<String> resourceId = authzClient.protection()
|
|
||||||
.resource()
|
|
||||||
.findByFilter("name=Hello World Resource");
|
|
||||||
|
|
||||||
// obtian a Entitlement API Token in order to get access to the Entitlement API.
|
// obtian a Entitlement API Token in order to get access to the Entitlement API.
|
||||||
// this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement
|
// this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement
|
||||||
String eat = getEntitlementAPIToken(authzClient);
|
String eat = getEntitlementAPIToken(authzClient);
|
||||||
|
|
||||||
// create an entitlement request
|
|
||||||
EntitlementRequest request = new EntitlementRequest();
|
|
||||||
|
|
||||||
request.addPermission(new PermissionRequest(resourceId.iterator().next()));
|
|
||||||
|
|
||||||
// send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user
|
// send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user
|
||||||
EntitlementResponse response = authzClient.entitlement(eat)
|
EntitlementResponse response = authzClient.entitlement(eat).getAll("hello-world-authz-service");
|
||||||
.get("hello-world-authz-service", request);
|
|
||||||
String rpt = response.getRpt();
|
String rpt = response.getRpt();
|
||||||
|
|
||||||
System.out.println("You got a RPT: " + rpt);
|
System.out.println("You got a RPT: " + rpt);
|
||||||
|
@ -82,14 +75,19 @@ AuthzClient authzClient = AuthzClient.create();
|
||||||
// this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement
|
// this token is just an access token issued to a client on behalf of an user with a scope kc_entitlement
|
||||||
String eat = getEntitlementAPIToken(authzClient);
|
String eat = getEntitlementAPIToken(authzClient);
|
||||||
|
|
||||||
|
// create an entitlement request
|
||||||
|
EntitlementRequest request = new EntitlementRequest();
|
||||||
|
PermissionRequest permission = new PermissionRequest();
|
||||||
|
|
||||||
|
permission.setResourceSetName("Hello World Resource");
|
||||||
|
|
||||||
|
request.addPermission(permission);
|
||||||
|
|
||||||
// send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user
|
// send the entitlement request to the server in order to obtain a RPT with all permissions granted to the user
|
||||||
EntitlementResponse response = authzClient.entitlement(eat)
|
EntitlementResponse response = authzClient.entitlement(eat).get("hello-world-authz-service", request);
|
||||||
.getAll("hello-world-authz-service", request);
|
|
||||||
String rpt = response.getRpt();
|
String rpt = response.getRpt();
|
||||||
|
|
||||||
System.out.println("You got a RPT: " + rpt);
|
System.out.println("You got a RPT: " + rpt);
|
||||||
|
|
||||||
// now you can use the RPT to access protected resources on the resource server
|
|
||||||
```
|
```
|
||||||
|
|
||||||
=== Creating a Resource Using the Protection API
|
=== Creating a Resource Using the Protection API
|
||||||
|
@ -107,6 +105,11 @@ newResource.setType("urn:hello-world-authz:resources:example");
|
||||||
newResource.addScope(new ScopeRepresentation("urn:hello-world-authz:scopes:view"));
|
newResource.addScope(new ScopeRepresentation("urn:hello-world-authz:scopes:view"));
|
||||||
|
|
||||||
ProtectedResource resourceClient = authzClient.protection().resource();
|
ProtectedResource resourceClient = authzClient.protection().resource();
|
||||||
|
Set<String> existingResource = resourceClient.findByFilter("name=" + newResource.getName());
|
||||||
|
|
||||||
|
if (!existingResource.isEmpty()) {
|
||||||
|
resourceClient.delete(existingResource.iterator().next());
|
||||||
|
}
|
||||||
|
|
||||||
// create the resource on the server
|
// create the resource on the server
|
||||||
RegistrationResponse response = resourceClient.create(newResource);
|
RegistrationResponse response = resourceClient.create(newResource);
|
||||||
|
@ -114,4 +117,4 @@ String resourceId = response.getId();
|
||||||
|
|
||||||
// query the resource using its newly generated id
|
// query the resource using its newly generated id
|
||||||
ResourceRepresentation resource = resourceClient.findById(resourceId).getResourceDescription();
|
ResourceRepresentation resource = resourceClient.findById(resourceId).getResourceDescription();
|
||||||
```java
|
```
|
|
@ -56,7 +56,7 @@ As a result, you'll get a response from the server as follows:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Unlink the GET version, the server is going to respond with a RPT holding the permissions granted during the evaluation of the permissions and authorization policies
|
Unlike the GET version, the server is going to respond with a RPT holding the permissions granted during the evaluation of the permissions and authorization policies
|
||||||
associated with the resources being requested.
|
associated with the resources being requested.
|
||||||
|
|
||||||
When asking for entitlements you can also specify the scopes you want to have access:
|
When asking for entitlements you can also specify the scopes you want to have access:
|
||||||
|
@ -77,22 +77,20 @@ curl -X POST -H "Authorization: Bearer ${EAT}" -d '{
|
||||||
=== Requesting Party Token or RPT
|
=== Requesting Party Token or RPT
|
||||||
|
|
||||||
A RPT is basically a https://tools.ietf.org/html/rfc7519[JSON Web Token (JWT)] digitally signed using https://www.rfc-editor.org/rfc/rfc7515.txt[JSON Web Signature (JWS)].
|
A RPT is basically a https://tools.ietf.org/html/rfc7519[JSON Web Token (JWT)] digitally signed using https://www.rfc-editor.org/rfc/rfc7515.txt[JSON Web Signature (JWS)].
|
||||||
Its lifetime is the same as with the OAuth2 access token (EAT) that was used to obtain it.
|
The token is built based on the EAT sent by the client during the authorization process.
|
||||||
|
|
||||||
When you decode a RPT you may see something like that:
|
When you decode a RPT you will see something like that:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
"authorization": {
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"resource_set_id": "152251e6-f4cf-4464-8d91-f1b7960fa5fc",
|
"resource_set_id": "d2fe9843-6462-4bfc-baba-b5787bb6e0e7",
|
||||||
"resource_set_name": "Hello World Resource"
|
"resource_set_name": "Hello World Resource"
|
||||||
"scopes" : [
|
|
||||||
"urn:my-app.com:scopes:view"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"accessToken": ${EAT},
|
},
|
||||||
"jti": "d6109a09-78fd-4998-bf89-95730dfd0892-1464906679405",
|
"jti": "d6109a09-78fd-4998-bf89-95730dfd0892-1464906679405",
|
||||||
"exp": 1464906971,
|
"exp": 1464906971,
|
||||||
"nbf": 0,
|
"nbf": 0,
|
||||||
|
@ -103,4 +101,4 @@ When you decode a RPT you may see something like that:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The *permissions* claim consists of all the permissions granted by the server. There is also a *accessToken* property holding the AAT that was used to issue the RPT.
|
From this token you can obtain all permissions granted by the server from the *permissions* claim.
|
|
@ -3,7 +3,7 @@
|
||||||
The *Entitlement API* provides a 1-legged protocol for obtaining authorization data from the server, where the authorization data
|
The *Entitlement API* provides a 1-legged protocol for obtaining authorization data from the server, where the authorization data
|
||||||
represents the result of the evaluation of all permissions and authorization policies associated with the resources being requested.
|
represents the result of the evaluation of all permissions and authorization policies associated with the resources being requested.
|
||||||
|
|
||||||
Unlink the_Authorization API, the Entitlement API is not UMA-compliant and don't require permission tickets.
|
Unlike the _Authorization API_, the Entitlement API is not UMA-compliant and don't require permission tickets.
|
||||||
|
|
||||||
The purpose of this API is provide a more lightweight API for obtaining authorization data, where the client in possession of a valid
|
The purpose of this API is provide a more lightweight API for obtaining authorization data, where the client in possession of a valid
|
||||||
OAuth2 Access Token is able to obtain the necessary authorization data on behalf of their users.
|
OAuth2 Access Token is able to obtain the necessary authorization data on behalf of their users.
|
||||||
|
|
|
@ -32,5 +32,5 @@ As a result, you will get the following response from the server:
|
||||||
|
|
||||||
== About the kc_entitlement scope
|
== About the kc_entitlement scope
|
||||||
|
|
||||||
The *kc_entitlement* scope indicates that an user consented access to his authorization data to a client application. You can create this
|
The *kc_entitlement* scope can be created just like any other _realm role_. Or even as a _client role_. Once you created it, just grant this role to
|
||||||
scope a _client role_ and map it to your users.
|
the users of your realm.
|
|
@ -1,3 +1,6 @@
|
||||||
== Authorization Services
|
== Authorization Services
|
||||||
|
|
||||||
Keycloak Authorization Services are based on OAuth2 and User-Managed Access (UMA).
|
Keycloak Authorization Services are based on OAuth2 and User-Managed Access (UMA).
|
||||||
|
|
||||||
|
In this section you'll learn about the different RESTful endpoints that you can interact with in order to enable fine-grained
|
||||||
|
authorization to your application and services.
|
|
@ -4,7 +4,7 @@ The Protection API provides a UMA-compliant set of endpoints providing:
|
||||||
|
|
||||||
* *Resource Registration*
|
* *Resource Registration*
|
||||||
+
|
+
|
||||||
From this endpoint resource servers can manage their resources remotely and enable link::/enforcer/overview.adoc[Policy Enforcers] to query the server for the resources that need to be protected
|
From this endpoint resource servers can manage their resources remotely and enable link::/enforcer/overview.adoc[Policy Enforcers] to query the server for the resources that need protection.
|
||||||
|
|
||||||
* *Permission Registation*
|
* *Permission Registation*
|
||||||
+
|
+
|
||||||
|
|
|
@ -13,6 +13,6 @@ This endpoint provides some registration operations as follows:
|
||||||
* Update resource set description: PUT /resource_set/{_id}
|
* Update resource set description: PUT /resource_set/{_id}
|
||||||
* Delete resource set description: DELETE /resource_set/{_id}
|
* Delete resource set description: DELETE /resource_set/{_id}
|
||||||
* List resource set descriptions: GET /resource_set
|
* List resource set descriptions: GET /resource_set
|
||||||
* List resource set descriptions using a filter: GET /resource_set?filter=filter-name=
|
* List resource set descriptions using a filter: GET /resource_set?filter=${filter}
|
||||||
|
|
||||||
For more details about the contract for each of these operations, please take a look at https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html[UMA Resource Set Registration].
|
For more details about the contract for each of these operations, please take a look at https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html[UMA Resource Set Registration].
|
||||||
|
|
|
@ -34,4 +34,4 @@ In the example above, we are using the *client_credentials* grant type to obtain
|
||||||
== About the uma_protection scope
|
== About the uma_protection scope
|
||||||
|
|
||||||
The *uma_protection* scope can be created just like any other _realm role_. Once you created it, just grant this role to
|
The *uma_protection* scope can be created just like any other _realm role_. Once you created it, just grant this role to
|
||||||
the client application that you want to use as a resource server.
|
the service account of the client application that you want to use as a resource server.
|