merge
This commit is contained in:
commit
c5303df4fc
28 changed files with 936 additions and 308 deletions
|
@ -109,6 +109,10 @@
|
|||
</module-def>
|
||||
<module-def name="org.picketlink.federation.bindings">
|
||||
<maven-resource group="org.picketlink" artifact="picketlink-wildfly-common"/>
|
||||
<maven-resource group="org.picketlink" artifact="picketlink-tomcat-common"/>
|
||||
<maven-resource group="org.picketlink" artifact="picketlink-tomcat5-single"/>
|
||||
<maven-resource group="org.picketlink" artifact="picketlink-jbas-common"/>
|
||||
<maven-resource group="org.picketlink" artifact="picketlink-jbas7-single"/>
|
||||
</module-def>
|
||||
<module-def name="org.picketlink.idm">
|
||||
<maven-resource group="org.picketlink" artifact="picketlink-idm-impl"/>
|
||||
|
|
|
@ -81,10 +81,31 @@
|
|||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-federation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Wildfly federation binding -->
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-wildfly-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- AS7/EAP6 federation binding -->
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-tomcat-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-tomcat5-single</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-jbas-common</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-jbas7-single</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-idm-simple-schema</artifactId>
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
<module name="javax.servlet.api" />
|
||||
<module name="org.jboss.common-core" />
|
||||
<module name="org.jboss.logging" />
|
||||
<module name="org.wildfly.extension.undertow" />
|
||||
<module name="org.jboss.security.xacml" />
|
||||
<module name="org.picketbox" />
|
||||
<module name="javax.xml.ws.api" />
|
||||
|
@ -43,11 +42,18 @@
|
|||
<module name="org.jboss.ws.api" />
|
||||
<module name="org.jboss.ws.spi" />
|
||||
<module name="org.apache.cxf" />
|
||||
<module name="io.undertow.core" />
|
||||
<module name="io.undertow.servlet" />
|
||||
<module name="org.picketlink.common" />
|
||||
<module name="org.picketlink.config" />
|
||||
<module name="org.picketlink.federation" />
|
||||
|
||||
<!-- Wildfly dependencies -->
|
||||
<module name="org.wildfly.extension.undertow" optional="true" />
|
||||
<module name="io.undertow.core" optional="true" />
|
||||
<module name="io.undertow.servlet" optional="true" />
|
||||
|
||||
<!-- AS7/EAP6 dependencies -->
|
||||
<module name="org.jboss.as.web" optional="true"/>
|
||||
<module name="org.jboss.modules" optional="true"/>
|
||||
</dependencies>
|
||||
|
||||
</module>
|
|
@ -29,7 +29,7 @@
|
|||
<module name="javax.servlet.api" />
|
||||
<module name="org.jboss.common-core" />
|
||||
<module name="org.jboss.logging" />
|
||||
<module name="org.jboss.as.web-common" />
|
||||
<module name="org.jboss.as.web-common" optional="true" />
|
||||
<module name="org.jboss.security.xacml" />
|
||||
<module name="org.picketbox" />
|
||||
<module name="javax.xml.ws.api" />
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<!ENTITY Jetty9Adapter SYSTEM "modules/jetty9-adapter.xml">
|
||||
<!ENTITY Jetty8Adapter SYSTEM "modules/jetty8-adapter.xml">
|
||||
<!ENTITY FuseAdapter SYSTEM "modules/fuse-adapter.xml">
|
||||
<!ENTITY SpringBootAdapter SYSTEM "modules/spring-boot-adapter.xml">
|
||||
<!ENTITY InstalledApplications SYSTEM "modules/installed-applications.xml">
|
||||
<!ENTITY Logout SYSTEM "modules/logout.xml">
|
||||
<!ENTITY SAML SYSTEM "modules/saml.xml">
|
||||
|
@ -95,6 +96,7 @@ This one is short
|
|||
&Jetty8Adapter;
|
||||
&FuseAdapter;
|
||||
&JavascriptAdapter;
|
||||
&SpringBootAdapter;
|
||||
&InstalledApplications;
|
||||
&Logout;
|
||||
&MultiTenancy;
|
||||
|
|
72
docbook/reference/en/en-US/modules/spring-boot-adapter.xml
Executable file
72
docbook/reference/en/en-US/modules/spring-boot-adapter.xml
Executable file
|
@ -0,0 +1,72 @@
|
|||
<section id="spring-boot-adapter">
|
||||
<title>Spring Boot Adapter</title>
|
||||
<para>
|
||||
To be able to secure Spring Boot apps you must add the Keycloak Spring Boot adapter
|
||||
JAR to your app. You then have to provide some extra configuration via normal Spring
|
||||
Boot configuration (<literal>application.properties</literal>). Let's go over these steps.
|
||||
</para>
|
||||
<section id="spring-boot-adapter-installation">
|
||||
<title>Adapter Installation</title>
|
||||
<para>
|
||||
The Keycloak Spring Boot adapter takes advantage of Spring Boot's autoconfiguration so all
|
||||
you need to do is add the Keycloak Spring Boot adapter JAR to your project. Depending on
|
||||
what container you are using with Spring Boot, you also need to add the appropriate
|
||||
Keycloak container adapter. If you are using Maven, add the following to your pom.xml (using
|
||||
Tomcat as an example):
|
||||
</para>
|
||||
<para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-spring-boot-adapter</artifactId>
|
||||
<version>1.2.0.Beta1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-tomcat8-adapter</artifactId>
|
||||
<version>${keycloak.version}</version>
|
||||
</dependency>
|
||||
]]>
|
||||
</programlisting>
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="spring-boot-adapter-configuration">
|
||||
<title>Required Spring Boot Adapter Configuration</title>
|
||||
<para>
|
||||
This section describes how to configure your Spring Boot app to use Keycloak.
|
||||
</para>
|
||||
<para>
|
||||
Instead of a <literal>keycloak.json</literal> file, you configure the realm for the Spring
|
||||
Boot Keycloak adapter via the normal Spring Boot configuration. For example:
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
keycloak.realm = demorealm
|
||||
keycloak.realmKey = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCLCWYuxXmsmfV+Xc9Ik8QET8lD4wuHrJAXbbutS2O/eMjQQLNK7QDX/k/XhOkhxP0YBEypqeXeGaeQJjCxDhFjJXQuewUEMlmSja3IpoJ9/hFn4Cns4m7NGO+rtvnfnwgVfsEOS5EmZhRddp+40KBPPJfTH6Vgu6KjQwuFPj6DTwIDAQAB
|
||||
keycloak.auth-server-url = http://127.0.0.1:8080/auth
|
||||
keycloak.ssl-required = external
|
||||
keycloak.resource = demoapp
|
||||
keycloak.credentials.secret = 11111111-1111-1111-1111-111111111111
|
||||
keycloak.use-resource-role-mappings = true
|
||||
]]>
|
||||
</programlisting>
|
||||
<para>
|
||||
You also need to specify the J2EE security config that would normally go in the <literal>web.xml</literal>.
|
||||
Here's an example configuration:
|
||||
</para>
|
||||
<programlisting>
|
||||
<![CDATA[
|
||||
keycloak.securityConstraints[0].securityCollections[0].name = insecure stuff
|
||||
keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = admin
|
||||
keycloak.securityConstraints[0].securityCollections[0].authRoles[0] = user
|
||||
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /insecure
|
||||
|
||||
keycloak.securityConstraints[0].securityCollections[1].name = admin stuff
|
||||
keycloak.securityConstraints[0].securityCollections[1].authRoles[0] = admin
|
||||
keycloak.securityConstraints[0].securityCollections[1].patterns[0] = /admin
|
||||
]]>
|
||||
</programlisting>
|
||||
</section>
|
||||
</section>
|
|
@ -68,3 +68,8 @@ Fuse
|
|||
----
|
||||
|
||||
This is set of demo applications, showing how to secure your own web applications running inside OSGI environment in JBoss Fuse or Apache Karaf. Fore more information look at `fuse/README.md`
|
||||
|
||||
SAML
|
||||
----
|
||||
|
||||
This is set of demo applications, showing how to secure your own SAML web applications. Fore more information look at `saml/README.md`
|
||||
|
|
|
@ -80,18 +80,24 @@ features:uninstall pax-http-whiteboard
|
|||
features:uninstall pax-http
|
||||
features:uninstall pax-jetty
|
||||
features:removeurl mvn:org.ops4j.pax.web/pax-web-features/3.0.6/xml/features
|
||||
features:addurl mvn:org.ops4j.pax.web/pax-web-features/3.1.2/xml/features
|
||||
```
|
||||
|
||||
Now restart fuse (use `osgi:shutdown` command and start it again from command line. You can ignore startup messages after restart
|
||||
as pax-web is not installed at the moment. Then run those commands:
|
||||
|
||||
```
|
||||
features:addurl mvn:org.ops4j.pax.web/pax-web-features/3.1.2/xml/features
|
||||
features:addurl mvn:org.keycloak/keycloak-osgi-features/1.1.0.Final/xml/features
|
||||
features:addurl mvn:org.keycloak.example.demo/keycloak-fuse-example-features/1.1.0.Final/xml/features
|
||||
|
||||
features:install keycloak-pax-web-upgrade
|
||||
features:install pax-http-whiteboard/3.1.2
|
||||
features:install pax-war/3.1.2
|
||||
```
|
||||
|
||||
features:uninstall cxf
|
||||
features:install cxf
|
||||
Now it's recommended to restart again. After the start, you shouldn't see any error messages, which indicates that upgrade to pax-web 3.1.2 went fine.
|
||||
So last step is to install the demo now:
|
||||
|
||||
```
|
||||
features:install keycloak-fuse-example
|
||||
```
|
||||
|
||||
|
|
123
examples/saml/README.md
Normal file
123
examples/saml/README.md
Normal file
|
@ -0,0 +1,123 @@
|
|||
# Keycloak SAML Quickstarts
|
||||
|
||||
## Introduction
|
||||
|
||||
These quickstarts run on JBoss Enterprise Application Platform 6 or WildFly.
|
||||
|
||||
We recommend using the Keycloak Appliance Distribution to test the quickstarts as it has already some things pre-set for you.
|
||||
There is individual README.md file specific for each quickstart in the particular subdirectory with the quickstart. Here are just some general info about the requirements for your OS etc.
|
||||
|
||||
## System Requirements
|
||||
|
||||
To run these quickstarts with the provided build scripts, you need the following:
|
||||
|
||||
1. Java 1.6 or Java 1.7, depending if you're using JBoss EAP or WildFly to run the quickstarts. You can choose from the following:
|
||||
* OpenJDK
|
||||
* Oracle Java SE
|
||||
* Oracle JRockit
|
||||
|
||||
2. Maven 3.0.0 or newer, to build and deploy the examples
|
||||
* If you have not yet installed Maven, see the [Maven Getting Started Guide](http://maven.apache.org/guides/getting-started/index.html) for details.
|
||||
* If you have installed Maven, you can check the version by typing the following in a command line:
|
||||
|
||||
mvn --version
|
||||
|
||||
3. The JBoss Enterprise Application Platform 6 distribution ZIP or the WildFly distribution ZIP.
|
||||
* For information on how to install and run those servers, refer to the their documentation.
|
||||
|
||||
|
||||
## Run the Quickstarts
|
||||
|
||||
The root folder of each individual quickstart contains a README file with specific details on how to build and run the example. In most cases you do the following:
|
||||
|
||||
* [Start the JBoss server](#start-the-jboss-server)
|
||||
* [Build and deploy the quickstarts](#build-and-deploy-the-quickstarts)
|
||||
|
||||
## About the Keycloak SAML Quickstarts
|
||||
|
||||
The *Keycloak SAML Quickstarts* provide a lot of examples about how to use *Keycloak SAML Support* to enable SSO for your applications.
|
||||
Before running them you need to understand how they are related with each other. Basically, Keycloak server is used as SAML Identity Provider and each individual WAR is used as SAML Service Provider.
|
||||
Keycloak actually uses *Picketlink Federation* library as base implementation of SAML support.
|
||||
|
||||
|
||||
### Using SAML Tracer Firefox Add-On to Debug the SAML SSO Flow
|
||||
|
||||
If you want to understand even better how IdPs and SPs communicate with each other, you may want to configure the [SAML Tracer Add-On](https://addons.mozilla.org/en-US/firefox/addon/saml-tracer/) to your Mozilla Firefox.
|
||||
This is a nice way to debug and view SAML Messages, so you can take a look on how the IdP and SP exchange messages when establishing a SSO session.
|
||||
|
||||
### Start the JBoss Server
|
||||
|
||||
Before you deploy a quickstart, in most cases you need a running JBoss Enterprise Application Platform 6 or WildFly server. A few of the Arquillian tests do not require a running server. This will be noted in the README for that quickstart.
|
||||
|
||||
The JBoss server can be started a few different ways.
|
||||
|
||||
* [Start the JBoss Server With the _web_ profile](#start-the-jboss-server-with-the-web-profile): This is the default configuration. It defines minimal subsystems and services.
|
||||
* [Start the JBoss Server with the _full_ profile](#start-the-jboss-server-with-the-full-profile): This profile configures many of the commonly used subsystems and services.
|
||||
* [Start the JBoss Server with a custom configuration](#start-the-jboss-server-with-custom-configuration-options): Custom configuration parameters can be specified on the command line when starting the server.
|
||||
|
||||
The README for each quickstart will specify which configuration is required to run the example.
|
||||
|
||||
#### Start the JBoss Server with the Web Profile
|
||||
|
||||
To start JBoss Enterprise Application Platform 6 or WildFly with the Web Profile:
|
||||
|
||||
1. Open a command line and navigate to the root of the JBoss server directory.
|
||||
2. The following shows the command line to start the JBoss server with the web profile:
|
||||
|
||||
For Linux: JBOSS_HOME/bin/standalone.sh
|
||||
For Windows: JBOSS_HOME\bin\standalone.bat
|
||||
|
||||
#### Start the JBoss Server with the Full Profile
|
||||
|
||||
To start JBoss Enterprise Application Platform 6 or WildFly with the Full Profile:
|
||||
|
||||
1. Open a command line and navigate to the root of the JBoss server directory.
|
||||
2. The following shows the command line to start the JBoss server with the full profile:
|
||||
|
||||
For Linux: JBOSS_HOME/bin/standalone.sh -c standalone-full.xml
|
||||
For Windows: JBOSS_HOME\bin\standalone.bat -c standalone-full.xml
|
||||
|
||||
#### Start the JBoss Server with Custom Configuration Options
|
||||
|
||||
To start JBoss Enterprise Application Platform 6 or WildFly with custom configuration options:
|
||||
|
||||
1. Open a command line and navigate to the root of the JBoss server directory.
|
||||
2. The following shows the command line to start the JBoss server. Replace the CUSTOM_OPTIONS with the custom optional parameters specified in the quickstart.
|
||||
|
||||
For Linux: JBOSS_HOME/bin/standalone.sh CUSTOM_OPTIONS
|
||||
For Windows: JBOSS_HOME\bin\standalone.bat CUSTOM_OPTIONS
|
||||
|
||||
### Build and Deploy the Quickstarts
|
||||
|
||||
See the README file in each individual quickstart folder for specific details and information on how to run and access the example.
|
||||
|
||||
#### Build the Quickstart Archive
|
||||
|
||||
In some cases, you may want to build the application to test for compile errors or view the contents of the archive.
|
||||
|
||||
1. Open a command line and navigate to the root directory of the quickstart you want to build.
|
||||
2. Use this command if you only want to build the archive, but not deploy it:
|
||||
|
||||
For EAP 6: mvn clean package
|
||||
For WildFly: mvn -Pwildfly clean package
|
||||
|
||||
#### Build and Deploy the Quickstart Archive
|
||||
|
||||
1. Make sure you [start the JBoss server](#start-the-jboss-server) as described in the README.
|
||||
2. Open a command line and navigate to the root directory of the quickstart you want to run.
|
||||
3. Use this command to build and deploy the archive:
|
||||
|
||||
For EAP 6: mvn clean package jboss-as:deploy
|
||||
For WildFly: mvn -Pwildfly clean package wildfly:deploy
|
||||
|
||||
#### Undeploy an Archive
|
||||
|
||||
The command to undeploy the quickstart is simply:
|
||||
|
||||
For EAP 6: mvn jboss-as:undeploy
|
||||
For WildFly: mvn -Pwildfly wildfly:undeploy
|
||||
|
||||
Keycloak Documentation
|
||||
------------
|
||||
|
||||
The documentation is available from the following [link](https://docs.jboss.org/keycloak/docs/1.1.0.Final/userguide/html/saml.html).
|
|
@ -12,6 +12,9 @@ What is it?
|
|||
|
||||
This example demonstrates Keycloak SAML 2.0 support in conjunction with a servlet secured by Picketlink's SAML SP client.
|
||||
|
||||
WARNING: This example doesn't use signed SAML request and response messages. It's used just for demonstration purpose, but in production
|
||||
you should always use signed SAML messages as shown in "post-with-signature" or "post-with-encryption" examples.
|
||||
|
||||
|
||||
Make sure you've set up the Keycloak Server
|
||||
--------------------------------------
|
||||
|
@ -228,7 +231,7 @@ Access the application
|
|||
|
||||
The application will be running at the following URL: <http://localhost:8080/sales-post>.
|
||||
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-picketlink-federation-quickstarts).*
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-keycloak-saml-quickstarts).*
|
||||
|
||||
|
||||
Undeploy the Archive
|
||||
|
@ -242,11 +245,6 @@ Undeploy the Archive
|
|||
For WildFly: mvn -Pwildfly wildfly:undeploy
|
||||
|
||||
|
||||
Run the Quickstart in JBoss Developer Studio or Eclipse
|
||||
-------------------------------------
|
||||
You can also start the server and deploy the quickstarts from Eclipse using JBoss tools. For more information, see [Use JBoss Developer Studio or Eclipse to Run the Quickstarts](../README.md#use-jboss-developer-studio-or-eclipse-to-run-the-quickstarts)
|
||||
|
||||
|
||||
Debug the Application
|
||||
------------------------------------
|
||||
|
||||
|
|
|
@ -225,7 +225,7 @@ Access the application
|
|||
|
||||
The application will be running at the following URL: <http://localhost:8080/sales-post-enc>.
|
||||
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-picketlink-federation-quickstarts).*
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-keycloak-saml-quickstarts).*
|
||||
|
||||
Undeploy the Archive
|
||||
--------------------
|
||||
|
@ -238,11 +238,6 @@ Undeploy the Archive
|
|||
For WildFly: mvn -Pwildfly wildfly:undeploy
|
||||
|
||||
|
||||
Run the Quickstart in JBoss Developer Studio or Eclipse
|
||||
-------------------------------------
|
||||
You can also start the server and deploy the quickstarts from Eclipse using JBoss tools. For more information, see [Use JBoss Developer Studio or Eclipse to Run the Quickstarts](../README.md#use-jboss-developer-studio-or-eclipse-to-run-the-quickstarts)
|
||||
|
||||
|
||||
Debug the Application
|
||||
------------------------------------
|
||||
|
||||
|
|
|
@ -227,7 +227,7 @@ Access the application
|
|||
|
||||
The application will be running at the following URL: <http://localhost:8080/sales-post-sig>.
|
||||
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-picketlink-federation-quickstarts).*
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-keycloak-saml-quickstarts).*
|
||||
|
||||
Undeploy the Archive
|
||||
--------------------
|
||||
|
@ -240,11 +240,6 @@ Undeploy the Archive
|
|||
For WildFly: mvn -Pwildfly wildfly:undeploy
|
||||
|
||||
|
||||
Run the Quickstart in JBoss Developer Studio or Eclipse
|
||||
-------------------------------------
|
||||
You can also start the server and deploy the quickstarts from Eclipse using JBoss tools. For more information, see [Use JBoss Developer Studio or Eclipse to Run the Quickstarts](../README.md#use-jboss-developer-studio-or-eclipse-to-run-the-quickstarts)
|
||||
|
||||
|
||||
Debug the Application
|
||||
------------------------------------
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ What is it?
|
|||
|
||||
This example demonstrates Keycloak SAML 2.0 support in conjunction with a servlet secured by Picketlink's SAML SP client.
|
||||
|
||||
WARNING: This example doesn't use signed SAML request and response messages. It's used just for demonstration purpose, but in production
|
||||
you should always use signed SAML messages as shown in "redirect-with-signature" example.
|
||||
|
||||
Make sure you've set up the Keycloak Server
|
||||
--------------------------------------
|
||||
|
@ -227,7 +229,7 @@ Access the application
|
|||
|
||||
The application will be running at the following URL: <http://localhost:8080/employee>.
|
||||
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-picketlink-federation-quickstarts).*
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-keycloak-saml-quickstarts).*
|
||||
|
||||
Undeploy the Archive
|
||||
--------------------
|
||||
|
@ -240,11 +242,6 @@ Undeploy the Archive
|
|||
For WildFly: mvn -Pwildfly wildfly:undeploy
|
||||
|
||||
|
||||
Run the Quickstart in JBoss Developer Studio or Eclipse
|
||||
-------------------------------------
|
||||
You can also start the server and deploy the quickstarts from Eclipse using JBoss tools. For more information, see [Use JBoss Developer Studio or Eclipse to Run the Quickstarts](../README.md#use-jboss-developer-studio-or-eclipse-to-run-the-quickstarts)
|
||||
|
||||
|
||||
Debug the Application
|
||||
------------------------------------
|
||||
|
||||
|
|
|
@ -228,7 +228,7 @@ Access the application
|
|||
|
||||
The application will be running at the following URL: <http://localhost:8080/employee-sig>.
|
||||
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-picketlink-federation-quickstarts).*
|
||||
*Note: A Service Provider alone is not very useful without an Identity Provider to authenticate users and issue SAML Assertions. Once you get this application deployed, please take a look at [About the PicketLink Federation Quickstarts](../README.md#about-the-keycloak-saml-quickstarts).*
|
||||
|
||||
Undeploy the Archive
|
||||
--------------------
|
||||
|
@ -241,11 +241,6 @@ Undeploy the Archive
|
|||
For WildFly: mvn -Pwildfly wildfly:undeploy
|
||||
|
||||
|
||||
Run the Quickstart in JBoss Developer Studio or Eclipse
|
||||
-------------------------------------
|
||||
You can also start the server and deploy the quickstarts from Eclipse using JBoss tools. For more information, see [Use JBoss Developer Studio or Eclipse to Run the Quickstarts](../README.md#use-jboss-developer-studio-or-eclipse-to-run-the-quickstarts)
|
||||
|
||||
|
||||
Debug the Application
|
||||
------------------------------------
|
||||
|
||||
|
|
|
@ -55,9 +55,8 @@
|
|||
"saml.signature.algorithm": "RSA_SHA256",
|
||||
"saml.client.signature": "true",
|
||||
"saml.authnstatement": "true",
|
||||
"privateKey": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
|
||||
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQAB",
|
||||
"X509Certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
|
||||
"saml.signing.private.key": "MIICWwIBAAKBgQDVG8a7xGN6ZIkDbeecySygcDfsypjUMNPE4QJjis8B316CvsZQ0hcTTLUyiRpHlHZys2k3xEhHBHymFC1AONcvzZzpb40tAhLHO1qtAnut00khjAdjR3muLVdGkM/zMC7G5s9iIwBVhwOQhy+VsGnCH91EzkjZ4SVEr55KJoyQJQIDAQABAoGADaTtoG/+foOZUiLjRWKL/OmyavK9vjgyFtThNkZY4qHOh0h3og0RdSbgIxAsIpEa1FUwU2W5yvI6mNeJ3ibFgCgcxqPk6GkAC7DWfQfdQ8cS+dCuaFTs8ObIQEvU50YzeNPiiFxRA+MnauCUXaKm/PnDfjd4tPgru7XZvlGh0wECQQDsBbN2cKkBKpr/b5oJiBcBaSZtWiMNuYBDn9x8uORj+Gy/49BUIMHF2EWyxOWz6ocP5YiynNRkPe21Zus7PEr1AkEA5yWQOkxUTIg43s4pxNSeHtL+Ebqcg54lY2xOQK0yufxUVZI8ODctAKmVBMiCKpU3mZQquOaQicuGtocpgxlScQI/YM31zZ5nsxLGf/5GL6KhzPJT0IYn2nk7IoFu7bjn9BjwgcPurpLA52TNMYWQsTqAKwT6DEhG1NaRqNWNpb4VAkBehObAYBwMm5udyHIeEc+CzUalm0iLLa0eRdiN7AUVNpCJ2V2Uo0NcxPux1AgeP5xXydXafDXYkwhINWcNO9qRAkEA58ckAC5loUGwU5dLaugsGH/a2Q8Ac8bmPglwfCstYDpl8Gp/eimb1eKyvDEELOhyImAv4/uZV9wN85V0xZXWsw==",
|
||||
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGP5dZDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1zaWcvMB4XDTE0MTAxNjEyNDQyM1oXDTI0MTAxNjEyNDYwM1owMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3Qtc2lnLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1RvGu8RjemSJA23nnMksoHA37MqY1DDTxOECY4rPAd9egr7GUNIXE0y1MokaR5R2crNpN8RIRwR8phQtQDjXL82c6W+NLQISxztarQJ7rdNJIYwHY0d5ri1XRpDP8zAuxubPYiMAVYcDkIcvlbBpwh/dRM5I2eElRK+eSiaMkCUCAwEAATANBgkqhkiG9w0BAQsFAAOBgQCLms6htnPaY69k1ntm9a5jgwSn/K61cdai8R8B0ccY7zvinn9AfRD7fiROQpFyY29wKn8WCLrJ86NBXfgFUGyR5nLNHVy3FghE36N2oHy53uichieMxffE6vhkKJ4P8ChfJMMOZlmCPsQPDvjoAghHt4mriFiQgRdPgIy/zDjSNw=="
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -76,9 +75,10 @@
|
|||
"saml.client.signature": "true",
|
||||
"saml.encrypt": "true",
|
||||
"saml.authnstatement": "true",
|
||||
"privateKey": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
|
||||
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQAB",
|
||||
"X509Certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
|
||||
"saml.signing.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
|
||||
"saml.signing.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g==",
|
||||
"saml.encryption.private.key": "MIICXQIBAAKBgQDb7kwJPkGdU34hicplwfp6/WmNcaLh94TSc7Jyr9Undp5pkyLgb0DE7EIE+6kSs4LsqCb8HDkB0nLD5DXbBJFd8n0WGoKstelvtg6FtVJMnwN7k7yZbfkPECWH9zF70VeOo9vbzrApNRnct8ZhH5fbflRB4JMA9L9R+LbURdoSKQIDAQABAoGBANtbZG9bruoSGp2s5zhzLzd4hczT6Jfk3o9hYjzNb5Z60ymN3Z1omXtQAdEiiNHkRdNxK+EM7TcKBfmoJqcaeTkW8cksVEAW23ip8W9/XsLqmbU2mRrJiKa+KQNDSHqJi1VGyimi4DDApcaqRZcaKDFXg2KDr/Qt5JFD/o9IIIPZAkEA+ZENdBIlpbUfkJh6Ln+bUTss/FZ1FsrcPZWu13rChRMrsmXsfzu9kZUWdUeQ2Dj5AoW2Q7L/cqdGXS7Mm5XhcwJBAOGZq9axJY5YhKrsksvYRLhQbStmGu5LG75suF+rc/44sFq+aQM7+oeRr4VY88Mvz7mk4esdfnk7ae+cCazqJvMCQQCx1L1cZw3yfRSn6S6u8XjQMjWE/WpjulujeoRiwPPY9WcesOgLZZtYIH8nRL6ehEJTnMnahbLmlPFbttxPRUanAkA11MtSIVcKzkhp2KV2ipZrPJWwI18NuVJXb+3WtjypTrGWFZVNNkSjkLnHIeCYlJIGhDd8OL9zAiBXEm6kmgLNAkBWAg0tK2hCjvzsaA505gWQb4X56uKWdb0IzN+fOLB3Qt7+fLqbVQNQoNGzqey6B4MoS1fUKAStqdGTFYPG/+9t",
|
||||
"saml.encryption.certificate": "MIIB1DCCAT0CBgFJGVacCDANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDEyVodHRwOi8vbG9jYWxob3N0OjgwODAvc2FsZXMtcG9zdC1lbmMvMB4XDTE0MTAxNjE0MjA0NloXDTI0MTAxNjE0MjIyNlowMDEuMCwGA1UEAxMlaHR0cDovL2xvY2FsaG9zdDo4MDgwL3NhbGVzLXBvc3QtZW5jLzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEikCAwEAATANBgkqhkiG9w0BAQsFAAOBgQBMrfGD9QFfx5v7ld/OAto5rjkTe3R1Qei8XRXfcs83vLaqEzjEtTuLGrJEi55kXuJgBpVmQpnwCCkkjSy0JxbqLDdVi9arfWUxEGmOr01ZHycELhDNaQcFqVMPr5kRHIHgktT8hK2IgCvd3Fy9/JCgUgCPxKfhwecyEOKxUc857g=="
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -110,9 +110,8 @@
|
|||
"saml.client.signature": "true",
|
||||
"saml.signature.algorithm": "RSA_SHA1",
|
||||
"saml.authnstatement": "true",
|
||||
"privateKey": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
|
||||
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQAB",
|
||||
"X509Certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
|
||||
"saml.signing.private.key": "MIICXQIBAAKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABAoGANU1efgc6ojIvwn7Lsf8GAKN9z2D6uS0T3I9nw1k2CtI+xWhgKAUltEANx5lEfBRYIdYclidRpqrk8DYgzASrDYTHXzqVBJfAk1VrAGpqyRq+TNMLUHkXiTiSDOQ6WqhX93UGMmAgQm1RsLa6+fy1BO/B2y85+Yf2OUylsKS6avECQQDslRDiNFdtEjdvyOL20tQ7+W+eKVxVxKAyQ3gFjIIDizELZt+Jq1Wz6XV9NhK1JFtlVugeD1tlW/+K16fEmDYXAkEAzqKoN/JeGb20rfQldAUWdQbb0jrQAYlgoSU/9fYH9YVJT8vnkfhPBTwIw9H9euf1//lRP/jHltHd5ch4230YyQJBAN3rOkoltPiABPZbpuLGgwS7BwOCYrWlWmurtBLoaTCvyVKbrgXybNL1pBrOtR+rufvGWLeRyja65Gs1vY6BBQMCQQCTsNq/MjJj/522f7yNUl2cw4w2lOa7Um+IflFbAcDqkZu2ty0Kvgns2d4B6INeZ5ECpjaWnMA7YkFRzZnkd2NRAkB8lEY56ScnNigoZkkjtEUd2ejdhZPYuS9SKfv9zHwN+I+DE2vVFZz8GPq/iLcMx13PkZaYaJNQ4FtQY/hRLSn5",
|
||||
"saml.signing.certificate": "MIIB0DCCATkCBgFJH5u0EDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtc2lnLzAeFw0xNDEwMTcxOTMzNThaFw0yNDEwMTcxOTM1MzhaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1zaWcvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+9kVgPFpshjS2aT2g52lqTv2lqb1jgvXZVk7iFF4LAO6SdCXKXRZI4SuzIRkVNpE1a42V1kQRlaozoFklgvX5sje8tkpa9ylq+bxGXM9RRycqRu2B+oWUV7Aqq7Bs0Xud0WeHQYRcEoCjqsFKGy65qkLRDdT70FTJgpSHts+gDwIDAQABMA0GCSqGSIb3DQEBCwUAA4GBACKyPLGqMX8GsIrCfJU8eVnpaqzTXMglLVo/nTcfAnWe9UAdVe8N3a2PXpDBvuqNA/DEAhVcQgxdlOTWnB6s8/yLTRuH0bZgb3qGdySif+lU+E7zZ/SiDzavAvn+ABqemnzHcHyhYO+hNRGHvUbW5OAii9Vdjhm8BI32YF1NwhKp"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -14,8 +14,7 @@
|
|||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label" for="secret">Secret</label>
|
||||
<div class="col-sm-4">
|
||||
<input ng-disabled="true" class="form-control" type="text" id="secret" name="secret" data-ng-model="secret" autofocus
|
||||
required>
|
||||
<input readonly kc-select-action="click" class="form-control" type="text" id="secret" name="secret" data-ng-model="secret">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<label class="col-sm-2 control-label" for="publicKey">Public key</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea type="text" id="publicKey" name="publicKey" class="form-control" rows="5"
|
||||
<textarea type="text" id="publicKey" name="publicKey" class="form-control" rows="4"
|
||||
kc-select-action="click" readonly>{{realm.publicKey}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,8 +18,7 @@
|
|||
<label class="col-sm-2 control-label" for="certificate">Certificate</label>
|
||||
|
||||
<div class="col-sm-10">
|
||||
<textarea type="text" id="certificate" name="certificate" class="form-control" rows="5"
|
||||
kc-select-action="click" readonly>{{realm.certificate}}</textarea>
|
||||
<textarea type="text" id="certificate" name="certificate" class="form-control" rows="8" kc-select-action="click" readonly>{{realm.certificate}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
|
@ -836,4 +836,11 @@ legend .kc-icon-collapse {
|
|||
|
||||
table table {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.form-control[readonly] {
|
||||
cursor: default;
|
||||
border: none;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
}
|
|
@ -14,6 +14,7 @@ import org.keycloak.adapters.KeycloakAccount;
|
|||
import org.keycloak.adapters.tomcat.GenericPrincipalFactory;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.security.Principal;
|
||||
import java.security.acl.Group;
|
||||
import java.util.ArrayList;
|
||||
|
@ -29,6 +30,8 @@ import java.util.Set;
|
|||
*/
|
||||
public class JBossWebPrincipalFactory extends GenericPrincipalFactory {
|
||||
|
||||
private static Constructor jbossWebPrincipalConstructor = findJBossGenericPrincipalConstructor();
|
||||
|
||||
@Override
|
||||
protected GenericPrincipal createPrincipal(Principal userPrincipal, List<String> roles) {
|
||||
return null;
|
||||
|
@ -84,9 +87,12 @@ public class JBossWebPrincipalFactory extends GenericPrincipalFactory {
|
|||
sc.getUtil().createSubjectInfo(userPrincipal, account, subject);
|
||||
List<String> rolesAsStringList = new ArrayList<String>();
|
||||
rolesAsStringList.addAll(roleSet);
|
||||
return new JBossGenericPrincipal(realm, userPrincipal.getName(), null, rolesAsStringList,
|
||||
userPrincipal, null, account, null, subject);
|
||||
|
||||
try {
|
||||
return (GenericPrincipal) jbossWebPrincipalConstructor.newInstance(realm, userPrincipal.getName(), null, rolesAsStringList, userPrincipal, null, account, null, subject);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Failed to create JBossGenericPrincipal", t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -150,4 +156,19 @@ public class JBossWebPrincipalFactory extends GenericPrincipalFactory {
|
|||
return roleSets;
|
||||
}
|
||||
|
||||
static Constructor findJBossGenericPrincipalConstructor() {
|
||||
for (Constructor<?> c : JBossGenericPrincipal.class.getConstructors()) {
|
||||
if (c.getParameterTypes().length == 9 &&
|
||||
c.getParameterTypes()[0].equals(Realm.class) &&
|
||||
c.getParameterTypes()[1].equals(String.class) &&
|
||||
c.getParameterTypes()[3].equals(List.class) &&
|
||||
c.getParameterTypes()[4].equals(Principal.class) &&
|
||||
c.getParameterTypes()[6].equals(Object.class) &&
|
||||
c.getParameterTypes()[8].equals(Subject.class)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package org.keycloak.adapters.jbossweb;
|
||||
|
||||
import org.apache.catalina.Realm;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JBossWebPrincipalFactoryTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Constructor constructor = JBossWebPrincipalFactory.findJBossGenericPrincipalConstructor();
|
||||
Assert.assertNotNull(constructor);
|
||||
Assert.assertEquals(Realm.class, constructor.getParameterTypes()[0]);
|
||||
Assert.assertEquals(String.class, constructor.getParameterTypes()[1]);
|
||||
Assert.assertEquals(List.class, constructor.getParameterTypes()[3]);
|
||||
Assert.assertEquals(Principal.class, constructor.getParameterTypes()[4]);
|
||||
Assert.assertEquals(Object.class, constructor.getParameterTypes()[6]);
|
||||
Assert.assertEquals(Subject.class, constructor.getParameterTypes()[8]);
|
||||
}
|
||||
|
||||
}
|
|
@ -29,5 +29,6 @@
|
|||
<module>installed</module>
|
||||
<module>admin-client</module>
|
||||
<module>osgi-adapter</module>
|
||||
<module>spring-boot</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
77
integration/spring-boot/pom.xml
Executable file
77
integration/spring-boot/pom.xml
Executable file
|
@ -0,0 +1,77 @@
|
|||
<?xml version="1.0"?>
|
||||
<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/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-spring-boot-adapter</artifactId>
|
||||
<name>Keycloak Spring Boot Integration</name>
|
||||
<description/>
|
||||
|
||||
<properties>
|
||||
<spring-boot.version>1.2.1.RELEASE</spring-boot.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>${jboss.logging.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-tomcat8-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-jetty92-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,28 @@
|
|||
package org.keycloak.adapters.springboot;
|
||||
|
||||
import org.keycloak.adapters.HttpFacade;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
|
||||
public class KeycloakSpringBootConfigResolver implements org.keycloak.adapters.KeycloakConfigResolver {
|
||||
|
||||
private KeycloakDeployment keycloakDeployment;
|
||||
|
||||
private static AdapterConfig adapterConfig;
|
||||
|
||||
@Override
|
||||
public KeycloakDeployment resolve(HttpFacade.Request request) {
|
||||
if (keycloakDeployment != null) {
|
||||
return keycloakDeployment;
|
||||
}
|
||||
|
||||
keycloakDeployment = KeycloakDeploymentBuilder.build(KeycloakSpringBootConfigResolver.adapterConfig);
|
||||
|
||||
return keycloakDeployment;
|
||||
}
|
||||
|
||||
static void setAdapterConfig(AdapterConfig adapterConfig) {
|
||||
KeycloakSpringBootConfigResolver.adapterConfig = adapterConfig;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package org.keycloak.adapters.springboot;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.tomcat.util.descriptor.web.LoginConfig;
|
||||
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
|
||||
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
|
||||
import org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
|
||||
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
|
||||
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Keycloak authentication integration for Spring Boot
|
||||
*
|
||||
* @author <a href="mailto:jimmidyson@gmail.com">Jimmi Dyson</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnWebApplication
|
||||
@EnableConfigurationProperties(KeycloakSpringBootProperties.class)
|
||||
public class KeycloakSpringBootConfiguration {
|
||||
|
||||
private KeycloakSpringBootProperties keycloakProperties;
|
||||
|
||||
@Autowired
|
||||
public void setKeycloakSpringBootProperties(KeycloakSpringBootProperties keycloakProperties) {
|
||||
this.keycloakProperties = keycloakProperties;
|
||||
KeycloakSpringBootConfigResolver.setAdapterConfig(keycloakProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddedServletContainerCustomizer getKeycloakContainerCustomizer() {
|
||||
return new EmbeddedServletContainerCustomizer() {
|
||||
@Override
|
||||
public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
|
||||
if (configurableEmbeddedServletContainer instanceof TomcatEmbeddedServletContainerFactory) {
|
||||
TomcatEmbeddedServletContainerFactory container = (TomcatEmbeddedServletContainerFactory) configurableEmbeddedServletContainer;
|
||||
|
||||
container.addContextValves(new KeycloakAuthenticatorValve());
|
||||
|
||||
container.addContextCustomizers(getTomcatKeycloakContextCustomizer());
|
||||
} else if (configurableEmbeddedServletContainer instanceof UndertowEmbeddedServletContainerFactory) {
|
||||
throw new IllegalArgumentException("Undertow Keycloak integration is not yet implemented");
|
||||
} else if (configurableEmbeddedServletContainer instanceof JettyEmbeddedServletContainerFactory) {
|
||||
throw new IllegalArgumentException("Jetty Keycloak integration is not yet implemented");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TomcatContextCustomizer getTomcatKeycloakContextCustomizer() {
|
||||
return new TomcatContextCustomizer() {
|
||||
@Override
|
||||
public void customize(Context context) {
|
||||
LoginConfig loginConfig = new LoginConfig();
|
||||
loginConfig.setAuthMethod("KEYCLOAK");
|
||||
context.setLoginConfig(loginConfig);
|
||||
|
||||
Set<String> authRoles = new HashSet<String>();
|
||||
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
||||
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
|
||||
for (String authRole : collection.getAuthRoles()) {
|
||||
if (!authRoles.contains(authRole)) {
|
||||
context.addSecurityRole(authRole);
|
||||
authRoles.add(authRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityConstraint constraint : keycloakProperties.getSecurityConstraints()) {
|
||||
SecurityConstraint tomcatConstraint = new SecurityConstraint();
|
||||
|
||||
for (KeycloakSpringBootProperties.SecurityCollection collection : constraint.getSecurityCollections()) {
|
||||
SecurityCollection tomcatSecCollection = new SecurityCollection();
|
||||
|
||||
if (collection.getName() != null) {
|
||||
tomcatSecCollection.setName(collection.getName());
|
||||
}
|
||||
if (collection.getDescription() != null) {
|
||||
tomcatSecCollection.setDescription(collection.getDescription());
|
||||
}
|
||||
|
||||
for (String authRole : collection.getAuthRoles()) {
|
||||
tomcatConstraint.addAuthRole(authRole);
|
||||
}
|
||||
|
||||
for (String pattern : collection.getPatterns()) {
|
||||
tomcatSecCollection.addPattern(pattern);
|
||||
}
|
||||
|
||||
for (String method : collection.getMethods()) {
|
||||
tomcatSecCollection.addMethod(method);
|
||||
}
|
||||
|
||||
for (String method : collection.getOmittedMethods()) {
|
||||
tomcatSecCollection.addOmittedMethod(method);
|
||||
}
|
||||
|
||||
tomcatConstraint.addCollection(tomcatSecCollection);
|
||||
}
|
||||
|
||||
context.addConstraint(tomcatConstraint);
|
||||
}
|
||||
|
||||
context.addParameter("keycloak.config.resolver", KeycloakSpringBootConfigResolver.class.getName());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
package org.keycloak.adapters.springboot;
|
||||
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@ConfigurationProperties(prefix = "keycloak", ignoreUnknownFields = false)
|
||||
public class KeycloakSpringBootProperties extends AdapterConfig {
|
||||
|
||||
private List<SecurityConstraint> securityConstraints = new ArrayList<SecurityConstraint>();
|
||||
|
||||
public static class SecurityConstraint {
|
||||
private List<SecurityCollection> securityCollections = new ArrayList<SecurityCollection>();
|
||||
|
||||
public List<SecurityCollection> getSecurityCollections() {
|
||||
return securityCollections;
|
||||
}
|
||||
|
||||
public void setSecurityCollections(List<SecurityCollection> securityCollections) {
|
||||
this.securityCollections = securityCollections;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SecurityCollection {
|
||||
private String name;
|
||||
private String description;
|
||||
private List<String> authRoles = new ArrayList<String>();
|
||||
private List<String> patterns = new ArrayList<String>();
|
||||
private List<String> methods = new ArrayList<String>();
|
||||
private List<String> omittedMethods = new ArrayList<String>();
|
||||
|
||||
public List<String> getAuthRoles() {
|
||||
return authRoles;
|
||||
}
|
||||
|
||||
public List<String> getPatterns() {
|
||||
return patterns;
|
||||
}
|
||||
|
||||
public List<String> getMethods() {
|
||||
return methods;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<String> getOmittedMethods() {
|
||||
return omittedMethods;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setAuthRoles(List<String> authRoles) {
|
||||
this.authRoles = authRoles;
|
||||
}
|
||||
|
||||
public void setPatterns(List<String> patterns) {
|
||||
this.patterns = patterns;
|
||||
}
|
||||
|
||||
public void setMethods(List<String> methods) {
|
||||
this.methods = methods;
|
||||
}
|
||||
|
||||
public void setOmittedMethods(List<String> omittedMethods) {
|
||||
this.omittedMethods = omittedMethods;
|
||||
}
|
||||
}
|
||||
|
||||
public List<SecurityConstraint> getSecurityConstraints() {
|
||||
return securityConstraints;
|
||||
}
|
||||
|
||||
public void setSecurityConstraints(List<SecurityConstraint> securityConstraints) {
|
||||
this.securityConstraints = securityConstraints;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.keycloak.adapters.springboot.KeycloakSpringBootConfiguration
|
20
pom.xml
20
pom.xml
|
@ -260,6 +260,26 @@
|
|||
<artifactId>picketlink-wildfly-common</artifactId>
|
||||
<version>${picketlink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-tomcat-common</artifactId>
|
||||
<version>${picketlink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-tomcat5-single</artifactId>
|
||||
<version>${picketlink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-jbas-common</artifactId>
|
||||
<version>${picketlink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-jbas7-single</artifactId>
|
||||
<version>${picketlink.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-idm-simple-schema</artifactId>
|
||||
|
|
|
@ -1,254 +1,268 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.testsuite.oauth;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a>
|
||||
*/
|
||||
public class OAuthRedirectUriTest {
|
||||
|
||||
@ClassRule
|
||||
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
ApplicationModel installedApp = appRealm.addApplication("test-installed");
|
||||
installedApp.setEnabled(true);
|
||||
installedApp.addRedirectUri(Constants.INSTALLED_APP_URN);
|
||||
installedApp.addRedirectUri(Constants.INSTALLED_APP_URL);
|
||||
installedApp.setSecret("password");
|
||||
|
||||
ApplicationModel installedApp2 = appRealm.addApplication("test-installed2");
|
||||
installedApp2.setEnabled(true);
|
||||
installedApp2.addRedirectUri(Constants.INSTALLED_APP_URL + "/myapp");
|
||||
installedApp2.setSecret("password");
|
||||
|
||||
ApplicationModel installedApp3 = appRealm.addApplication("test-wildcard");
|
||||
installedApp3.setEnabled(true);
|
||||
installedApp3.addRedirectUri("http://example.com/foo/*");
|
||||
installedApp3.setSecret("password");
|
||||
}
|
||||
});
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
|
||||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@WebResource
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
@Test
|
||||
public void testNoParam() throws IOException {
|
||||
oauth.redirectUri(null);
|
||||
OAuthClient.AuthorizationCodeResponse response = oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
Assert.assertNotNull(response.getCode());
|
||||
Assert.assertEquals(oauth.getCurrentRequest(), "http://localhost:8081/app");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoParamMultipleValidUris() throws IOException {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").addRedirectUri("http://localhost:8081/app2");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
oauth.redirectUri(null);
|
||||
oauth.openLoginForm();
|
||||
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").removeRedirectUri("http://localhost:8081/app2");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoParamNoValidUris() throws IOException {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").removeRedirectUri("http://localhost:8081/app/*");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
oauth.redirectUri(null);
|
||||
oauth.openLoginForm();
|
||||
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").addRedirectUri("http://localhost:8081/app/*");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoValidUris() throws IOException {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").removeRedirectUri("http://localhost:8081/app/*");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
oauth.redirectUri(null);
|
||||
oauth.openLoginForm();
|
||||
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").addRedirectUri("http://localhost:8081/app/*");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValid() throws IOException {
|
||||
oauth.redirectUri("http://localhost:8081/app");
|
||||
OAuthClient.AuthorizationCodeResponse response = oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
Assert.assertNotNull(response.getCode());
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/app?code="));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalid() throws IOException {
|
||||
oauth.redirectUri("http://localhost:8081/app2");
|
||||
oauth.openLoginForm();
|
||||
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithParams() throws IOException {
|
||||
oauth.redirectUri("http://localhost:8081/app?key=value");
|
||||
OAuthClient.AuthorizationCodeResponse response = oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
Assert.assertNotNull(response.getCode());
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/app?key=value&code="));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWildcard() throws IOException {
|
||||
oauth.clientId("test-wildcard");
|
||||
checkRedirectUri("http://example.com", false);
|
||||
checkRedirectUri("http://example.com/foo", true);
|
||||
checkRedirectUri("http://example.com/foobar", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalhost() throws IOException {
|
||||
oauth.clientId("test-installed");
|
||||
|
||||
checkRedirectUri("urn:ietf:wg:oauth:2.0:oob", true);
|
||||
checkRedirectUri("http://localhost", true);
|
||||
|
||||
checkRedirectUri("http://localhost:8081", true);
|
||||
|
||||
checkRedirectUri("http://localhosts", false);
|
||||
checkRedirectUri("http://localhost/myapp", false);
|
||||
checkRedirectUri("http://localhost:8081/myapp", false);
|
||||
|
||||
oauth.clientId("test-installed2");
|
||||
|
||||
checkRedirectUri("http://localhost/myapp", true);
|
||||
checkRedirectUri("http://localhost:8081/myapp", true);
|
||||
|
||||
checkRedirectUri("http://localhosts/myapp", false);
|
||||
checkRedirectUri("http://localhost", false);
|
||||
checkRedirectUri("http://localhost/myapp2", false);
|
||||
}
|
||||
|
||||
private void checkRedirectUri(String redirectUri, boolean expectValid) throws IOException {
|
||||
oauth.redirectUri(redirectUri);
|
||||
oauth.openLoginForm();
|
||||
|
||||
if (expectValid) {
|
||||
Assert.assertTrue(loginPage.isCurrent());
|
||||
} else {
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
}
|
||||
|
||||
if (expectValid) {
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
Assert.assertNotNull(code);
|
||||
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
|
||||
Assert.assertEquals("Expected success, but got error: " + tokenResponse.getError(), 200, tokenResponse.getStatusCode());
|
||||
|
||||
oauth.doLogout(tokenResponse.getRefreshToken(), "password");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.testsuite.oauth;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.ErrorPage;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a>
|
||||
*/
|
||||
public class OAuthRedirectUriTest {
|
||||
|
||||
@ClassRule
|
||||
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
ApplicationModel installedApp = appRealm.addApplication("test-installed");
|
||||
installedApp.setEnabled(true);
|
||||
installedApp.addRedirectUri(Constants.INSTALLED_APP_URN);
|
||||
installedApp.addRedirectUri(Constants.INSTALLED_APP_URL);
|
||||
installedApp.setSecret("password");
|
||||
|
||||
ApplicationModel installedApp2 = appRealm.addApplication("test-installed2");
|
||||
installedApp2.setEnabled(true);
|
||||
installedApp2.addRedirectUri(Constants.INSTALLED_APP_URL + "/myapp");
|
||||
installedApp2.setSecret("password");
|
||||
|
||||
ApplicationModel installedApp3 = appRealm.addApplication("test-wildcard");
|
||||
installedApp3.setEnabled(true);
|
||||
installedApp3.addRedirectUri("http://example.com/foo/*");
|
||||
installedApp3.addRedirectUri("http://localhost:8081/foo/*");
|
||||
installedApp3.setSecret("password");
|
||||
}
|
||||
});
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
|
||||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@WebResource
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
@Test
|
||||
public void testNoParam() throws IOException {
|
||||
oauth.redirectUri(null);
|
||||
OAuthClient.AuthorizationCodeResponse response = oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
Assert.assertNotNull(response.getCode());
|
||||
Assert.assertEquals(oauth.getCurrentRequest(), "http://localhost:8081/app");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoParamMultipleValidUris() throws IOException {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").addRedirectUri("http://localhost:8081/app2");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
oauth.redirectUri(null);
|
||||
oauth.openLoginForm();
|
||||
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").removeRedirectUri("http://localhost:8081/app2");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoParamNoValidUris() throws IOException {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").removeRedirectUri("http://localhost:8081/app/*");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
oauth.redirectUri(null);
|
||||
oauth.openLoginForm();
|
||||
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").addRedirectUri("http://localhost:8081/app/*");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoValidUris() throws IOException {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").removeRedirectUri("http://localhost:8081/app/*");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
oauth.redirectUri(null);
|
||||
oauth.openLoginForm();
|
||||
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").addRedirectUri("http://localhost:8081/app/*");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValid() throws IOException {
|
||||
oauth.redirectUri("http://localhost:8081/app");
|
||||
OAuthClient.AuthorizationCodeResponse response = oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
Assert.assertNotNull(response.getCode());
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/app?code="));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalid() throws IOException {
|
||||
oauth.redirectUri("http://localhost:8081/app2");
|
||||
oauth.openLoginForm();
|
||||
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithParams() throws IOException {
|
||||
oauth.redirectUri("http://localhost:8081/app?key=value");
|
||||
OAuthClient.AuthorizationCodeResponse response = oauth.doLogin("test-user@localhost", "password");
|
||||
|
||||
Assert.assertNotNull(response.getCode());
|
||||
Assert.assertTrue(driver.getCurrentUrl().startsWith("http://localhost:8081/app?key=value&code="));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWildcard() throws IOException {
|
||||
oauth.clientId("test-wildcard");
|
||||
checkRedirectUri("http://example.com", false);
|
||||
checkRedirectUri("http://localhost:8080", false, true);
|
||||
checkRedirectUri("http://example.com/foo", true);
|
||||
checkRedirectUri("http://example.com/foo/bar", true);
|
||||
checkRedirectUri("http://localhost:8081/foo", true, true);
|
||||
checkRedirectUri("http://localhost:8081/foo/bar", true, true);
|
||||
checkRedirectUri("http://example.com/foobar", false);
|
||||
checkRedirectUri("http://localhost:8081/foobar", false, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalhost() throws IOException {
|
||||
oauth.clientId("test-installed");
|
||||
|
||||
checkRedirectUri("urn:ietf:wg:oauth:2.0:oob", true, true);
|
||||
checkRedirectUri("http://localhost", true);
|
||||
|
||||
checkRedirectUri("http://localhost:8081", true, true);
|
||||
|
||||
checkRedirectUri("http://localhosts", false);
|
||||
checkRedirectUri("http://localhost/myapp", false);
|
||||
checkRedirectUri("http://localhost:8081/myapp", false, true);
|
||||
|
||||
oauth.clientId("test-installed2");
|
||||
|
||||
checkRedirectUri("http://localhost/myapp", true);
|
||||
checkRedirectUri("http://localhost:8081/myapp", true, true);
|
||||
|
||||
checkRedirectUri("http://localhosts/myapp", false);
|
||||
checkRedirectUri("http://localhost", false);
|
||||
checkRedirectUri("http://localhost/myapp2", false);
|
||||
}
|
||||
|
||||
private void checkRedirectUri(String redirectUri, boolean expectValid) throws IOException {
|
||||
checkRedirectUri(redirectUri, expectValid, false);
|
||||
}
|
||||
|
||||
private void checkRedirectUri(String redirectUri, boolean expectValid, boolean checkCodeToToken) throws IOException {
|
||||
oauth.redirectUri(redirectUri);
|
||||
oauth.openLoginForm();
|
||||
|
||||
if (expectValid) {
|
||||
Assert.assertTrue(loginPage.isCurrent());
|
||||
} else {
|
||||
Assert.assertTrue(errorPage.isCurrent());
|
||||
Assert.assertEquals("Invalid redirect_uri.", errorPage.getError());
|
||||
}
|
||||
|
||||
if (expectValid) {
|
||||
Assert.assertTrue(loginPage.isCurrent());
|
||||
|
||||
if (checkCodeToToken) {
|
||||
loginPage.login("test-user@localhost", "password");
|
||||
|
||||
String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
|
||||
Assert.assertNotNull(code);
|
||||
|
||||
OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "password");
|
||||
|
||||
Assert.assertEquals("Expected success, but got error: " + tokenResponse.getError(), 200, tokenResponse.getStatusCode());
|
||||
|
||||
oauth.doLogout(tokenResponse.getRefreshToken(), "password");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue