KEYCLOAK-5404 Add testing
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
.verified-links
|
||||
|
||||
_book
|
||||
node_modules
|
||||
|
||||
|
|
13
.travis.yml
Normal file
|
@ -0,0 +1,13 @@
|
|||
language: java
|
||||
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
|
||||
env:
|
||||
- PROFILE=community
|
||||
- PROFILE=product
|
||||
|
||||
install: true
|
||||
|
||||
script:
|
||||
- mvn clean install test -D$PROFILE
|
|
@ -14,6 +14,12 @@
|
|||
<packaging>pom</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>api-documentation</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>authorization-services</artifactId>
|
||||
|
@ -59,6 +65,22 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-api_documentation</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.outputDirectory}/api_documentation/</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>../api_documentation/target/generated-docs</directory>
|
||||
<include>**/**</include>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-authorization_services</id>
|
||||
<phase>process-resources</phase>
|
||||
|
@ -204,7 +226,6 @@
|
|||
<configuration>
|
||||
<target>
|
||||
<echo>OUTPUT (no-frames): file://${project.build.outputDirectory}/index.html</echo>
|
||||
<echo>OUTPUT (frames): file://${project.build.outputDirectory}/frames.html</echo>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
|
7
api_documentation/index.adoc
Normal file
|
@ -0,0 +1,7 @@
|
|||
include::topics/templates/document-attributes-community.adoc[]
|
||||
|
||||
:api_documentation:
|
||||
|
||||
= {apidocs_name}
|
||||
|
||||
include::topics/overview.adoc[]
|
37
api_documentation/pom.xml
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>documentation-parent</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<name>API Documentation</name>
|
||||
<artifactId>api-documentation</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>asciidoc-to-html</id>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>echo-output</id>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -1,9 +1,11 @@
|
|||
include::topics/templates/document-attributes-product.adoc[]
|
||||
|
||||
== {project_name} API Documentation
|
||||
|
||||
=== JavaDocs Documentation
|
||||
|
||||
https://access.redhat.com/webassets/avalon/d/red-hat-single-sign-on/version-{project_versionDoc}/javadocs/[Red Hat Single Sign-On Javadocs]
|
||||
{apidocs_javadocs_link}[{apidocs_javadocs_name}]
|
||||
|
||||
=== REST API Documentation
|
||||
=== Admin REST API Documentation
|
||||
|
||||
https://access.redhat.com/webassets/avalon/d/red-hat-single-sign-on/version-{project_versionDoc}/restapi/[Red Hat Single Sign-On REST API Documentation]
|
||||
{apidocs_adminrest_link}[{apidocs_adminrest_name}]
|
||||
|
|
BIN
authorization_services/images/policy/create-client.png
Normal file
After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 76 KiB |
|
@ -2,7 +2,7 @@
|
|||
== Getting Started
|
||||
|
||||
Before you can use this tutorial, you need to complete the installation of {project_name} and create the initial admin user as shown in the link:{gettingstarted_link}[{gettingstarted_name}] tutorial.
|
||||
There is one caveat to this. You have to run a separate {appServer} instance on the same machine as {project_name} Server. This separate instance will run your Java Servlet application. Because of this you will have to run the {project_name} under a different port so that there are no port conflicts when running on the same machine. Use the `jboss.socket.binding.port-offset` system property on the command line. The value of this property is a number that will be added to the base value of every port opened by {project_name} Server.
|
||||
There is one caveat to this. You have to run a separate {appserver_name} instance on the same machine as {project_name} Server. This separate instance will run your Java Servlet application. Because of this you will have to run the {project_name} under a different port so that there are no port conflicts when running on the same machine. Use the `jboss.socket.binding.port-offset` system property on the command line. The value of this property is a number that will be added to the base value of every port opened by {project_name} Server.
|
||||
|
||||
To boot {project_name} Server:
|
||||
|
||||
|
@ -18,7 +18,7 @@ $ .../bin/standalone.sh -Djboss.socket.binding.port-offset=100
|
|||
> ...\bin\standalone.bat -Djboss.socket.binding.port-offset=100
|
||||
----
|
||||
|
||||
For more details about how to install and configure a {appServer}, please follow the steps on the link:{adapterguide_link}[{adapterguide_name}] tutorial.
|
||||
For more details about how to install and configure a {appserver_name}, please follow the steps on the link:{adapterguide_link}[{adapterguide_name}] tutorial.
|
||||
|
||||
After installing and booting both servers you should be able to access {project_name} Admin Console at http://localhost:8180/auth/admin/ and also the {appServer} instance at
|
||||
After installing and booting both servers you should be able to access {project_name} Admin Console at http://localhost:8180/auth/admin/ and also the {appserver_name} instance at
|
||||
http://localhost:8080.
|
||||
|
|
|
@ -93,7 +93,7 @@ Each of these services provides a specific API covering the different steps invo
|
|||
|
||||
===== Protection API
|
||||
|
||||
The *Protection API* is a https://docs.kantarainitiative.org/uma/rec-uma-core.html[UMA-compliant] endpoint providing a small set of operations
|
||||
The *Protection API* is a https://kantarainitiative.org/confluence/display/uma/UMA+1.0+Core+Protocol[UMA-compliant] endpoint providing a small set of operations
|
||||
for resource servers to help them manage their resources and scopes. Only resource servers are allowed to access this API, which also requires a
|
||||
*uma_protection* scope.
|
||||
|
||||
|
@ -117,7 +117,7 @@ For more information, see <<_service_protection_api, Protection API>>.
|
|||
|
||||
===== Authorization API
|
||||
|
||||
The Authorization API is also a https://docs.kantarainitiative.org/uma/rec-uma-core.html[UMA-compliant] endpoint providing a single operation that exchanges an Access Token and <<_overview_terminology_permission_ticket, Permission Ticket>> with a Requesting Party Token (RPT).
|
||||
The Authorization API is also a https://kantarainitiative.org/confluence/display/uma/UMA+1.0+Core+Protocol[UMA-compliant] endpoint providing a single operation that exchanges an Access Token and <<_overview_terminology_permission_ticket, Permission Ticket>> with a Requesting Party Token (RPT).
|
||||
|
||||
The RPT contains all permissions granted to a client and can be used to call a resource server to get access to its protected resources.
|
||||
|
||||
|
|
|
@ -66,11 +66,11 @@ policy providers, and you can create your own policy types to support your speci
|
|||
[[_overview_terminology_permission_ticket]]
|
||||
==== Permission Ticket
|
||||
|
||||
A permission ticket is a special type of token defined by the https://docs.kantarainitiative.org/uma/rec-uma-core.html[OAuth2's User-Managed Access (UMA) Profile] specification that provides an opaque structure whose form is determined by the authorization server. This
|
||||
A permission ticket is a special type of token defined by the https://kantarainitiative.org/confluence/display/uma/UMA+1.0+Core+Protocol[OAuth2's User-Managed Access (UMA) Profile] specification that provides an opaque structure whose form is determined by the authorization server. This
|
||||
structure represents the resources and/or scopes being requested by a client as well as the policies that must be applied to a request for authorization data (requesting party token [RPT]).
|
||||
|
||||
In UMA, permission tickets are crucial to support person-to-person sharing and also person-to-organization sharing. Using permission tickets for authorization workflows enables a range of scenarios from simple to complex, where resource owners and resource servers have complete control over their resources based on fine-grained policies that govern the access to these resources.
|
||||
|
||||
In the UMA workflow, permission tickets are issued by the authorization server to a resource server, which returns the permission ticket to the client trying to access a protected resource. Once the client receives the ticket, it can make a request for an RPT (a final token holding authorization data) by sending the ticket back to the authorization server.
|
||||
|
||||
For more information on permission tickets, see <<_service_authorization_api, Authorization API>> and the https://docs.kantarainitiative.org/uma/rec-uma-core.html[UMA] specification.
|
||||
For more information on permission tickets, see <<_service_authorization_api, Authorization API>> and the https://kantarainitiative.org/confluence/display/uma/UMA+1.0+Core+Protocol[UMA] specification.
|
|
@ -6,7 +6,7 @@ You can use this type of policy to define conditions for your permissions where
|
|||
To create a new client-based policy, select *Client* in the dropdown list in the upper right corner of the policy listing.
|
||||
|
||||
.Add a Client-Based Policy
|
||||
image:{project_images}/policy/create-client.png[alt="Add Client-Based Policy"]
|
||||
image:images/policy/create-client.png[alt="Add Client-Based Policy"]
|
||||
|
||||
==== Configuration
|
||||
|
||||
|
|
|
@ -44,17 +44,7 @@ When processing an authorization request, {project_name} creates an `Evaluation`
|
|||
|
||||
Policies determine this by invoking the `grant()` or `deny()` methods on an `Evaluation` instance. By default, the state of the `Evaluation` instance is denied, which means that your policies must explicitly invoke the `grant()` method to indicate to the policy evaluation engine that permission should be granted.
|
||||
|
||||
ifeval::[{project_community}==true]
|
||||
|
||||
For more information about the Evaluation API see the http://www.keycloak.org/docs/javadocs/index.html[JavaDocs].
|
||||
|
||||
endif::[]
|
||||
|
||||
ifeval::[{project_product}==true]
|
||||
|
||||
For more information about the Evaluation API see the https://access.redhat.com/webassets/avalon/d/red-hat-single-sign-on/version-7.1/javadocs/[JavaDocs].
|
||||
|
||||
endif::[]
|
||||
For more information about the Evaluation API see the {apidocs_link}[JavaDocs].
|
||||
|
||||
==== The Evaluation Context
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
OPTS=$1
|
||||
|
||||
while true; do
|
||||
CHANGED=`inotifywait -r -e modify,move,create,delete authorization_services getting_started securing_apps server_admin server_development server_installation upgrading --format %w`
|
||||
GUIDE=`echo $CHANGED | cut -d '/' -f 1`
|
||||
mvn install -f $GUIDE
|
||||
mvn clean install -f $GUIDE $OPTS
|
||||
done
|
||||
|
|
110
internal_resources/testing.adoc
Normal file
|
@ -0,0 +1,110 @@
|
|||
= Testing
|
||||
|
||||
The `tests` directory contains a testsuite for the documentation. It has a separate test for each guide.
|
||||
|
||||
Currently we are testing the following:
|
||||
|
||||
* Variables - check if there are variables that are not resolved
|
||||
* Includes - check if there are includes where the included file is not found
|
||||
* Images - check if there are images included that are not missing
|
||||
* Internal links - check that all internal links (HTML anchors) point to an valid id
|
||||
* External links - check that all external links work, including HTML anchors
|
||||
|
||||
More details about each test below and what to do if the tests are not working.
|
||||
|
||||
By default the tests run against the locally built documentation. It is also possible to run the test against an
|
||||
externally built and hosted version of the documentation.
|
||||
|
||||
|
||||
== Running the tests
|
||||
|
||||
The tests are ran with a regular build:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
# mvn clean install
|
||||
----
|
||||
|
||||
The tests are also ran when building the product documentation:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
# mvn clean install -Dproduct
|
||||
----
|
||||
|
||||
You can also run the tests from within your IDE, but you have to manually build the guides first.
|
||||
|
||||
== Testing externally built and hosted
|
||||
|
||||
The tests can check externally built and hosted documentation instead of the locally built version. To do this set
|
||||
the `guideBaseUrl` system property to the base URL of the externally hosted documentation.
|
||||
|
||||
For example to test Keycloak documentation run:
|
||||
[source,bash]
|
||||
----
|
||||
mvn -f test test -DguideBaseUrl=http://<HOSTNAME>/docs/{version}
|
||||
----
|
||||
|
||||
Or for example to test RH-SSO documentation run:
|
||||
[source,bash]
|
||||
----
|
||||
mvn -f test test -DguideBaseUrl=https://<HOSTNAME>/documentation/en-us/red_hat_single_sign-on/{version}/html-single/
|
||||
----
|
||||
|
||||
NOTE: `{version}` is replaced with `{project_versionDoc}` from document-attributes.
|
||||
|
||||
== Travis
|
||||
|
||||
Travis will run the tests both for community and product versions of the documentation for every PR and against
|
||||
branches when they are updated.
|
||||
|
||||
|
||||
== Tests
|
||||
|
||||
=== Variables
|
||||
|
||||
Missing variables are detected by looking for '{name}' in the built documentation. There are some cases where we
|
||||
want to have these though, so the following are excluded:
|
||||
|
||||
* If prefixed with `$` (for example `${name}`)
|
||||
* If prefixed with `/` (for example `http://localhost:8080/{realm-name}`)
|
||||
* If listed in `tests/src/resources/ignored-variables`
|
||||
|
||||
=== Includes
|
||||
|
||||
Checking for missing includes is pretty simple as AsciiDoctor will output _Unresolved directive..._ in the generated
|
||||
HTML so we're just searching for that.
|
||||
|
||||
=== Images
|
||||
|
||||
Missing images are checked by searching for `<img ..>` tags and checking that they src attribute refers to an existing
|
||||
image.
|
||||
|
||||
=== Internal links
|
||||
|
||||
Internal links are checked by searching for `<a href="#link">` and checking that the built documentation contains a
|
||||
corresponding `<a id="link">` element.
|
||||
|
||||
=== External links
|
||||
|
||||
External links are checked by searching for `<a href="http://link">` then a connection is opened to verify if the link
|
||||
is valid. If the returned status is 200 we're all good and we also check the returned document to see if it contains
|
||||
a corresponding `<a id="link">` element or `<a name="link">` element.
|
||||
|
||||
Specific URLs can be excluded by adding them to `tests/src/resources/ignored-links`. This is used for example for
|
||||
`http://localhost:8080` which won't be available unless Keycloak is running on the machine where the tests are ran.
|
||||
|
||||
For 302 redirects there is some special handling. The returned `Location` header needs to be added to
|
||||
`tests/src/resources/ignored-link-redirects` otherwise the link will be marked as failed. It's important to verify the
|
||||
redirected link before you add it to this file though as in some cases it redirects to some generic moved or unavailable
|
||||
page.
|
||||
|
||||
To prevent the checking of links being to slow to execute there is a cache file with valid links. This is stored in
|
||||
`.verified-links`. The first time you run the tests you will notice that the checking links is fairly slow, but this
|
||||
will be faster the second time you run it. Entries are purged from the cache after 1 day.
|
||||
|
||||
Links to other guides within the documentation are handled specially. When testing local builds the link to the guide as
|
||||
specified within document-attributes is replaced with the link to the locally built HTML file. When testing externally
|
||||
built and hosted guides the base part of the links are replaced with `guideBaseUrl`. This allows leaving the links
|
||||
in document-attributes to point to the location the documentation will eventually be published to while also allowing
|
||||
testing cross referencing between guides.
|
29
pom.xml
|
@ -18,9 +18,17 @@
|
|||
<version.plugin.resources>3.0.2</version.plugin.resources>
|
||||
<version.plugin.dependency>3.0.1</version.plugin.dependency>
|
||||
<version.plugin.antrun>1.8</version.plugin.antrun>
|
||||
<version.compiler.plugin>3.6.1</version.compiler.plugin>
|
||||
<version.jar.plugin>3.0.2</version.jar.plugin>
|
||||
<version.install.plugin>2.5.2</version.install.plugin>
|
||||
|
||||
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>api_documentation</module>
|
||||
<module>authorization_services</module>
|
||||
<module>getting_started</module>
|
||||
<module>securing_apps</module>
|
||||
|
@ -29,6 +37,7 @@
|
|||
<module>server_installation</module>
|
||||
<module>upgrading</module>
|
||||
<module>aggregation</module>
|
||||
<module>tests</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
|
@ -49,6 +58,26 @@
|
|||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${version.compiler.plugin}</version>
|
||||
<configuration>
|
||||
<source>${maven.compiler.target}</source>
|
||||
<target>${maven.compiler.source}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${version.jar.plugin}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<version>${version.install.plugin}</version>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
[[_fuse_adapter]]
|
||||
==== JBoss Fuse Adapter
|
||||
|
||||
Currently {project_name} supports securing your web applications running inside http://developers.redhat.com/products/fuse/overview/[JBoss Fuse].
|
||||
Currently {project_name} supports securing your web applications running inside https://developers.redhat.com/products/fuse/overview/[JBoss Fuse].
|
||||
|
||||
ifeval::[{project_community}==true]
|
||||
It leverages <<_jetty9_adapter,Jetty 9 adapter>> as {fuseVersion} is bundled with http://eclipse.org/jetty/[Jetty 9.2 server]
|
||||
It leverages <<_jetty9_adapter,Jetty 9 adapter>> as {fuseVersion} is bundled with http://www.eclipse.org/jetty/[Jetty 9.2 server]
|
||||
under the covers and Jetty is used for running various kinds of web applications.
|
||||
endif::[]
|
||||
|
||||
|
|
|
@ -56,20 +56,20 @@ features:install keycloak
|
|||
+
|
||||
Note that the user needs to have the proper realm role to successfully authenticate to Hawtio. The available roles are configured in the `$FUSE_HOME/etc/system.properties` file in `hawtio.roles`.
|
||||
|
||||
====== Securing Hawtio on {hawtioEAPVersion}
|
||||
====== Securing Hawtio on {fuseHawtioEAPVersion}
|
||||
|
||||
To run Hawtio on the {hawtioEAPVersion} server, complete the following steps:
|
||||
To run Hawtio on the {fuseHawtioEAPVersion} server, complete the following steps:
|
||||
|
||||
. Set up {project_name} as described in the previous section, Securing the Hawtio Administration Console. It is assumed that:
|
||||
* you have a {project_name} realm `demo` and client `hawtio-client`
|
||||
* your {project_name} is running on `localhost:8080`
|
||||
* the {hawtioEAPVersion} server with deployed Hawtio will be running on `localhost:8181`. The directory with this server is referred in next steps as `$EAP_HOME`.
|
||||
* the {fuseHawtioEAPVersion} server with deployed Hawtio will be running on `localhost:8181`. The directory with this server is referred in next steps as `$EAP_HOME`.
|
||||
|
||||
. Copy the `{hawtioWARVersion}` archive to the `$EAP_HOME/standalone/configuration` directory. For more details about deploying Hawtio see the https://access.redhat.com/documentation/en-us/red_hat_jboss_fuse/6.3/html-single/deploying_into_a_web_server/eapcamelsubsystem#idm140313338064000[Fuse Hawtio documentation].
|
||||
. Copy the `{fuseHawtioWARVersion}` archive to the `$EAP_HOME/standalone/configuration` directory. For more details about deploying Hawtio see the https://access.redhat.com/documentation/en-us/red_hat_jboss_fuse/6.3/html-single/deploying_into_a_web_server/[Fuse Hawtio documentation].
|
||||
|
||||
. Copy the `keycloak-hawtio.json` and `keycloak-hawtio-client.json` files with the above content to the `$EAP_HOME/standalone/configuration` directory.
|
||||
|
||||
. Install the {project_name} adapter subsystem to your {hawtioEAPVersion} server as described in the <<_jboss_adapter,JBoss adapter documentation>>.
|
||||
. Install the {project_name} adapter subsystem to your {fuseHawtioEAPVersion} server as described in the <<_jboss_adapter,JBoss adapter documentation>>.
|
||||
|
||||
. In the `$EAP_HOME/standalone/configuration/standalone.xml` file configure the system properties as in this example:
|
||||
+
|
||||
|
@ -109,11 +109,11 @@ To run Hawtio on the {hawtioEAPVersion} server, complete the following steps:
|
|||
[source,xml,options="nowrap",subs="attributes+"]
|
||||
----
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak:1.1">
|
||||
<secure-deployment name="{hawtioWARVersion}" />
|
||||
<secure-deployment name="{fuseHawtioWARVersion}" />
|
||||
</subsystem>
|
||||
----
|
||||
|
||||
. Restart the {hawtioEAPVersion} server with Hawtio:
|
||||
. Restart the {fuseHawtioEAPVersion} server with Hawtio:
|
||||
+
|
||||
[source,xml]
|
||||
----
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
[[_multi_tenancy]]
|
||||
==== Multi Tenancy
|
||||
|
||||
Multi Tenancy, in our context, means that a single target application (WAR) can be secured with multiple {project_name} realms. The realms can be located
|
||||
|
|
|
@ -27,7 +27,7 @@ The Keycloak Spring Security adapter takes advantage of Spring Security's flexib
|
|||
|
||||
====== Java Configuration
|
||||
|
||||
Keycloak provides a KeycloakWebSecurityConfigurerAdapter as a convenient base class for creating a http://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/config/annotation/web/WebSecurityConfigurer.html[WebSecurityConfigurer] instance.
|
||||
Keycloak provides a KeycloakWebSecurityConfigurerAdapter as a convenient base class for creating a https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/config/annotation/web/WebSecurityConfigurer.html[WebSecurityConfigurer] instance.
|
||||
The implementation allows customization by overriding methods.
|
||||
While its use is not required, it greatly simplifies your security context configuration.
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ This is what one might look like:
|
|||
----
|
||||
|
||||
Some of these configuration switches may be adapter specific and some are common across all adapters.
|
||||
For Java adapters you can use `$\{...}` enclosure as System property replacement.
|
||||
For example `$\{jboss.server.config.dir}`.
|
||||
For Java adapters you can use `${...}` enclosure as System property replacement.
|
||||
For example `${jboss.server.config.dir}`.
|
||||
|
||||
|
||||
|
|
|
@ -30,5 +30,5 @@ signatureAlgorithm::
|
|||
and defaults to `RSA_SHA256`.
|
||||
signatureCanonicalizationMethod::
|
||||
This is the signature canonicalization method that the IDP expects signed documents to use. This setting is _OPTIONAL_.
|
||||
The default value is `http://www.w3.org/2001/10/xml-exc-c14n#` and should be good for most IDPs.
|
||||
The default value is `\http://www.w3.org/2001/10/xml-exc-c14n#` and should be good for most IDPs.
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ responseBinding::
|
|||
assertionConsumerServiceUrl::
|
||||
URL of the assertion consumer service (ACS) where the IDP login service should send responses to.
|
||||
This setting is _OPTIONAL_. By default it is unset, relying on the configuration in the IdP.
|
||||
When set, it must end in `/saml`, e.g. `http://sp.domain.com/my/endpoint/for/saml`. The value
|
||||
When set, it must end in `/saml`, e.g. `\http://sp.domain.com/my/endpoint/for/saml`. The value
|
||||
of this property is sent in `AssertionConsumerServiceURL` attribute of SAML `AuthnRequest` message.
|
||||
This property is typically accompanied by the `responseBinding` attribute.
|
||||
|
||||
|
|
BIN
server_admin/images/openshift-add-identity-provider.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
server_admin/images/x509-browser-flow.png
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
server_admin/images/x509-configuration.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
server_admin/images/x509-directgrant-execution.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
server_admin/images/x509-directgrant-flow.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
server_admin/images/x509-execution.png
Normal file
After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 85 KiB |
Before Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 68 KiB |
|
@ -3,7 +3,7 @@
|
|||
== User Account Service
|
||||
|
||||
{project_name} has a built-in User Account Service which every user has access to. This service allows users to manage their account,
|
||||
change their credentials, update their profile, and view their login sessions. The URL to this service is `<server-root>/auth/realms/\{realm-name}/account`.
|
||||
change their credentials, update their profile, and view their login sessions. The URL to this service is `<server-root>/auth/realms/{realm-name}/account`.
|
||||
|
||||
.Account Service
|
||||
image:{project_images}/account-service-profile.png[]
|
||||
|
@ -17,7 +17,7 @@ The `Password` left menu item allows the user to change their password.
|
|||
image:{project_images}/account-service-password.png[]
|
||||
|
||||
The `Authenticator` menu item allows the user to set up OTP if they desire. This will only show up if OTP is a valid authentication mechanism for your realm.
|
||||
Users are given directions to install https://fedorahosted.org/freeotp/[FreeOTP] or https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2[Google Authenticator] on their mobile device to be their OTP generator.
|
||||
Users are given directions to install https://freeotp.github.io/[FreeOTP] or https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2[Google Authenticator] on their mobile device to be their OTP generator.
|
||||
The QR code you see in the screen shot can be scanned into the FreeOTP or Google Authenticator mobile application for nice and easy setup.
|
||||
|
||||
.OTP Authenticator
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
=== Dedicated Realm Admin Consoles
|
||||
|
||||
Each realm has a dedicated Admin Console that can be accessed by going to the url `/auth/admin/\{realm-name}/console`.
|
||||
Each realm has a dedicated Admin Console that can be accessed by going to the url `/auth/admin/{realm-name}/console`.
|
||||
Users within that realm can be granted realm management permissions by assigning specific user role mappings.
|
||||
|
||||
Each realm has a built-in client called `realm-management`. You can view this client by going to the
|
||||
|
|
|
@ -131,7 +131,7 @@ For easier testing with Kerberos, we provided some example setups to test.
|
|||
|
||||
===== {project_name} and FreeIPA docker image
|
||||
|
||||
Once you install https://www.docker.com/[docker], you can run docker image with http://www.freeipa.org/[FreeIPA] server installed.
|
||||
Once you install https://www.docker.com/[docker], you can run docker image with http://www.freeipa.org/page/Main_Page[FreeIPA] server installed.
|
||||
FreeIPA provides integrated security solution with MIT Kerberos and 389 LDAP server among other things . The image provides also {project_name}
|
||||
server configured with LDAP Federation provider and enabled SPNEGO/Kerberos authentication against the FreeIPA server.
|
||||
See details https://github.com/mposolda/keycloak-freeipa-docker/blob/master/README.md[here] .
|
||||
|
@ -140,7 +140,7 @@ See details https://github.com/mposolda/keycloak-freeipa-docker/blob/master/READ
|
|||
|
||||
For quick testing and unit tests, we use a very simple http://directory.apache.org/apacheds/[ApacheDS] Kerberos server.
|
||||
You need to build {project_name} from sources and then run the Kerberos server with maven-exec-plugin from our testsuite.
|
||||
See details https://github.com/keycloak/keycloak/blob/master/misc/Testsuite.md#kerberos-server[here] .
|
||||
See details https://github.com/keycloak/keycloak/blob/master/misc/Testsuite.md#user-content-kerberos-server[here] .
|
||||
endif::[]
|
||||
|
||||
==== Credential Delegation
|
||||
|
@ -178,7 +178,7 @@ GSSContext context = gssManager.createContext(serviceName, krb5Oid,
|
|||
ifeval::[{project_community}==true]
|
||||
We have an example, that shows this in detail.
|
||||
It's in `examples/kerberos` in the {project_name} example distribution or demo distribution download.
|
||||
You can also check the example sources directly https://github.com/keycloak/keycloak/blob/master/examples/kerberos[here] .
|
||||
You can also check the example sources directly https://github.com/keycloak/keycloak/tree/master/examples/kerberos[here] .
|
||||
endif::[]
|
||||
|
||||
|
||||
|
@ -186,7 +186,7 @@ Note that you also need to configure `forwardable` kerberos tickets in `krb5.con
|
|||
|
||||
WARNING: Credential delegation has some security implications so only use it if you really need it.
|
||||
It's highly recommended to use it together with HTTPS.
|
||||
See for example http://www.microhowto.info/howto/configure_firefox_to_authenticate_using_spnego_and_kerberos.html#idp27072[this article] for more details.
|
||||
See for example http://www.microhowto.info/howto/configure_firefox_to_authenticate_using_spnego_and_kerberos.html[this article] for more details.
|
||||
|
||||
==== Troubleshooting
|
||||
|
||||
|
|
|
@ -137,15 +137,15 @@ If set to `REQUESTED`, the server will optionally ask for a client certificate.
|
|||
* Using the drop down, select the copied flow, and click on "Add Execution"
|
||||
* Select "X509/Validate User Form" using the drop down and click on "Save"
|
||||
|
||||
image:{project_images}/x509-execution.png[]
|
||||
image:images/x509-execution.png[]
|
||||
|
||||
* Using the up/down arrows, change the order of the "X509/Validate Username Form" by moving it above the "Browser Forms" execution, and set the requirement to "ALTERNATIVE"
|
||||
|
||||
image:{project_images}/x509-browser-flow.png[]
|
||||
image:images/x509-browser-flow.png[]
|
||||
|
||||
#### Configuring X.509 Client Certificate Authentication
|
||||
|
||||
image:{project_images}/x509-configuration.png[]
|
||||
image:images/x509-configuration.png[]
|
||||
|
||||
`User Identity Source`::
|
||||
Defines how to extract the user identity from a client certificate.
|
||||
|
@ -190,11 +190,11 @@ If set, X.509 client certificate authentication will not prompt the user to conf
|
|||
* Delete "Validate Username" and "Password" authenticators,
|
||||
* Click on "Execution" and add "X509/Validate Username" and click on "Save" to add the execution step to the parent flow.
|
||||
|
||||
image:{project_images}/x509-directgrant-execution.png[]
|
||||
image:images/x509-directgrant-execution.png[]
|
||||
|
||||
* Change the `Requirement` to _REQUIRED_.
|
||||
|
||||
image:{project_images}/x509-directgrant-flow.png[]
|
||||
image:images/x509-directgrant-flow.png[]
|
||||
|
||||
* Set up the x509 authentication configuration by following the steps described earlier in the x.509 Browser Flow section.
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ This defines the type of the OIDC client.
|
|||
|
||||
_confidential_::
|
||||
Confidential access type is for server-side clients that need to perform a browser login and require a client secret when they turn an access code into an access token,
|
||||
(see http://tools.ietf.org/html/rfc6749#section-4.1.3[Access Token Request] in the OAuth 2.0 spec for more details). This type should be used for server-side applications.
|
||||
(see https://tools.ietf.org/html/rfc6749#section-4.1.3[Access Token Request] in the OAuth 2.0 spec for more details). This type should be used for server-side applications.
|
||||
|
||||
_public_::
|
||||
Public access type is for client-side clients that need to perform a browser login. With a client-side application there is no way to keep a secret safe. Instead it is very important to restrict access by configuring correct redirect URIs for the client.
|
||||
|
|
|
@ -12,7 +12,7 @@ To use it you must have registered a valid `confidential` Client and you need to
|
|||
In tab `Service Account Roles` you can configure the roles available to the service account retrieved on behalf of this client.
|
||||
Don't forget that you need those roles to be available in Scopes of this client as well (unless you have `Full Scope Allowed` on). As in normal login, roles from access token are the intersection of scopes and the service account roles.
|
||||
|
||||
The REST URL to invoke on is `/auth/realms/\{realm-name}/protocol/openid-connect/token`.
|
||||
The REST URL to invoke on is `/auth/realms/{realm-name}/protocol/openid-connect/token`.
|
||||
Invoking on this URL is a POST request and requires you to post the client credentials.
|
||||
By default, client credentials are represented by clientId and clientSecret of the client in `Authorization: Basic` header, but you can also authenticate the client with a signed JWT assertion or any other custom mechanism for client authentication.
|
||||
You also need to use the parameter `grant_type=client_credentials` as per the OAuth2 specification.
|
||||
|
@ -28,7 +28,7 @@ For example the POST invocation to retrieve a service account can look like this
|
|||
|
||||
grant_type=client_credentials
|
||||
----
|
||||
The response would be this http://tools.ietf.org/html/rfc6749#section-4.4.3[standard JSON document] from the OAuth 2.0 specification.
|
||||
The response would be this https://tools.ietf.org/html/rfc6749#section-4.4.3[standard JSON document] from the OAuth 2.0 specification.
|
||||
|
||||
[source]
|
||||
----
|
||||
|
|
|
@ -14,8 +14,8 @@ You can export/import your database either to:
|
|||
When importing using the directory strategy, note that the files need to follow the naming convention specified below.
|
||||
If you are importing files which were previously exported, the files already follow this convention.
|
||||
|
||||
* {REALM_NAME}-realm.json, such as "acme-roadrunner-affairs-realm.json" for the realm named "acme-roadrunner-affairs"
|
||||
* {REALM_NAME}-users-{INDEX}.json, such as "acme-roadrunner-affairs-users-0.json" for the first users file of the realm named "acme-roadrunner-affairs"
|
||||
* <REALM_NAME>-realm.json, such as "acme-roadrunner-affairs-realm.json" for the realm named "acme-roadrunner-affairs"
|
||||
* <REALM_NAME>-users-<INDEX>.json, such as "acme-roadrunner-affairs-users-0.json" for the first users file of the realm named "acme-roadrunner-affairs"
|
||||
|
||||
If you export to a directory, you can also specify the number of users that will be stored in each JSON file.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[[_default_identity_provider]]
|
||||
[[default_identity_provider]]
|
||||
|
||||
=== Default Identity Provider
|
||||
|
||||
|
|
|
@ -79,5 +79,5 @@ was compromised, it is obviously good to update your keys, but it's also good to
|
|||
|===
|
||||
|
||||
You can also import all this configuration data by providing a URL or file that points to OpenID Provider Metadata (see OIDC Discovery specification).
|
||||
If you are connecting to a {project_name} external IDP, you can import the IDP setttings from the url `<root>/auth/realms/\{realm-name}/.well-known/openid-configuration`.
|
||||
If you are connecting to a {project_name} external IDP, you can import the IDP setttings from the url `<root>/auth/realms/{realm-name}/.well-known/openid-configuration`.
|
||||
This link is a JSON document describing metadata about the IDP.
|
||||
|
|
|
@ -62,7 +62,7 @@ You must define the SAML configuration options as well. They basically describe
|
|||
|
||||
|
||||
You can also import all this configuration data by providing a URL or file that points to the SAML IDP entity descriptor of the external IDP.
|
||||
If you are connecting to a {project_name} external IDP, you can import the IDP setttings from the url `<root>/auth/realms/\{realm-name}/protocol/saml/descriptor`.
|
||||
If you are connecting to a {project_name} external IDP, you can import the IDP setttings from the url `<root>/auth/realms/{realm-name}/protocol/saml/descriptor`.
|
||||
This link is an XML document describing metadata about the IDP.
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ You can't click save yet, as you'll need to obtain a `Client ID` and `Client Sec
|
|||
page is the `Redirect URI`. You'll have to provide that to Google when you register {project_name} as a client there, so
|
||||
copy this URI to your clipboard.
|
||||
|
||||
To enable login with Google you first have to create a project and a client in the https://cloud.google.com/console/project[Google Developer Console].
|
||||
To enable login with Google you first have to create a project and a client in the https://console.cloud.google.com/project[Google Developer Console].
|
||||
Then you need to copy the client id and secret into the {project_name} Admin Console.
|
||||
|
||||
NOTE: Google often changes the look and feel of the Google Developer Console, so these directions might not always be up to date and the
|
||||
|
@ -18,7 +18,7 @@ NOTE: Google often changes the look and feel of the Google Developer Console, so
|
|||
|
||||
Let's see first how to create a project with Google.
|
||||
|
||||
Log in to the link:https://cloud.google.com/console/project[Google Developer Console].
|
||||
Log in to the link:https://console.cloud.google.com/project[Google Developer Console].
|
||||
|
||||
.Google Developer Console
|
||||
image:images/google-developer-console.png[]
|
||||
|
|
|
@ -11,7 +11,7 @@ You can't click save yet, as you'll need to obtain a `Client ID` and `Client Sec
|
|||
page is the `Redirect URI`. You'll have to provide that to LinkedIn when you register {project_name} as a client there, so
|
||||
copy this URI to your clipboard.
|
||||
|
||||
To enable login with LinkedIn you first have to create an application in https://www.linkedin.com/secure/developer[LinkedIn Developer Network].
|
||||
To enable login with LinkedIn you first have to create an application in https://www.linkedin.com/developer/apps[LinkedIn Developer Network].
|
||||
|
||||
NOTE: LinkedIn may change the look and feel of application registration, so these directions may not always be up to date.
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ There are a just a few steps you have to complete to be able to login to OpenShi
|
|||
and select `Openshift` from the `Add provider` drop down list. This will bring you to the `Add identity provider` page.
|
||||
|
||||
.Add Identity Provider
|
||||
image:{project_images}/openshift-add-identity-provider.png[]
|
||||
image:images/openshift-add-identity-provider.png[]
|
||||
|
||||
.Registering OAuth client
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ and select `StackOverflow` from the `Add provider` drop down list. This will br
|
|||
image:{project_images}/stack-overflow-add-identity-provider.png[]
|
||||
|
||||
To enable login with StackOverflow you first have to register an OAuth application on https://stackapps.com/[StackApps].
|
||||
Go to http://stackapps.com/apps/oauth/register[registering your application on Stack Apps] url and login.
|
||||
Go to https://stackapps.com/apps/oauth/register[registering your application on Stack Apps] url and login.
|
||||
|
||||
NOTE: StackOverflow often changes the look and feel of application registration, so these directions might not always be up to date and the
|
||||
configuration steps might be slightly different.
|
||||
|
|
|
@ -11,7 +11,7 @@ You can't click save yet, as you'll need to obtain a `Client ID` and `Client Sec
|
|||
page is the `Redirect URI`. You'll have to provide that to Twitter when you register {project_name} as a client there, so
|
||||
copy this URI to your clipboard.
|
||||
|
||||
To enable login with Twtter you first have to create an application in the https://dev.twitter.com/apps[Twitter Application Management].
|
||||
To enable login with Twtter you first have to create an application in the https://apps.twitter.com[Twitter Application Management].
|
||||
|
||||
.Register Application
|
||||
image:images/twitter-app-register.png[]
|
||||
|
|
|
@ -12,7 +12,7 @@ To retrieve a token for a particular identity provider you need to send a reques
|
|||
----
|
||||
GET /auth/realms/{realm}/broker/{provider_alias}/token HTTP/1.1
|
||||
Host: localhost:8080
|
||||
Authorization: Bearer {keycloak_access_token}
|
||||
Authorization: Bearer <KEYCLOAK ACCESS TOKEN>
|
||||
----
|
||||
|
||||
An application must have authenticated with {project_name} and have received an access token. This access token
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
=== How Does Security Work?
|
||||
|
||||
{project_name} is a separate server that you manage on your network. Applications are configured to point to and
|
||||
be secured by this server. {project_name} uses open protocol standards like link:http://openid.net/connect[Open ID Connect]
|
||||
be secured by this server. {project_name} uses open protocol standards like link:http://openid.net/connect/[Open ID Connect]
|
||||
or link:http://saml.xml.org/saml-specifications[SAML 2.0] to secure
|
||||
your applications. Browser applications redirect a user's browser from the application to the {project_name} authentication
|
||||
server where they enter their credentials. This is important because users are completely isolated from applications and
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[[_realm_keys]]
|
||||
[[realm_keys]]
|
||||
=== Realm Keys
|
||||
|
||||
The authentication protocols that are used by {project_name} require cryptographic signatures and sometimes
|
||||
|
|
|
@ -21,5 +21,5 @@ NOTE: This flow assumes that a `docker login` command has already been performed
|
|||
|
||||
{project_name} really only has one endpoint for all Docker auth v2 requests.
|
||||
|
||||
`http(s)://authserver.host/auth/realms/\{realm-name}/protocol/docker-v2`
|
||||
`http(s)://authserver.host/auth/realms/{realm-name}/protocol/docker-v2`
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ the credentials of the user as well as the id of the client and the client's sec
|
|||
are sent within form parameters. The HTTP response contains
|
||||
_identity_, _access_, and _refresh_ tokens.
|
||||
|
||||
[[_client_credentials_grant]]
|
||||
===== Client Credentials Grant
|
||||
|
||||
This is also used by REST clients, but instead of obtaining a token that works on behalf
|
||||
|
@ -92,14 +93,14 @@ Here's a list of OIDC endpoints that the {project_name} publishes. These URLs a
|
|||
talk OIDC with the auth server. These are all relative URLs and the root of the URL being the HTTP(S) protocol, hostname, and usually path prefixed with
|
||||
_/auth_: i.e. $$https://localhost:8080/auth$$
|
||||
|
||||
/realms/\{realm-name}/protocol/openid-connect/token::
|
||||
/realms/{realm-name}/protocol/openid-connect/token::
|
||||
This is the URL endpoint for obtaining a temporary code in the Authorization Code Flow or for obtaining tokens via the
|
||||
Implicit Flow, Direct Grants, or Client Grants.
|
||||
/realms/\{realm-name}/protocol/openid-connect/auth::
|
||||
/realms/{realm-name}/protocol/openid-connect/auth::
|
||||
This is the URL endpoint for the Authorization Code Flow to turn a temporary code into a token.
|
||||
/realms/{realm-name}/protocol/openid-connect/logout::
|
||||
This is the URL endpoint for performing logouts.
|
||||
/realms/\{realm-name}/protocol/openid-connect/userinfo::
|
||||
/realms/{realm-name}/protocol/openid-connect/userinfo::
|
||||
This is the URL endpoint for the User Info service described in the OIDC specification.
|
||||
|
||||
In all of these replace _\{realm-name}_ with the name of the realm.
|
||||
In all of these replace _{realm-name}_ with the name of the realm.
|
||||
|
|
|
@ -60,7 +60,7 @@ This is used most often for REST or SOAP-based clients.
|
|||
|
||||
{project_name} really only has one endpoint for all SAML requests.
|
||||
|
||||
`http(s)://authserver.host/auth/realms/\{realm-name}/protocol/saml`
|
||||
`http(s)://authserver.host/auth/realms/{realm-name}/protocol/saml`
|
||||
|
||||
All bindings use this endpoint.
|
||||
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
This chapter discusses possible security vulnerabilities any authentication server could have and how {project_name}
|
||||
mitigates those vulnerabilities.
|
||||
A good list of potential vulnerabilities and what security implementations should do to mitigate them can be found in
|
||||
the http://tools.ietf.org/html/rfc6819[OAuth 2.0 Threat Model] document put out by the IETF.
|
||||
the https://tools.ietf.org/html/rfc6819[OAuth 2.0 Threat Model] document put out by the IETF.
|
||||
Many of those vulnerabilities are discussed here.
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ The downside of {project_name} brute force detection is that the server becomes
|
|||
An attacker can simply try to guess passwords for any accounts it knows and these account will be disabled.
|
||||
Eventually we will expand this functionality to take client IP address into account when deciding whether to block a user.
|
||||
|
||||
A better option might be a tool like http://www.fail2ban.org[Fail2Ban]. You can point this service at the {project_name} server's log file.
|
||||
A better option might be a tool like http://www.fail2ban.org/wiki/index.php/Main_Page[Fail2Ban]. You can point this service at the {project_name} server's log file.
|
||||
{project_name} logs every login failure and client IP address that had the failure. Fail2Ban can be used to modify
|
||||
firewalls after it detects an attack to block connections from specific IP addresses.
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ When a user clicks a visible button, they are actually clicking a button (such a
|
|||
An attacker can steal a user's authentication credentials and access their resources.
|
||||
|
||||
By default, every response by {project_name} sets some specific browser headers that can prevent this from happening.
|
||||
Specifically, it sets http://tools.ietf.org/html/rfc7034[X-FRAME_OPTIONS] and http://www.w3.org/TR/CSP/[Content-Security-Policy].
|
||||
Specifically, it sets https://tools.ietf.org/html/rfc7034[X-FRAME_OPTIONS] and http://www.w3.org/TR/CSP/[Content-Security-Policy].
|
||||
You should take a look at the definition of both of these headers as there is a lot of fine-grain browser access you can control.
|
||||
In the admin console you can specify the values these headers will have. Go to the `Realm Settings` left menu item and
|
||||
click the `Security Defenses` tab and make sure you are on the `Headers` sub-tab.
|
||||
|
|
|
@ -7,7 +7,7 @@ Another thing you can do to mitigate leaked access tokens is to shorten their li
|
|||
Short lifespans (minutes) for access tokens for clients and applications to refresh their access tokens after a short amount of time. If an admin detects a leak, they can logout all user sessions to invalidate these refresh tokens or set up a revocation policy. Making sure refresh tokens always stay private to the client and are never transmitted ever is very important as well.
|
||||
|
||||
If an access token or refresh token is compromised, the first thing you should do is go to the admin console and push a not-before revocation policy to all applications. This will enforce that any tokens issued prior to that date are now invalid. Pushing new not-before policy will also ensure that application will be forced to download new public keys from {project_name}, hence it is also useful for the case, when you think that realm signing key was compromised.
|
||||
More info in the <<_realm_keys, keys chapter>>.
|
||||
More info in the <<realm_keys, keys chapter>>.
|
||||
|
||||
You can also disable specific applications, clients, and users if you feel that any one of those entities is completely compromised.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
=== SSSD and FreeIPA Identity Management Integration
|
||||
|
||||
{project_name} also comes with a built-in https://fedorahosted.org/sssd/wiki[SSSD] (System Security Services Daemon) plugin. SSSD is part of the latest Fedora or Red Hat Enterprise Linux and provides access to multiple identity and authentication providers. It provides benefits such as failover and offline support. To see configuration options and for more information see https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System-Level_Authentication_Guide/SSSD.html[the Red Hat Enterprise Linux Identity Management documentation].
|
||||
{project_name} also comes with a built-in https://fedoraproject.org/wiki/Features/SSSD[SSSD] (System Security Services Daemon) plugin. SSSD is part of the latest Fedora or Red Hat Enterprise Linux and provides access to multiple identity and authentication providers. It provides benefits such as failover and offline support. To see configuration options and for more information see https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System-Level_Authentication_Guide/SSSD.html[the Red Hat Enterprise Linux Identity Management documentation].
|
||||
|
||||
SSSD also integrates with the http://www.freeipa.org/page/Main_Page[FreeIPA identity management (IdM)] server, providing authentication and access control. For {project_name}, we benefit from this integration authenticating against http://tldp.org/HOWTO/User-Authentication-HOWTO/x115.html[PAM] services and retrieving user data from SSSD. For more information about using Red Hat Identity Management in Linux environments, see https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Linux_Domain_Identity_Authentication_and_Policy_Guide/index.html[the Red Hat Enterprise Linux Identity Management documentation].
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ image:{project_images}/recaptcha-config.png[]
|
|||
The final step you have to do is to change some default HTTP response headers that {project_name} sets. {project_name}
|
||||
will prevent a website from including any login page within an iframe. This is to prevent clickjacking attacks. You need to
|
||||
authorize Google to use the registration page within an iframe. Go to
|
||||
the `Realm Settings` left menu item and then go to the `Security Defenses` tab. You will need to add `https://www.google.com` to the
|
||||
the `Realm Settings` left menu item and then go to the `Security Defenses` tab. You will need to add `\https://www.google.com` to the
|
||||
values of both the `X-Frame-Options` and `Content-Security-Policy` headers.
|
||||
|
||||
.Authorizing Iframes
|
||||
|
|
|
@ -773,7 +773,7 @@ For more details about {project_name} adapter and OAuth2 flows see link:{adapter
|
|||
Actually Keycloak has 2 builtin implementations of client authentication:
|
||||
|
||||
Traditional authentication with client_id and client_secret::
|
||||
This is default mechanism mentioned in the http://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect] or http://tools.ietf.org/html/rfc6749[OAuth2] specification and Keycloak supports it since it's early days.
|
||||
This is default mechanism mentioned in the http://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect] or https://tools.ietf.org/html/rfc6749[OAuth2] specification and Keycloak supports it since it's early days.
|
||||
The public client needs to include `client_id` parameter with it's ID in the POST request (so it's defacto not authenticated) and the confidential client needs to include `Authorization: Basic` header with the clientId and clientSecret used as username and password.
|
||||
|
||||
Authentication with signed JWT::
|
||||
|
|
|
@ -24,7 +24,7 @@ Object getResource();
|
|||
|
||||
----
|
||||
|
||||
which allows you to return an object, which acts as a https://jax-rs-spec.java.net/[JAX-RS Resource]. For more details, see the Javadoc and our examples.
|
||||
which allows you to return an object, which acts as a https://github.com/jax-rs[JAX-RS Resource]. For more details, see the Javadoc and our examples.
|
||||
There is a very simple example in the example distribution in `providers/rest` and there is a more advanced example in `providers/domain-extension`,
|
||||
which shows how to add an authenticated REST endpoint and other functionalities like <<extensions.adoc#_extensions_spi,Adding your own SPI>>
|
||||
or <<extensions.adoc#_extensions_jpa,Extending datamodel with your own JPA entities>>.
|
||||
|
|
|
@ -22,7 +22,7 @@ To initiate the login, the application must fabricate a URL and redirect the use
|
|||
|
||||
[source,java]
|
||||
----
|
||||
{auth-server-root}/auth/realms/{realm}/broker/{provider}/link?client_id={id}&redirect_uri={uri}&nonce={nonce}&hash={hash}
|
||||
/{auth-server-root}/auth/realms/{realm}/broker/{provider}/link?client_id={id}&redirect_uri={uri}&nonce={nonce}&hash={hash}
|
||||
----
|
||||
|
||||
Here's a description of each path and query param:
|
||||
|
|
|
@ -12,7 +12,7 @@ To retrieve a token for a particular identity provider you need to send a reques
|
|||
----
|
||||
GET /auth/realms/{realm}/broker/{provider_alias}/token HTTP/1.1
|
||||
Host: localhost:8080
|
||||
Authorization: Bearer {keycloak_access_token}
|
||||
Authorization: Bearer <KEYCLOAK ACCESS TOKEN>
|
||||
----
|
||||
|
||||
An application must have authenticated with {project_name} and have received an access token. This access token
|
||||
|
|
|
@ -114,8 +114,8 @@ Then add a new `socket-binding` element to the `socket-binding-group` element:
|
|||
==== Verify Configuration
|
||||
|
||||
You can verify the reverse proxy or load balancer configuration by opening the path `/auth/realms/master/.well-known/openid-configuration`
|
||||
through the reverse proxy. For example if the reverse proxy address is `https://acme.com/` then open the URL
|
||||
`https://acme.com/auth/realms/master/.well-known/openid-configuration`. This will show a JSON document listing a number
|
||||
through the reverse proxy. For example if the reverse proxy address is `\https://acme.com/` then open the URL
|
||||
`\https://acme.com/auth/realms/master/.well-known/openid-configuration`. This will show a JSON document listing a number
|
||||
of endpoints for {project_name}. Make sure the endpoints starts with the address (scheme, domain and port) of your
|
||||
reverse proxy or load balancer. By doing this you make sure that {project_name} is using the correct endpoint.
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ You should answer `What is your first and last name ?` question with the DNS nam
|
|||
For testing purposes, `localhost` should be used.
|
||||
After executing this command, the `keycloak.jks` file will be generated in the same directory as you executed the `keytool` command in.
|
||||
|
||||
If you want a third-party signed certificate, but don't have one, you can obtain one for free at http://cacert.org[cacert.org].
|
||||
If you want a third-party signed certificate, but don't have one, you can obtain one for free at http://www.cacert.org[cacert.org].
|
||||
You'll have to do a little set up first before doing this though.
|
||||
|
||||
The first thing to do is generate a Certificate Request:
|
||||
|
|
9
tests.sh
Normal file
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
FILE=/home/st/dev/keycloak-documentation/server_admin/target/generated-docs/index.html
|
||||
|
||||
function check_missing_refs {
|
||||
|
||||
}
|
||||
|
||||
|
122
tests/pom.xml
Normal file
|
@ -0,0 +1,122 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-jar</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-install-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<parent>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>documentation-parent</artifactId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
<relativePath>../</relativePath>
|
||||
</parent>
|
||||
|
||||
<name>Tests</name>
|
||||
<artifactId>tests</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>api-documentation</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>authorization-services</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>getting-started</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>securing-apps</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>server-admin</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>server-development</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.documentation</groupId>
|
||||
<artifactId>server-installation</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.17</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,117 @@
|
|||
package org.keycloak.documentation.test;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.junit.*;
|
||||
import org.keycloak.documentation.test.utils.DocUtils;
|
||||
import org.keycloak.documentation.test.utils.LinkUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractDocsTest {
|
||||
|
||||
private static final Logger log = Logger.getLogger(AbstractDocsTest.class);
|
||||
|
||||
protected static final Config config = new Config();
|
||||
|
||||
protected DocUtils utils = new DocUtils();
|
||||
protected LinkUtils linkUtils = new LinkUtils(config);
|
||||
|
||||
protected File guideDir;
|
||||
protected static String body;
|
||||
|
||||
@Before
|
||||
public void before() throws IOException {
|
||||
guideDir = config.getGuideDir(getGuideDirName());
|
||||
|
||||
if (body == null) {
|
||||
if (config.isLoadFromFiles()) {
|
||||
File htmlFile = config.getGuideHtmlFile(getGuideDirName());
|
||||
body = utils.readBody(htmlFile);
|
||||
} else {
|
||||
String guideUrl = config.getGuideBaseUrl() + "/" + config.getGuideUrlFragment(getGuideDirName());
|
||||
|
||||
log.info("Loading guide from '" + guideUrl);
|
||||
body = utils.readBody(new URL(guideUrl));
|
||||
}
|
||||
|
||||
body = rewriteLinksToGuides(body);
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
linkUtils.close();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
body = null;
|
||||
}
|
||||
|
||||
public abstract String getGuideDirName();
|
||||
|
||||
@Test
|
||||
public void checkVariables() {
|
||||
List<String> missingVariables = utils.findMissingVariables(body, config.getIgnoredVariables());
|
||||
checkFailures("Variables not found", missingVariables);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkIncludes() {
|
||||
List<String> missingIncludes = utils.findMissingIncludes(body);
|
||||
checkFailures("Includes not found", missingIncludes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkImages() {
|
||||
List<String> failures = linkUtils.findInvalidImages(body, guideDir);
|
||||
checkFailures("Images not found", failures);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkInternalAnchors() {
|
||||
List<String> invalidInternalAnchors = linkUtils.findInvalidInternalAnchors(body);
|
||||
checkFailures("Internal anchors not found", invalidInternalAnchors);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkExternalLinks() throws IOException {
|
||||
List<LinkUtils.InvalidLink> invalidLinks = linkUtils.findInvalidLinks(body, config.getIgnoredLinks(), config.getIgnoredLinkRedirects());
|
||||
if (!invalidLinks.isEmpty()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Invalid links:");
|
||||
for (LinkUtils.InvalidLink l : invalidLinks) {
|
||||
sb.append("\n");
|
||||
sb.append(" * " + l.getLink() + " (" + l.getError() + ")");
|
||||
}
|
||||
Assert.fail(sb.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFailures(String message, List<String> failures) {
|
||||
if (!failures.isEmpty()) {
|
||||
Assert.fail(message + ":\n * " + String.join("\n * ", failures));
|
||||
}
|
||||
}
|
||||
|
||||
private String rewriteLinksToGuides(String body) throws MalformedURLException {
|
||||
if (config.isLoadFromFiles()) {
|
||||
for (Map.Entry<String, String> e : config.getGuideFragmentToDir().entrySet()) {
|
||||
String originalUrl = config.getDocBaseUrl() + "/" + e.getKey() + "/";
|
||||
String replacementUrl = config.getGuideHtmlFile(e.getValue()).toURI().toURL().toString();
|
||||
|
||||
body = body.replace("href=\"" + originalUrl, "href=\"" + replacementUrl);
|
||||
}
|
||||
} else {
|
||||
body = body.replace("href=\"" + config.getDocBaseUrl(), "href=\"" + config.getGuideBaseUrl());
|
||||
}
|
||||
return body;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.documentation.test;
|
||||
|
||||
public class AuthorizationServicesTest extends AbstractDocsTest {
|
||||
|
||||
@Override
|
||||
public String getGuideDirName() {
|
||||
return "authorization_services";
|
||||
}
|
||||
|
||||
}
|
176
tests/src/test/java/org/keycloak/documentation/test/Config.java
Normal file
|
@ -0,0 +1,176 @@
|
|||
package org.keycloak.documentation.test;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Config {
|
||||
|
||||
private static final Logger log = Logger.getLogger(Config.class);
|
||||
|
||||
private File docsRootDir;
|
||||
|
||||
private List<String> ignoredLinkRedirects;
|
||||
private List<String> ignoredVariables;
|
||||
private List<String> ignoredLinks;
|
||||
|
||||
private boolean community;
|
||||
|
||||
private Map<String, String> documentAttributes;
|
||||
private String docBaseUrl;
|
||||
|
||||
private String guideBaseUrl;
|
||||
|
||||
private Map<String, String> guideDirToFragment;
|
||||
private Map<String, String> guideFragmentToDir;
|
||||
|
||||
public Config() {
|
||||
docsRootDir = findDocsRoot();
|
||||
ignoredLinkRedirects = loadConfig("/ignored-link-redirects");
|
||||
ignoredVariables = loadConfig("/ignored-variables");
|
||||
ignoredLinks = loadConfig("/ignored-links");
|
||||
|
||||
community = !System.getProperties().containsKey("product");
|
||||
|
||||
if (community) {
|
||||
guideDirToFragment = loadConfigMap("/guide-url-fragments-community");
|
||||
} else {
|
||||
guideDirToFragment = loadConfigMap("/guide-url-fragments-product");
|
||||
}
|
||||
|
||||
guideFragmentToDir = new HashMap<>();
|
||||
for (Map.Entry<String, String> e : guideDirToFragment.entrySet()) {
|
||||
guideFragmentToDir.put(e.getValue(), e.getKey());
|
||||
}
|
||||
|
||||
documentAttributes = loadDocumentAttributes();
|
||||
docBaseUrl = documentAttributes.get("project_doc_base_url").replace("{project_versionDoc}", documentAttributes.get("project_versionDoc"));
|
||||
|
||||
guideBaseUrl = System.getProperty("guideBaseUrl");
|
||||
if (guideBaseUrl != null) {
|
||||
if (guideBaseUrl.endsWith("/")) {
|
||||
guideBaseUrl = guideBaseUrl.substring(0, guideBaseUrl.length() - 1);
|
||||
}
|
||||
guideBaseUrl = guideBaseUrl.replace("{version}", documentAttributes.get("project_versionDoc"));
|
||||
}
|
||||
|
||||
log.info("Testing " + (community ? "community" : "product") + " documentation");
|
||||
}
|
||||
|
||||
public File getVerifiedLinksCache() {
|
||||
return new File(docsRootDir, ".verified-links");
|
||||
}
|
||||
|
||||
public File getDocsRootDir() {
|
||||
return docsRootDir;
|
||||
}
|
||||
|
||||
public List<String> getIgnoredLinkRedirects() {
|
||||
return ignoredLinkRedirects;
|
||||
}
|
||||
|
||||
public List<String> getIgnoredVariables() {
|
||||
return ignoredVariables;
|
||||
}
|
||||
|
||||
public List<String> getIgnoredLinks() {
|
||||
return ignoredLinks;
|
||||
}
|
||||
|
||||
public boolean isLoadFromFiles() {
|
||||
return guideBaseUrl == null;
|
||||
}
|
||||
|
||||
public boolean isCommunity() {
|
||||
return community;
|
||||
}
|
||||
|
||||
public Map<String, String> getDocumentAttributes() {
|
||||
return documentAttributes;
|
||||
}
|
||||
|
||||
public String getDocBaseUrl() {
|
||||
return docBaseUrl;
|
||||
}
|
||||
|
||||
public String getGuideBaseUrl() {
|
||||
return guideBaseUrl;
|
||||
}
|
||||
|
||||
public String getGuideUrlFragment(String guideDir) {
|
||||
return guideDirToFragment.get(guideDir);
|
||||
}
|
||||
|
||||
public Map<String, String> getGuideFragmentToDir() {
|
||||
return guideFragmentToDir;
|
||||
}
|
||||
|
||||
public File getGuideDir(String guideDirName) {
|
||||
return new File(docsRootDir, guideDirName + "/target/generated-docs");
|
||||
}
|
||||
|
||||
public File getGuideHtmlFile(String guideDirName) {
|
||||
return new File(getGuideDir(guideDirName), community ? "index.html" : "master.html");
|
||||
}
|
||||
|
||||
private File findDocsRoot() {
|
||||
File f = new File("").getAbsoluteFile();
|
||||
if (f.getName().equals("tests")) {
|
||||
f = f.getParentFile();
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
private Map<String, String> loadDocumentAttributes() {
|
||||
try {
|
||||
File f;
|
||||
if (community) {
|
||||
f = new File(docsRootDir, "/topics/templates/document-attributes-community.adoc");
|
||||
} else {
|
||||
f = new File(docsRootDir, "/topics/templates/document-attributes-product.adoc");
|
||||
}
|
||||
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
for (String l : FileUtils.readLines(f, "utf-8")) {
|
||||
if (l.startsWith(":")) {
|
||||
String[] s = l.split(": ");
|
||||
attributes.put(s[0].substring(1).trim(), s[1].trim());
|
||||
}
|
||||
}
|
||||
|
||||
return attributes;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> loadConfig(String resource) {
|
||||
try {
|
||||
return IOUtils.readLines(AbstractDocsTest.class.getResourceAsStream(resource), "utf-8");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, String> loadConfigMap(String resource) {
|
||||
try {
|
||||
List<String> lines = IOUtils.readLines(AbstractDocsTest.class.getResourceAsStream(resource), "utf-8");
|
||||
Map<String, String> m = new HashMap<>();
|
||||
for (String l : lines) {
|
||||
String[] s = l.split("=");
|
||||
m.put(s[0], s[1]);
|
||||
}
|
||||
return m;
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.documentation.test;
|
||||
|
||||
public class GettingStartedTest extends AbstractDocsTest {
|
||||
|
||||
@Override
|
||||
public String getGuideDirName() {
|
||||
return "getting_started";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.documentation.test;
|
||||
|
||||
public class SecuringAppsTest extends AbstractDocsTest {
|
||||
|
||||
@Override
|
||||
public String getGuideDirName() {
|
||||
return "securing_apps";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.documentation.test;
|
||||
|
||||
public class ServerAdminTest extends AbstractDocsTest {
|
||||
|
||||
@Override
|
||||
public String getGuideDirName() {
|
||||
return "server_admin";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.documentation.test;
|
||||
|
||||
public class ServerDeveloperTest extends AbstractDocsTest {
|
||||
|
||||
@Override
|
||||
public String getGuideDirName() {
|
||||
return "server_development";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.documentation.test;
|
||||
|
||||
public class ServerInstallationTest extends AbstractDocsTest {
|
||||
|
||||
@Override
|
||||
public String getGuideDirName() {
|
||||
return "server_installation";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.documentation.test;
|
||||
|
||||
public class UpgradingTest extends AbstractDocsTest {
|
||||
|
||||
@Override
|
||||
public String getGuideDirName() {
|
||||
return "upgrading";
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package org.keycloak.documentation.test.utils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Constants {
|
||||
|
||||
public static final int HTTP_RETRY = 3;
|
||||
public static final int HTTP_CONNECTION_TIMEOUT = 5000;
|
||||
public static final int HTTP_READ_TIMEOUT = 10000;
|
||||
public static final long LINK_CHECK_EXPIRATION = TimeUnit.DAYS.toMillis(1);
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package org.keycloak.documentation.test.utils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class DocUtils {
|
||||
|
||||
public String readBody(File htmlFile) throws IOException {
|
||||
String s = FileUtils.readFileToString(htmlFile, "utf-8");
|
||||
|
||||
Pattern p = Pattern.compile("<body.*?>(.*?)</body>.*?",Pattern.DOTALL);
|
||||
Matcher m = p.matcher(s);
|
||||
|
||||
m.find();
|
||||
return m.group(1);
|
||||
}
|
||||
|
||||
public String readBody(URL url) throws IOException {
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setConnectTimeout(Constants.HTTP_CONNECTION_TIMEOUT);
|
||||
connection.setReadTimeout(Constants.HTTP_READ_TIMEOUT);
|
||||
StringWriter w = new StringWriter();
|
||||
IOUtils.copy(connection.getInputStream(), w, "utf-8");
|
||||
String s = w.toString();
|
||||
|
||||
Pattern p;
|
||||
if (s.contains("<article class=\"rh_docs\">")) {
|
||||
p = Pattern.compile("<article class=\"rh_docs\">(.*?)</article>.*?", Pattern.DOTALL);
|
||||
} else {
|
||||
p = Pattern.compile("<body.*?>(.*?)</body>.*?",Pattern.DOTALL);
|
||||
}
|
||||
|
||||
Matcher m = p.matcher(s);
|
||||
m.find();
|
||||
return m.group(1);
|
||||
|
||||
} finally {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> findMissingVariables(String body, List<String> ignoredVariables) {
|
||||
List<String> missingVariables = new LinkedList<>();
|
||||
Pattern p = Pattern.compile("[^$/=\\s]\\{([^ }]*)}");
|
||||
Matcher m = p.matcher(body);
|
||||
while (m.find()) {
|
||||
String key = m.group(1);
|
||||
if (!key.isEmpty() && !ignoredVariables.contains(key)) {
|
||||
missingVariables.add(key);
|
||||
}
|
||||
}
|
||||
return missingVariables;
|
||||
}
|
||||
|
||||
public List<String> findMissingIncludes(String body) {
|
||||
List<String> missingIncludes = new LinkedList<>();
|
||||
Pattern p = Pattern.compile("Unresolved directive.*");
|
||||
Matcher m = p.matcher(body);
|
||||
if (m.find()) {
|
||||
missingIncludes.add(m.group());
|
||||
}
|
||||
return missingIncludes;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
package org.keycloak.documentation.test.utils;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
public class HttpUtils {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(HttpUtils.class);
|
||||
|
||||
public boolean isValid(String url) {
|
||||
Response response = load(url, false, false);
|
||||
return response.isSuccess();
|
||||
}
|
||||
|
||||
public Response load(String url, boolean readContent, boolean followRedirects) {
|
||||
int retryCount = Constants.HTTP_RETRY;
|
||||
|
||||
while (true) {
|
||||
HttpURLConnection.setFollowRedirects(followRedirects);
|
||||
HttpURLConnection connection = null;
|
||||
Response response = new Response();
|
||||
try {
|
||||
connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
connection.setConnectTimeout(Constants.HTTP_CONNECTION_TIMEOUT);
|
||||
connection.setReadTimeout(Constants.HTTP_READ_TIMEOUT);
|
||||
int status = connection.getResponseCode();
|
||||
if (status == 200) {
|
||||
if (readContent) {
|
||||
StringWriter w = new StringWriter();
|
||||
IOUtils.copy(connection.getInputStream(), w, "utf-8");
|
||||
response.setContent(w.toString());
|
||||
}
|
||||
response.setSuccess(true);
|
||||
} else if (status == 302) {
|
||||
String location = URLDecoder.decode(connection.getHeaderField("Location"), "utf-8");
|
||||
response.setRedirectLocation(location);
|
||||
response.setSuccess(false);
|
||||
} else {
|
||||
response.setError("invalid status code " + status);
|
||||
response.setSuccess(false);
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (Exception e) {
|
||||
response.setError("exception " + e.getMessage());
|
||||
response.setSuccess(false);
|
||||
|
||||
retryCount--;
|
||||
if (retryCount == 0) {
|
||||
return response;
|
||||
}
|
||||
|
||||
logger.info("Retrying " + url);
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Response {
|
||||
|
||||
private boolean success;
|
||||
private String content;
|
||||
private String redirectLocation;
|
||||
private String error;
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
private void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
private void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getRedirectLocation() {
|
||||
return redirectLocation;
|
||||
}
|
||||
|
||||
private void setRedirectLocation(String redirectLocation) {
|
||||
this.redirectLocation = redirectLocation;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
private void setError(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
package org.keycloak.documentation.test.utils;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.keycloak.documentation.test.Config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LinkUtils {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(LinkUtils.class);
|
||||
|
||||
private HttpUtils http = new HttpUtils();
|
||||
private Config config;
|
||||
private File verifiedLinksCacheFile;
|
||||
private Map<String, Long> verifiedLinks;
|
||||
|
||||
public LinkUtils(Config config) {
|
||||
this.config = config;
|
||||
this.verifiedLinksCacheFile = config.getVerifiedLinksCache();
|
||||
this.verifiedLinks = loadCheckedLinksCache();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
saveCheckedLinksCache();
|
||||
}
|
||||
|
||||
public List<String> findInvalidInternalAnchors(String body) {
|
||||
List<String> invalidInternalAnchors = new LinkedList<>();
|
||||
Pattern p = Pattern.compile("<a href=\"([^ \"]*)[^>]*\">");
|
||||
Matcher m = p.matcher(body);
|
||||
while (m.find()) {
|
||||
String link = m.group(1);
|
||||
|
||||
if (link.startsWith("#")) {
|
||||
if (!body.contains("id=\"" + link.substring(1) + "\"")) {
|
||||
invalidInternalAnchors.add(link.substring(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
return invalidInternalAnchors;
|
||||
}
|
||||
|
||||
public List<InvalidLink> findInvalidLinks(String body, List<String> ignoredLinks, List<String> ignoredLinkRedirects) throws IOException {
|
||||
List<InvalidLink> invalidLinks = new LinkedList<>();
|
||||
Pattern p = Pattern.compile("<a href=\"([^ \"]*)[^>]*\">");
|
||||
Matcher m = p.matcher(body);
|
||||
while (m.find()) {
|
||||
String link = m.group(1);
|
||||
|
||||
if (verifyLink(link, ignoredLinks, invalidLinks)) {
|
||||
if (link.startsWith("http")) {
|
||||
String anchor = link.contains("#") ? link.split("#")[1] : null;
|
||||
String error = null;
|
||||
|
||||
HttpUtils.Response response = http.load(link, anchor != null, false);
|
||||
|
||||
if (response.getRedirectLocation() != null) {
|
||||
if (!validRedirect(response.getRedirectLocation(), ignoredLinkRedirects)) {
|
||||
error = "invalid redirect to " + response.getRedirectLocation();
|
||||
}
|
||||
} else if (response.isSuccess() && anchor != null) {
|
||||
if (!(response.getContent().contains("id=\"" + anchor + "\"") || response.getContent().toString().contains("name=\"" + anchor + "\""))) {
|
||||
error = "invalid anchor " + anchor;
|
||||
}
|
||||
} else {
|
||||
error = response.getError();
|
||||
}
|
||||
|
||||
if (error == null) {
|
||||
verifiedLinks.put(link, System.currentTimeMillis());
|
||||
|
||||
logger.debug("Checked link: " + link);
|
||||
} else {
|
||||
logger.debug("Bad link: " + link + " (" + error + ")");
|
||||
invalidLinks.add(new InvalidLink(link, error));
|
||||
}
|
||||
} else if (link.startsWith("file")) {
|
||||
File f = new File(new URL(link).getFile());
|
||||
if (!f.isFile()) {
|
||||
invalidLinks.add(new InvalidLink(link, "local guide not found"));
|
||||
} else {
|
||||
String anchor = link.contains("#") ? link.split("#")[1] : null;
|
||||
if (anchor != null) {
|
||||
String w = FileUtils.readFileToString(f, "utf-8");
|
||||
if (!(w.contains("id=\"" + anchor + "\"") || w.contains("name=\"" + anchor + "\""))) {
|
||||
invalidLinks.add(new InvalidLink(link, "invalid anchor " + anchor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return invalidLinks;
|
||||
}
|
||||
|
||||
public List<String> findInvalidImages(String body, File guideDir) {
|
||||
List<String> missingImages = new LinkedList<>();
|
||||
Pattern p = Pattern.compile("<img src=\"([^ \"]*)[^>]*\"");
|
||||
Matcher m = p.matcher(body);
|
||||
while (m.find()) {
|
||||
String image = m.group(1);
|
||||
if (config.isLoadFromFiles()) {
|
||||
File f = new File(guideDir, image);
|
||||
if (!f.isFile()) {
|
||||
missingImages.add(image);
|
||||
}
|
||||
} else {
|
||||
if (!verifiedLinks.containsKey(image)) {
|
||||
boolean valid = http.isValid(image);
|
||||
if (valid) {
|
||||
logger.debug("Checked image: " + image);
|
||||
|
||||
verifiedLinks.put(image, System.currentTimeMillis());
|
||||
} else {
|
||||
logger.debug("Bad image: " + image);
|
||||
|
||||
missingImages.add(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return missingImages;
|
||||
}
|
||||
|
||||
private boolean verifyLink(String link, List<String> ignoredLinks, List<InvalidLink> invalidLinks) {
|
||||
for (String ignored : ignoredLinks) {
|
||||
if (ignored.endsWith("*") && link.startsWith(ignored.substring(0, ignored.length() - 1))) {
|
||||
return false;
|
||||
} else if (ignored.equals(link)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (verifiedLinks.containsKey(link)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (InvalidLink l : invalidLinks) {
|
||||
if (l.getLink().equals(link)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean validRedirect(String location, List<String> ignoredLinkRedirects) {
|
||||
for (String valid : ignoredLinkRedirects) {
|
||||
if (valid.endsWith("*") && location.startsWith(valid.substring(0, valid.length() - 1))) {
|
||||
return true;
|
||||
} else if (valid.equals(location)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static class InvalidLink {
|
||||
|
||||
private String link;
|
||||
private String error;
|
||||
|
||||
public InvalidLink(String link, String error) {
|
||||
this.link = link;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public String getLink() {
|
||||
return link;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Long> loadCheckedLinksCache() {
|
||||
Map<String, Long> m = new HashMap<>();
|
||||
try {
|
||||
if (verifiedLinksCacheFile.isFile()) {
|
||||
Properties p = new Properties();
|
||||
p.load(new FileInputStream(verifiedLinksCacheFile));
|
||||
for(Map.Entry<Object, Object> e : p.entrySet()) {
|
||||
long checked = Long.valueOf((String) e.getValue());
|
||||
if (checked < System.currentTimeMillis() + Constants.LINK_CHECK_EXPIRATION) {
|
||||
m.put((String) e.getKey(), System.currentTimeMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
private void saveCheckedLinksCache() {
|
||||
try {
|
||||
Properties p = new Properties();
|
||||
for (Map.Entry<String, Long> e : verifiedLinks.entrySet()) {
|
||||
p.put(e.getKey(), Long.toString(e.getValue()));
|
||||
}
|
||||
FileOutputStream os = new FileOutputStream(verifiedLinksCacheFile);
|
||||
p.store(os, null);
|
||||
os.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
8
tests/src/test/resources/guide-url-fragments-community
Normal file
|
@ -0,0 +1,8 @@
|
|||
api_documentation=api_documentation
|
||||
authorization_services=authorization_services
|
||||
getting_started=getting_started
|
||||
securing_apps=securing_apps
|
||||
server_admin=server_admin
|
||||
server_development=server_development
|
||||
server_installation=server_installation
|
||||
upgrading=upgrading
|
8
tests/src/test/resources/guide-url-fragments-product
Normal file
|
@ -0,0 +1,8 @@
|
|||
api_documentation=api-documentation
|
||||
authorization_services=authorization_services_guide
|
||||
getting_started=getting_started_guide
|
||||
securing_apps=securing_applications_and_services_guide
|
||||
server_admin=server_administration_guide
|
||||
server_development=server_developer_guide
|
||||
server_installation=server_installation_and_configuration_guide
|
||||
upgrading=upgrading_guide
|
10
tests/src/test/resources/ignored-link-redirects
Normal file
|
@ -0,0 +1,10 @@
|
|||
https://sso.jboss.org/login*
|
||||
https://accounts.google.com/ServiceLogin*
|
||||
https://github.com/login*
|
||||
https://www.linkedin.com/uas/login*
|
||||
https://apps.dev.microsoft.com/?mkt=en-us&deeplink=/appList/create/sapi&referrer=https://account.live.com
|
||||
/users/login?returnurl=/apps/oauth/register
|
||||
/wiki/spaces/ops4j/pages/5046841/Pax Web Extender - War
|
||||
/wiki/spaces/ops4j/pages/5046828/Pax Web Extender - Whiteboard
|
||||
/wiki/spaces/ops4j/pages/5046828/Pax Web Extender - Whiteboard
|
||||
https://nodejs.org/en/
|
10
tests/src/test/resources/ignored-links
Normal file
|
@ -0,0 +1,10 @@
|
|||
http://localhost:8080*
|
||||
http://localhost:8180*
|
||||
http://localhost:8181*
|
||||
https://myhost.com/myapp/k_jwks
|
||||
https://myhost.com/myapp
|
||||
http://host:port*
|
||||
https://host:port*
|
||||
http://broker-keycloak:8180*
|
||||
https://expressjs.com/
|
||||
https://github.com/keycloak/keycloak/tree/*
|
4
tests/src/test/resources/ignored-variables
Normal file
|
@ -0,0 +1,4 @@
|
|||
realm-name
|
||||
0
|
||||
1
|
||||
id
|
5
tests/src/test/resources/log4j.properties
Normal file
|
@ -0,0 +1,5 @@
|
|||
log4j.rootLogger=debug, keycloak
|
||||
|
||||
log4j.appender.keycloak=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.keycloak.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.keycloak.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
|
|
@ -1,10 +1,10 @@
|
|||
:project_name: Keycloak
|
||||
:project_community: true
|
||||
:project_product: false
|
||||
:project_version: 3.3.0.CR1
|
||||
:project_versionMvn: 3.3.0.CR1
|
||||
:project_versionNpm: 3.3.0-cr.1
|
||||
:project_versionDoc: 3.3
|
||||
:project_version: 3.4.0.CR1
|
||||
:project_versionMvn: 3.4.0.CR1
|
||||
:project_versionNpm: 3.4.0-cr.1
|
||||
:project_versionDoc: 3.4
|
||||
:project_images: keycloak-images
|
||||
:project_doc_base_url: http://www.keycloak.org/docs/{project_versionDoc}
|
||||
:project_dirref: KEYCLOAK_HOME
|
||||
|
@ -20,7 +20,7 @@
|
|||
:adminguide_name: Server Administration Guide
|
||||
:adminguide_link: {project_doc_base_url}/server_admin/
|
||||
:apidocs_name: API Documentation
|
||||
:apidocs_link: {project_doc_base_url}/api-documentation/
|
||||
:apidocs_link: {project_doc_base_url}/api_documentation/
|
||||
:developerguide_name: Server Developer Guide
|
||||
:developerguide_link: {project_doc_base_url}/server_development/
|
||||
:gettingstarted_name: Getting Started Guide
|
||||
|
@ -30,7 +30,12 @@
|
|||
:installguide_name: Server Installation and Configuration Guide
|
||||
:installguide_link: {project_doc_base_url}/server_installation/
|
||||
:installguide_profile_name: Profiles
|
||||
:installguide_profile_link: {installguide_link}/#profiles
|
||||
:installguide_profile_link: {installguide_link}#_profiles
|
||||
|
||||
:apidocs_javadocs_name: JavaDocs Documentation
|
||||
:apidocs_javadocs_link: http://www.keycloak.org/docs-api/{project_versionDoc}/javadocs/
|
||||
:apidocs_adminrest_name: Administration REST API
|
||||
:apidocs_adminrest_link: http://www.keycloak.org/docs-api/{project_versionDoc}/rest-api/
|
||||
|
||||
:appserver_name: WildFly
|
||||
:appserver_dirref: WILDFLY_HOME
|
||||
|
@ -54,6 +59,8 @@
|
|||
:appserver_loadbalancer_name: {appserver_name} {appserver_version} Documentation
|
||||
|
||||
:fuseVersion: JBoss Fuse 6.3.0 Rollup 5
|
||||
:fuseHawtioEAPVersion: JBoss EAP 6.4
|
||||
:fuseHawtioWARVersion: hawtio-wildfly-1.4.0.redhat-630254.war
|
||||
|
||||
:subsystem_undertow_xml_urn: urn:jboss:domain:undertow:3.1
|
||||
:saml_adapter_xsd_urn: http://www.keycloak.org/schema/keycloak_saml_adapter_1_9.xsd
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
:project_versionNpm: 3.3.0.Final-redhat1
|
||||
:project_versionDoc: 7.2
|
||||
:project_images: rhsso-images
|
||||
:project_doc_base_url: https://doc-stage.usersys.redhat.com/documentation/en-us/red_hat_single_sign-on
|
||||
:project_doc_base_url: https://access.redhat.com/documentation/en-us/red_hat_single_sign-on/{project_versionDoc}/html-single
|
||||
:project_dirref: RHSSO_HOME
|
||||
|
||||
:quickstartRepo_name: {project_name} Quickstarts Repository
|
||||
|
@ -14,48 +14,56 @@
|
|||
:quickstartRepo_dir: redhat-sso-quickstarts
|
||||
|
||||
:authorizationguide_name: Authorization Services Guide
|
||||
:authorizationguide_link: {project_doc_base_url}/{project_versionDoc}/html-single/authorization_services_guide/
|
||||
:authorizationguide_link: {project_doc_base_url}/authorization_services_guide/
|
||||
:adapterguide_name: Securing Applications and Services Guide
|
||||
:adapterguide_link: {project_doc_base_url}/{project_versionDoc}/html-single/securing_applications_and_services_guide/
|
||||
:adapterguide_link: {project_doc_base_url}/securing_applications_and_services_guide/
|
||||
:adminguide_name: Server Administration Guide
|
||||
:adminguide_link: {project_doc_base_url}/{project_versionDoc}/html-single/server_administration_guide/
|
||||
:adminguide_link: {project_doc_base_url}/server_administration_guide/
|
||||
:apidocs_name: API Documentation
|
||||
:apidocs_link: {project_doc_base_url}/{project_versionDoc}/html-single/api-documentation/
|
||||
:apidocs_link: {project_doc_base_url}/api-documentation/
|
||||
:developerguide_name: Server Developer Guide
|
||||
:developerguide_link: {project_doc_base_url}/{project_versionDoc}/html-single/server_developer_guide/
|
||||
:developerguide_link: {project_doc_base_url}/server_developer_guide/
|
||||
:gettingstarted_name: Getting Started Guide
|
||||
:gettingstarted_link: {project_doc_base_url}/{project_versionDoc}/html-single/getting_started_guide/
|
||||
:gettingstarted_link: {project_doc_base_url}/getting_started_guide/
|
||||
:upgradingguide_name: Upgrading Guide
|
||||
:upgradingguide_link: {project_doc_base_url}/{project_versionDoc}/html-single/upgrading_guide/
|
||||
:upgradingguide_link: {project_doc_base_url}/upgrading_guide/
|
||||
:installguide_name: Server Installation and Configuration Guide
|
||||
:installguide_link: {project_doc_base_url}/{project_versionDoc}/html-single/server_installation_and_configuration_guide/
|
||||
:installguide_link: {project_doc_base_url}/server_installation_and_configuration_guide/
|
||||
:installguide_profile_name: Profiles
|
||||
:installguide_profile_link: {installguide_link}/#profiles
|
||||
:installguide_profile_link: {installguide_link}#_profiles
|
||||
|
||||
:apidocs_javadocs_name: JavaDocs Documentation
|
||||
:apidocs_javadocs_link: https://access.redhat.com/webassets/avalon/d/red-hat-single-sign-on/version-{project_versionDoc}/javadocs/
|
||||
:apidocs_adminrest_name: Administration REST API
|
||||
:apidocs_adminrest_link: https://access.redhat.com/webassets/avalon/d/red-hat-single-sign-on/version-{project_versionDoc}/restapi/
|
||||
|
||||
:appserver_name: JBoss EAP
|
||||
:appserver_dirref: EAP_HOME
|
||||
:appserver_version: 7.0
|
||||
:appserver_doc_base_url: https://access.redhat.com/documentation/en/red-hat-jboss-enterprise-application-platform/{appserver_version}
|
||||
|
||||
:appserver_doc_base_url: https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/{appserver_version}
|
||||
:appserver_socket_name: JBoss EAP Configuration Guide
|
||||
:appserver_socket_link: {appserver_doc_base_url}/html-single/configuration-guide/#network_and_port_configuration
|
||||
:appserver_socket_link: {appserver_doc_base_url}/html-single/configuration_guide/#network_and_port_configuration
|
||||
:appserver_jgroups_name: JBoss EAP Configuration Guide
|
||||
:appserver_jgroups_link: {appserver_doc_base_url}/html-single/configuration-guide/#cluster_communication_jgroups
|
||||
:appserver_jgroups_link: {appserver_doc_base_url}/html-single/configuration_guide/#cluster_communication_jgroups
|
||||
:appserver_jpa_name: JBoss EAP Development Guide
|
||||
:appserver_jpa_link: {appserver_doc_base_url}/html-single/development-guide/#hibernate
|
||||
:appserver_jpa_link: {appserver_doc_base_url}/html-single/development_guide/#java_persistence_api_jpa
|
||||
:appserver_network_name: JBoss EAP Configuration Guide
|
||||
:appserver_network_link: {appserver_doc_base_url}/html-single/configuration-guide/#network_and_port_configuration
|
||||
:appserver_network_link: {appserver_doc_base_url}/html-single/configuration_guide/#network_and_port_configuration
|
||||
:appserver_datasource_name: JBoss EAP Configuration Guide
|
||||
:appserver_datasource_link: {appserver_doc_base_url}/html-single/configuration-guide/#datasource_management
|
||||
:appserver_datasource_link: {appserver_doc_base_url}/html-single/configuration_guide/#datasource_management
|
||||
:appserver_caching_name: JBoss EAP Configuration Guide
|
||||
:appserver_caching_link: {appserver_doc_base_url}/html-single/configuration-guide/#infinispan
|
||||
:appserver_caching_link: {appserver_doc_base_url}/html-single/configuration_guide/#infinispan
|
||||
:appserver_admindoc_name: JBoss EAP Configuration Guide
|
||||
:appserver_admindoc_link: {appserver_doc_base_url}/html-single/configuration-guide/configuration-guide
|
||||
:appserver_admindoc_link: {appserver_doc_base_url}/html-single/configuration_guide/
|
||||
:appserver_loadbalancer_name: JBoss EAP Configuration Guide
|
||||
:appserver_loadbalancer_link: {appserver_doc_base_url}/html-single/configuration-guide/#configuring_high_availability
|
||||
:appserver_loadbalancer_link: {appserver_doc_base_url}/html-single/configuration_guide/#configuring_high_availability
|
||||
:appserver_managementcli_link: https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.0/html-single/configuration_guide/#management_cli_overview
|
||||
:appserver_managementconsole_link: https://access.redhat.com/documentation/en-us/red_hat_jboss_enterprise_application_platform/7.0/html-single/configuration_guide/#management_console_overview
|
||||
|
||||
:fuseVersion: JBoss Fuse 6.3.0 Rollup 5
|
||||
:fuseHawtioEAPVersion: JBoss EAP 6.4
|
||||
:fuseHawtioWARVersion: hawtio-wildfly-1.4.0.redhat-630254.war
|
||||
|
||||
:subsystem_undertow_xml_urn: urn:jboss:domain:undertow:3.1
|
||||
:saml_adapter_xsd_urn: http://www.keycloak.org/schema/keycloak_saml_adapter_1_9.xsd
|
|
@ -188,7 +188,7 @@ These libraries are now only added to your deployment if you have Keycloak authe
|
|||
|
||||
==== Client Registration service endpoints moved
|
||||
|
||||
The Client Registration service endpoints have been moved from `{realm}/clients` to `{realm}/clients-registrations`.
|
||||
The Client Registration service endpoints have been moved from `{realm-name}/clients` to `{realm-name}/clients-registrations`.
|
||||
|
||||
==== Session state parameter in authentication response renamed
|
||||
|
||||
|
@ -225,17 +225,16 @@ Keycloak SAML SP Client Adapter now requires a specific endpoint, `/saml` to be
|
|||
|
||||
In previous releases we shipped with a default admin user with a default password, this has now been removed.
|
||||
If you are doing a new installation of 1.8 you will have to create an admin user as a first step.
|
||||
This can be done easily by following the steps in <<_create_admin_user,Admin User>>.
|
||||
|
||||
==== OAuth2 Token Introspection
|
||||
|
||||
In order to add more compliance with OAuth2 specification, we added a new endpoint for token introspection.
|
||||
The new endpoint can reached at `/realms/{realm}/protocols/openid-connect/token/introspect` and it is solely based on `RFC-7662.`
|
||||
The new endpoint can reached at `/realms/{realm-name}/protocols/openid-connect/token/introspect` and it is solely based on `RFC-7662.`
|
||||
|
||||
The `/realms/{realm}/protocols/openid-connect/validate` endpoint is now deprecated and we strongly recommend you to move to the new introspection endpoint as soon as possible.
|
||||
The `/realms/{realm-name}/protocols/openid-connect/validate` endpoint is now deprecated and we strongly recommend you to move to the new introspection endpoint as soon as possible.
|
||||
The reason for this change is that RFC-7662 provides a more standard and secure introspection endpoint.
|
||||
|
||||
The new token introspection URL can now be obtained from OpenID Connect Provider's configuration at `/realms/{realm}/.well-known/openid-configuration`.
|
||||
The new token introspection URL can now be obtained from OpenID Connect Provider's configuration at `/realms/{realm-name}/.well-known/openid-configuration`.
|
||||
There you will find a claim with name `token_introspection_endpoint` within the response.
|
||||
Only `confidential clients` are allowed to invoke the new endpoint, where these clients will be usually acting as a resource server and looking for token metadata in order to perform local authorization checks.
|
||||
|
||||
|
@ -370,7 +369,7 @@ There are now 3 separate adapter downloads for WildFly, JBoss EAP and JBoss AS7:
|
|||
Make sure you grab the correct one.
|
||||
|
||||
You also need to update standalone.xml as the extension module and subsystem definition has changed.
|
||||
See <<_jboss_adapter_installation,Adapter Installation>> for details.
|
||||
See link:{adapterguide_link}[{adapterguide_name] for details.
|
||||
|
||||
=== Migrating from 1.2.0.Beta1 to 1.2.0.RC1
|
||||
|
||||
|
@ -414,7 +413,7 @@ If you're using another library (or RSATokenVerifier) you need to make the corre
|
|||
|
||||
To comply with OpenID Connect specification the authentication and token endpoints have been changed to having a single authentication endpoint and a single token endpoint.
|
||||
As per-spec `response_type` and `grant_type` parameters are used to select the required flow.
|
||||
The old endpoints (`/realms/{realm}/protocols/openid-connect/login`, `/realms/{realm}/protocols/openid-connect/grants/access`, `/realms/{realm}/protocols/openid-connect/refresh`, `/realms/{realm}/protocols/openid-connect/access/codes)` are now deprecated and will be removed in a future version.
|
||||
The old endpoints (`/realms/{realm-name}/protocols/openid-connect/login`, `/realms/{realm-name}/protocols/openid-connect/grants/access`, `/realms/{realm-name}/protocols/openid-connect/refresh`, `/realms/{realm-name}/protocols/openid-connect/access/codes)` are now deprecated and will be removed in a future version.
|
||||
|
||||
==== Theme changes
|
||||
|
||||
|
@ -425,20 +424,19 @@ This change was done to make it easier to have group the different types for the
|
|||
|
||||
If you deployed themes as a JAR in the past you had to create a custom theme loader which required Java code.
|
||||
This has been simplified to only requiring a plain text file (`META-INF/keycloak-themes.json`) to describe the themes included in a JAR.
|
||||
See the <<_themes,themes>> section in the docs for more information.
|
||||
|
||||
==== Claims changes
|
||||
|
||||
Previously there was `Claims` tab in admin console for application and OAuth clients.
|
||||
This was used to configure which attributes should go into access token for particular application/client.
|
||||
This was removed and replaced with <<_mappers,Protocol mappers>>, which are more flexible.
|
||||
This was removed and replaced with protocol mappers which are more flexible.
|
||||
|
||||
You don't need to care about migration of database from previous version.
|
||||
We did migration scripts for both RDBMS and Mongo, which should ensure that claims configured for particular application/client will be converted into corresponding protocol mappers (Still it's safer to backup DB before migrating to newer version though). Same applies for exported JSON representation from previous version.
|
||||
|
||||
==== Social migration to identity brokering
|
||||
|
||||
We refactored social providers SPI and replaced it with <<_identity_broker,identity brokering SPI>>, which is more flexible.
|
||||
We refactored social providers SPI and replaced it with Identity Brokering SPI, which is more flexible.
|
||||
The `Social` tab in admin console is renamed to `Identity Provider` tab.
|
||||
|
||||
Again you don't need to care about migration of database from previous version similarly like for Claims/protocol mappers.
|
||||
|
@ -449,12 +447,6 @@ You can first go to the Keycloak admin console and copy Redirect URI from the pa
|
|||
Then you can simply paste this as allowed Redirect URI to the admin console of 3rd party provider (IE.
|
||||
Facebook admin console).
|
||||
|
||||
=== Migrating from 1.1.0.Beta2 to 1.1.0.Final
|
||||
|
||||
* WEB-INF/lib
|
||||
+`standalone/configuration/providers`<<_providers,+providers>>
|
||||
|
||||
|
||||
=== Migrating from 1.1.0.Beta1 to 1.1.0.Beta2
|
||||
|
||||
* Adapters are now a separate download. They are not included in appliance and war distribution. We have too many now and the distro
|
||||
|
|