KEYCLOAK-5404 Add testing

This commit is contained in:
Stian Thorgersen 2017-09-05 09:49:24 +02:00
parent c843927c75
commit ed7a90a8a5
92 changed files with 1303 additions and 128 deletions

2
.gitignore vendored
View file

@ -1,3 +1,5 @@
.verified-links
_book
node_modules

13
.travis.yml Normal file
View file

@ -0,0 +1,13 @@
language: java
jdk:
- oraclejdk8
env:
- PROFILE=community
- PROFILE=product
install: true
script:
- mvn clean install test -D$PROFILE

View file

@ -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>

View 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
View 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>

View file

@ -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}]

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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

View 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
View file

@ -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>

View file

@ -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::[]

View file

@ -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]
----

View file

@ -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

View file

@ -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.

View file

@ -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}`.

View file

@ -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.

View file

@ -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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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]
----

View file

@ -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.

View file

@ -1,4 +1,4 @@
[[_default_identity_provider]]
[[default_identity_provider]]
=== Default Identity Provider

View file

@ -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.

View file

@ -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.

View file

@ -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[]

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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[]

View file

@ -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

View file

@ -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

View file

@ -1,4 +1,4 @@
[[_realm_keys]]
[[realm_keys]]
=== Realm Keys
The authentication protocols that are used by {project_name} require cryptographic signatures and sometimes

View file

@ -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`

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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].

View file

@ -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

View file

@ -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::

View file

@ -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>>.

View file

@ -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:

View file

@ -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

View file

@ -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.

View file

@ -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
View 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
View 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>

View file

@ -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;
}
}

View file

@ -0,0 +1,10 @@
package org.keycloak.documentation.test;
public class AuthorizationServicesTest extends AbstractDocsTest {
@Override
public String getGuideDirName() {
return "authorization_services";
}
}

View 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);
}
}
}

View file

@ -0,0 +1,10 @@
package org.keycloak.documentation.test;
public class GettingStartedTest extends AbstractDocsTest {
@Override
public String getGuideDirName() {
return "getting_started";
}
}

View file

@ -0,0 +1,10 @@
package org.keycloak.documentation.test;
public class SecuringAppsTest extends AbstractDocsTest {
@Override
public String getGuideDirName() {
return "securing_apps";
}
}

View file

@ -0,0 +1,10 @@
package org.keycloak.documentation.test;
public class ServerAdminTest extends AbstractDocsTest {
@Override
public String getGuideDirName() {
return "server_admin";
}
}

View file

@ -0,0 +1,10 @@
package org.keycloak.documentation.test;
public class ServerDeveloperTest extends AbstractDocsTest {
@Override
public String getGuideDirName() {
return "server_development";
}
}

View file

@ -0,0 +1,10 @@
package org.keycloak.documentation.test;
public class ServerInstallationTest extends AbstractDocsTest {
@Override
public String getGuideDirName() {
return "server_installation";
}
}

View file

@ -0,0 +1,10 @@
package org.keycloak.documentation.test;
public class UpgradingTest extends AbstractDocsTest {
@Override
public String getGuideDirName() {
return "upgrading";
}
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View 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

View 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

View 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/

View 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/*

View file

@ -0,0 +1,4 @@
realm-name
0
1
id

View 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

View file

@ -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

View file

@ -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

View file

@ -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