Compare commits
317
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
efff6bf2e0 | ||
|
|
b9532419ef | ||
|
|
f4811b423c | ||
|
|
00d1e0fce3 | ||
|
|
6cab6532ed | ||
|
|
430108455f | ||
|
|
28acd183cc | ||
|
|
5b5405c1eb | ||
|
|
7ff64078fd | ||
|
|
08f8b06de2 | ||
|
|
45b58de49f | ||
|
|
a0368908aa | ||
|
|
f3f4b8dee7 | ||
|
|
783f484386 | ||
|
|
cf97bf48bf | ||
|
|
72c5afbeb7 | ||
|
|
fec74f2621 | ||
|
|
ba740c13c6 | ||
|
|
bc78e878d2 | ||
|
|
5da1858d99 | ||
|
|
4833dd82cb | ||
|
|
dd74ec97f5 | ||
|
|
76ccc9ea63 | ||
|
|
a917939441 | ||
|
|
f793e883ef | ||
|
|
d2780c0b2e | ||
|
|
896e309866 | ||
|
|
a6dbfc7861 | ||
|
|
070ed88337 | ||
|
|
bd52908df4 | ||
|
|
5399c20a8c | ||
|
|
4a82ed73a4 | ||
|
|
f388c3d385 | ||
|
|
2de0748e95 | ||
|
|
997ede06fb | ||
|
|
68be5dd12a | ||
|
|
ad751e419c | ||
|
|
c862479dcd | ||
|
|
5cb11b9036 | ||
|
|
f63d52af04 | ||
|
|
3c0959640b | ||
|
|
dea4f4b931 | ||
|
|
3d26ab8817 | ||
|
|
3f3b21a75e | ||
|
|
b0002d6a09 | ||
|
|
c1708ea7d5 | ||
|
|
937ab313e5 | ||
|
|
523f746745 | ||
|
|
f5f5d934c8 | ||
|
|
d67804cd0b | ||
|
|
dcbdb59e00 | ||
|
|
92db89f9e8 | ||
|
|
aafc46657a | ||
|
|
667e014971 | ||
|
|
ed525f8ed9 | ||
|
|
e03140a9f2 | ||
|
|
6377cb71f7 | ||
|
|
a3c40f311c | ||
|
|
c736fab75e | ||
|
|
df442a6ab8 | ||
|
|
d355d53262 | ||
|
|
a7bebadf20 | ||
|
|
59d7ff3f0a | ||
|
|
7f02ac84ad | ||
|
|
b3723cf323 | ||
|
|
edfe7bd285 | ||
|
|
311fca5a72 | ||
|
|
98d82d2cae | ||
|
|
9372d23ad9 | ||
|
|
de2ce8a04e | ||
|
|
7eafb14622 | ||
|
|
fd91d46b5b | ||
|
|
3768c1f1ad | ||
|
|
dcbf91eff0 | ||
|
|
66242b4176 | ||
|
|
39627f0661 | ||
|
|
71f2bf77f8 | ||
|
|
99c79f9fd7 | ||
|
|
c942f44437 | ||
|
|
efaca72631 | ||
|
|
6fb98c547a | ||
|
|
25bddb8ee7 | ||
|
|
f1bff01999 | ||
|
|
74e5c62069 | ||
|
|
e52e13d611 | ||
|
|
8f6e7d275e | ||
|
|
ad42b4dde3 | ||
|
|
973fa0d4ec | ||
|
|
640e6efefd | ||
|
|
e026c8b206 | ||
|
|
761be66362 | ||
|
|
e54bf4a16f | ||
|
|
35c33f132e | ||
|
|
666827b7cb | ||
|
|
10998eb35b | ||
|
|
10d02e7442 | ||
|
|
8d493381a9 | ||
|
|
d353add130 | ||
|
|
77d17001bf | ||
|
|
df317b8368 | ||
|
|
ee358ccbc4 | ||
|
|
4450416905 | ||
|
|
0b821a71e3 | ||
|
|
fd620ef505 | ||
|
|
e05f60b66e | ||
|
|
947a2f0c20 | ||
|
|
063cefa18c | ||
|
|
8dfc7d9e8d | ||
|
|
d632d22d2a | ||
|
|
4a89fe93b5 | ||
|
|
08e1f455e2 | ||
|
|
e65c0d9893 | ||
|
|
be656b2dba | ||
|
|
bfcf595346 | ||
|
|
06a7938aa6 | ||
|
|
e51cb4df60 | ||
|
|
553f8a6f27 | ||
|
|
4d288f1ffc | ||
|
|
3e5854c651 | ||
|
|
53fc940952 | ||
|
|
c95e21a49a | ||
|
|
692898501e | ||
|
|
1fbbdb5f4a | ||
|
|
d4c69e98ce | ||
|
|
8224f3ba48 | ||
|
|
5e0f560fdc | ||
|
|
1d4bf4ddaa | ||
|
|
775d1f096c | ||
|
|
316053018b | ||
|
|
1d4ef83b81 | ||
|
|
074b6d261a | ||
|
|
bbe2532acb | ||
|
|
4166393396 | ||
|
|
9ce452443d | ||
|
|
215a3497ae | ||
|
|
f17863041a | ||
|
|
40510683a1 | ||
|
|
2b1ec74fba | ||
|
|
206b44d9c2 | ||
|
|
44787d83d2 | ||
|
|
23a3aa023c | ||
|
|
9151d2aebd | ||
|
|
5e1ac684a5 | ||
|
|
5fca18b3c0 | ||
|
|
7395d6585c | ||
|
|
05d0f1d2ee | ||
|
|
a338626d2b | ||
|
|
767d1fcc20 | ||
|
|
b8d23829aa | ||
|
|
ce747ad039 | ||
|
|
3a4670e8ff | ||
|
|
68c3652078 | ||
|
|
1f49ae8a2f | ||
|
|
ac49ba2f44 | ||
|
|
f0d3fa783a | ||
|
|
d007ee3630 | ||
|
|
84fd38316c | ||
|
|
104a9f9842 | ||
|
|
d49e0eda9e | ||
|
|
76f406e993 | ||
|
|
d1e6b7523e | ||
|
|
14b3d085a0 | ||
|
|
c942a8ac4f | ||
|
|
535023d800 | ||
|
|
dae219d32e | ||
|
|
a7d245b1fb | ||
|
|
1f4df58e6c | ||
|
|
d314198e40 | ||
|
|
35b6c88500 | ||
|
|
f74e1463df | ||
|
|
37243d058c | ||
|
|
927032997e | ||
|
|
e64a2d8de5 | ||
|
|
e697d5ebf5 | ||
|
|
9d5cd2091e | ||
|
|
870f29d797 | ||
|
|
f037aaa1af | ||
|
|
e17105cc8e | ||
|
|
cf75a97f08 | ||
|
|
8e0252ecb1 | ||
|
|
34e033e351 | ||
|
|
11035dbe1b | ||
|
|
0b782e9910 | ||
|
|
136fd37b1b | ||
|
|
b5f3efe272 | ||
|
|
955967f78a | ||
|
|
642fc996fb | ||
|
|
6ce0285315 | ||
|
|
aef61411e9 | ||
|
|
83a409bbf6 | ||
|
|
5ed864fbbc | ||
|
|
416e71fa97 | ||
|
|
f9b099d49d | ||
|
|
193aa23581 | ||
|
|
3a4750c6de | ||
|
|
63201c10f5 | ||
|
|
65f058a088 | ||
|
|
c95c9fa022 | ||
|
|
af736a29f1 | ||
|
|
22f26a6221 | ||
|
|
d30b81144e | ||
|
|
e32ef66e2a | ||
|
|
3ceb818ee5 | ||
|
|
6b1e4b2ab3 | ||
|
|
3a4141f5b5 | ||
|
|
5ea6169214 | ||
|
|
46c63d9169 | ||
|
|
db1d6e79d9 | ||
|
|
8eb2f35423 | ||
|
|
a7c563b0eb | ||
|
|
6a6ddc8a0c | ||
|
|
6965364813 | ||
|
|
9937be1cdf | ||
|
|
78830836b8 | ||
|
|
aea5055b30 | ||
|
|
a75d608f87 | ||
|
|
d52a365766 | ||
|
|
c071601660 | ||
|
|
fad0e80bcf | ||
|
|
f959332117 | ||
|
|
fb7ca4928d | ||
|
|
f5bc936e0d | ||
|
|
1ef5bf2abf | ||
|
|
14b2401aa8 | ||
|
|
6e5820ea11 | ||
|
|
115e533a71 | ||
|
|
495ab25301 | ||
|
|
afe4f84135 | ||
|
|
7d853dabe4 | ||
|
|
e32d7c52fc | ||
|
|
5addabe893 | ||
|
|
0daa3ee29f | ||
|
|
9c5e38a023 | ||
|
|
d42c917448 | ||
|
|
8fb60226c8 | ||
|
|
fda190de01 | ||
|
|
58dd353238 | ||
|
|
0027d22964 | ||
|
|
1a05f0f3b4 | ||
|
|
476da4e3e0 | ||
|
|
edb9f0cecf | ||
|
|
0addc4cf58 | ||
|
|
770d2d8a4c | ||
|
|
f4898884e4 | ||
|
|
5ef1ddb9f1 | ||
|
|
f3e5541b7d | ||
|
|
a19a3f4277 | ||
|
|
3f327326aa | ||
|
|
29ea24c8fb | ||
|
|
4fe328002a | ||
|
|
b1b149e0c3 | ||
|
|
47da227809 | ||
|
|
43d33260c9 | ||
|
|
ad715371a3 | ||
|
|
0ff4b6dd93 | ||
|
|
e3bb61248a | ||
|
|
e75e85cf80 | ||
|
|
375d061f58 | ||
|
|
78eb5acb8e | ||
|
|
656a5c4baa | ||
|
|
b8d05c452c | ||
|
|
f6631a6163 | ||
|
|
c0f6b6d8d0 | ||
|
|
d13acaa9ef | ||
|
|
8ebac35706 | ||
|
|
517e42f13d | ||
|
|
9c6e6c8284 | ||
|
|
a9ed193826 | ||
|
|
a94fd9a098 | ||
|
|
411ff56499 | ||
|
|
c70d12a411 | ||
|
|
d4f138cadc | ||
|
|
da70391677 | ||
|
|
f61fb4ba18 | ||
|
|
d2a5403527 | ||
|
|
06ac706057 | ||
|
|
4abd62661f | ||
|
|
f56f1c85b9 | ||
|
|
46e386cd43 | ||
|
|
600353899a | ||
|
|
4fbbf39c51 | ||
|
|
9a047eda36 | ||
|
|
716bf104db | ||
|
|
f435a8c6df | ||
|
|
a18715a774 | ||
|
|
f0f0aae88d | ||
|
|
4ae9310f83 | ||
|
|
1567982f0b | ||
|
|
875aae91fc | ||
|
|
bbef4e2be1 | ||
|
|
4955f36115 | ||
|
|
0606cb4a5e | ||
|
|
85e7cf5783 | ||
|
|
6fbc0975c0 | ||
|
|
b92a178142 | ||
|
|
eb5ae4aae9 | ||
|
|
dc23ef6188 | ||
|
|
b0b28a9f38 | ||
|
|
e6f6f92403 | ||
|
|
63f8d92a8b | ||
|
|
a42a750ebb | ||
|
|
335832d553 | ||
|
|
ac1e8e8389 | ||
|
|
448fb35a72 | ||
|
|
2ba2e7768d | ||
|
|
d0ead0f0a0 | ||
|
|
463310e1c3 | ||
|
|
5ca9af0b1d | ||
|
|
e795bdc16c | ||
|
|
37306e689d | ||
|
|
ca13e3c4ba | ||
|
|
3a5861c624 | ||
|
|
326818ea45 | ||
|
|
148b466ce4 | ||
|
|
18e4be3cc9 | ||
|
|
6a34ad36f5 | ||
|
|
56a9451128 |
No files matched your search
@@ -1,39 +1,72 @@
|
||||
keycloak
|
||||
Keycloak
|
||||
========
|
||||
|
||||
Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
|
||||
and video tutorials.
|
||||
Keycloak is an SSO Service for web apps and REST services. For more information visit [http://keycloak.org](http://keycloak.org).
|
||||
|
||||
Keycloak is an SSO Service for web apps and REST services.
|
||||
It can be used for social applications as well as enterprise applications. It is based on OpenID Connect with support for SAML 2.0 as well.
|
||||
Here's some of the features:
|
||||
|
||||
* SSO and Single Log Out for browser applications
|
||||
* Social Broker. Enable Google, Facebook, Yahoo, Twitter, GitHub, LinkedIn social login with no code required.
|
||||
* Optional LDAP/Active Directory integration
|
||||
* Optional User Registration
|
||||
* Password and TOTP support (via Google Authenticator or FreeOTP). Client cert auth coming soon.
|
||||
* User session management from both admin and user perspective
|
||||
* Customizable themes for user facing pages: login, grant pages, account management, emails, and admin console all customizable!
|
||||
* OAuth Bearer token auth for REST Services
|
||||
* Integrated Browser App to REST Service token propagation
|
||||
* Admin REST API
|
||||
* OAuth 2.0 Grant requests
|
||||
* CORS Support
|
||||
* CORS Web Origin management and validation
|
||||
* Completely centrally managed user and role mapping metadata. Minimal configuration at the application side
|
||||
* Admin Console for managing users, roles, role mappings, applications, user sessions, allowed CORS web origins, and OAuth clients.
|
||||
* Deployable as a WAR, appliance, or an Openshift cloud service (SaaS).
|
||||
* Supports JBoss AS7, EAP 6.x, Wildfly, Tomcat, and Jetty applications. Plans to support Node.js, RAILS, GRAILS, and other non-Java applications.
|
||||
* Javascript/HTML 5 adapter for pure Javascript apps
|
||||
* Session management from admin console
|
||||
* Revocation policies
|
||||
* Password policies
|
||||
* OpenID Connect Support
|
||||
* SAML Support
|
||||
* Token claim and SAML assertion mappings, role name mappings, etc. Ability to configure exactly what information you want in your tokens and SAML documents
|
||||
* IDP brokering or chaining. You can set up Keycloak to be a child IDP to another SAML or OIDC IDP.
|
||||
* Kerberos bridging. Logged in Kerberos users can access Keycloak SAML or OIDC applications via our Kerberos bridge.
|
||||
Building
|
||||
--------
|
||||
|
||||
Please visit [http://keycloak.org](http://keycloak.org) for more information on Keycloak including how to download, documentation,
|
||||
and video tutorials.
|
||||
Ensure you have JDK 7 (or newer), Maven 3.2.1 (or newer) and Git installed
|
||||
|
||||
java -version
|
||||
mvn -version
|
||||
git --version
|
||||
|
||||
First clone the Keycloak repository:
|
||||
|
||||
git clone https://github.com/keycloak/keycloak.git
|
||||
cd keycloak
|
||||
|
||||
To build Keycloak run:
|
||||
|
||||
mvn install
|
||||
|
||||
This will build all modules and run the testsuite.
|
||||
|
||||
To build the distribution run:
|
||||
|
||||
mvn install -Pdistribution
|
||||
|
||||
Once completed you will find distribution archives in `distribution`.
|
||||
|
||||
|
||||
Starting Keycloak
|
||||
-----------------
|
||||
|
||||
To start Keycloak during development first build as specficied above, then run:
|
||||
|
||||
mvn -f testsuite/integration/pom.xml exec:java -Pkeycloak-server
|
||||
|
||||
|
||||
To start Keycloak from the appliance distribution first build the distribution it as specified above, then run:
|
||||
|
||||
tar xfz distribution/appliance-dist/target/keycloak-appliance-dist-all-<VERSION>.tar.gz
|
||||
cd keycloak-appliance-dist-all-<VERSION>/keycloak
|
||||
bin/standalone.sh
|
||||
|
||||
To stop the server press `Ctrl + C`.
|
||||
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
* See [Hacking on Keycloak](misc/HackingOnKeycloak.md)
|
||||
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
* [User Guide, Admin REST API and Javadocs](http://keycloak.jboss.org/docs)
|
||||
* Developer documentation
|
||||
* [Hacking on Keycloak](misc/HackingOnKeycloak.md) - how to become a Keycloak contributor
|
||||
* [Testsuite](misc/Testsuite.md) - details about testsuite, but also how to quickly run Keycloak during development and a few test tools (OTP generation, LDAP server, Mail server)
|
||||
* [Database Testing](misc/DatabaseTesting.md) - how to do testing of Keycloak on different databases
|
||||
* [Updating Database](misc/UpdatingDatabaseSchema.md) - how to change the Keycloak database
|
||||
* [Release Process](misc/ReleaseProcess.md) - how to release Keycloak
|
||||
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
+1
-4
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -18,17 +18,14 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-events-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
@@ -59,4 +62,24 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
|
||||
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backchannelLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
}
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class AbstractIdentityProviderMapper implements IdentityProviderMapper {
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityProviderMapper create(KeycloakSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(org.keycloak.Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
}
|
||||
+83
-47
@@ -17,25 +17,33 @@
|
||||
*/
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>Represents all identity information obtained from an {@link IdentityProvider} after a
|
||||
* <p>Represents all identity information obtained from an {@link org.keycloak.broker.provider.IdentityProvider} after a
|
||||
* successful authentication.</p>
|
||||
*
|
||||
* @author Pedro Igor
|
||||
*/
|
||||
public class FederatedIdentity {
|
||||
public class BrokeredIdentityContext {
|
||||
|
||||
private String id;
|
||||
private String username;
|
||||
private String email;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
private String email;
|
||||
private String token;
|
||||
private String identityProviderId;
|
||||
private String brokerSessionId;
|
||||
private String brokerUserId;
|
||||
private String code;
|
||||
private String token;
|
||||
private IdentityProviderModel idpConfig;
|
||||
private IdentityProvider idp;
|
||||
private Map<String, Object> contextData = new HashMap<>();
|
||||
|
||||
public FederatedIdentity(String id) {
|
||||
public BrokeredIdentityContext(String id) {
|
||||
if (id == null) {
|
||||
throw new RuntimeException("No identifier provider for identity.");
|
||||
}
|
||||
@@ -59,26 +67,6 @@ public class FederatedIdentity {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (name != null) {
|
||||
int i = name.lastIndexOf(' ');
|
||||
if (i != -1) {
|
||||
firstName = name.substring(0, i);
|
||||
lastName = name.substring(i + 1);
|
||||
} else {
|
||||
firstName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
@@ -87,23 +75,6 @@ public class FederatedIdentity {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return this.token;
|
||||
}
|
||||
|
||||
public String getIdentityProviderId() {
|
||||
return this.identityProviderId;
|
||||
}
|
||||
|
||||
public void setIdentityProviderId(String identityProviderId) {
|
||||
this.identityProviderId = identityProviderId;
|
||||
}
|
||||
|
||||
public String getBrokerSessionId() {
|
||||
return brokerSessionId;
|
||||
}
|
||||
@@ -120,16 +91,81 @@ public class FederatedIdentity {
|
||||
this.brokerUserId = brokerUserId;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public IdentityProviderModel getIdpConfig() {
|
||||
return idpConfig;
|
||||
}
|
||||
|
||||
public void setIdpConfig(IdentityProviderModel idpConfig) {
|
||||
this.idpConfig = idpConfig;
|
||||
}
|
||||
|
||||
public IdentityProvider getIdp() {
|
||||
return idp;
|
||||
}
|
||||
|
||||
public void setIdp(IdentityProvider idp) {
|
||||
this.idp = idp;
|
||||
}
|
||||
|
||||
public Map<String, Object> getContextData() {
|
||||
return contextData;
|
||||
}
|
||||
|
||||
public void setContextData(Map<String, Object> contextData) {
|
||||
this.contextData = contextData;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
if (name != null) {
|
||||
int i = name.lastIndexOf(' ');
|
||||
if (i != -1) {
|
||||
firstName = name.substring(0, i);
|
||||
lastName = name.substring(i + 1);
|
||||
} else {
|
||||
firstName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" +
|
||||
"id='" + id + '\'' +
|
||||
", username='" + username + '\'' +
|
||||
", firstName='" + firstName + '\'' +
|
||||
", lastName='" + lastName + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", token='" + token + '\'' +
|
||||
", identityProviderId='" + identityProviderId + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HardcodedRoleMapper extends AbstractIdentityProviderMapper {
|
||||
public static final String ROLE = "role";
|
||||
protected static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(ROLE);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role to grant to user. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String[] parseRole(String role) {
|
||||
int scopeIndex = role.indexOf('.');
|
||||
if (scopeIndex > -1) {
|
||||
String appName = role.substring(0, scopeIndex);
|
||||
role = role.substring(scopeIndex + 1);
|
||||
String[] rtn = {appName, role};
|
||||
return rtn;
|
||||
} else {
|
||||
String[] rtn = {null, role};
|
||||
return rtn;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static RoleModel getRoleFromString(RealmModel realm, String roleName) {
|
||||
String[] parsedRole = parseRole(roleName);
|
||||
RoleModel role = null;
|
||||
if (parsedRole[0] == null) {
|
||||
role = realm.getRole(parsedRole[1]);
|
||||
} else {
|
||||
ClientModel client = realm.getClientByClientId(parsedRole[0]);
|
||||
role = client.getRole(parsedRole[1]);
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Role Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Hardcoded Role";
|
||||
}
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {ANY_PROVIDER};
|
||||
|
||||
|
||||
public static final String PROVIDER_ID = "oidc-hardcoded-role-idp-mapper";
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
return COMPATIBLE_PROVIDERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String roleName = mapperModel.getConfig().get(ROLE);
|
||||
RoleModel role = getRoleFromString(realm, roleName);
|
||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
user.grantRole(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "When user is imported from provider, hardcode a role mapping for it.";
|
||||
}
|
||||
}
|
||||
@@ -18,15 +18,17 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.provider.Provider;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Pedro Igor
|
||||
@@ -38,15 +40,16 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
||||
* This method should be called by provider after the JAXRS callback endpoint has finished authentication
|
||||
* with the remote IDP
|
||||
*
|
||||
* @param userNotes notes to add to the UserSessionModel
|
||||
* @param identityProviderConfig provider config
|
||||
* @param federatedIdentity federated identity
|
||||
* @param code relayState or state parameter used to identity the client session
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public Response authenticated(Map<String, String> userNotes, IdentityProviderModel identityProviderConfig, FederatedIdentity federatedIdentity, String code);
|
||||
public Response authenticated(BrokeredIdentityContext context);
|
||||
}
|
||||
|
||||
void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context);
|
||||
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
|
||||
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, BrokeredIdentityContext context);
|
||||
|
||||
/**
|
||||
* JAXRS callback endpoint for when the remote IDP wants to callback to keycloak.
|
||||
*
|
||||
@@ -58,17 +61,11 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
||||
* <p>Initiates the authentication process by sending an authentication request to an identity provider. This method is called
|
||||
* only once during the authentication.</p>
|
||||
*
|
||||
* <p>Depending on how the authentication is performed, this method may redirect the user to the identity provider for authentication.
|
||||
* In this case, the response would contain a {@link javax.ws.rs.core.Response} that will be used to redirect the user.</p>
|
||||
*
|
||||
* <p>However, if the authentication flow does not require a redirect to the identity provider (eg.: simple challenge/response mechanism), this method may return a response containing
|
||||
* a {@link FederatedIdentity} representing the identity information for an user. In this case, the authentication flow stops.</p>
|
||||
*
|
||||
* @param request The initial authentication request. Contains all the contextual information in order to build an authentication request to the
|
||||
* identity provider.
|
||||
* @return
|
||||
*/
|
||||
Response handleRequest(AuthenticationRequest request);
|
||||
Response performLogin(AuthenticationRequest request);
|
||||
|
||||
/**
|
||||
* <p>Returns a {@link javax.ws.rs.core.Response} containing the token previously stored during the authentication process for a
|
||||
@@ -79,6 +76,8 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
||||
*/
|
||||
Response retrieveToken(FederatedIdentityModel identity);
|
||||
|
||||
void backchannelLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm);
|
||||
|
||||
/**
|
||||
* Called when a Keycloak application initiates a logout through the browser. This is expected to do a logout
|
||||
* with the IDP
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ConfiguredProvider;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface IdentityProviderMapper extends Provider, ProviderFactory<IdentityProviderMapper>,ConfiguredProvider {
|
||||
public static final String ANY_PROVIDER = "*";
|
||||
|
||||
String[] getCompatibleProviders();
|
||||
String getDisplayCategory();
|
||||
String getDisplayType();
|
||||
|
||||
void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
||||
void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
|
||||
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package org.keycloak.broker.provider;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class IdentityProviderMapperSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "identity-provider-mapper";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return IdentityProviderMapper.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return IdentityProviderMapper.class;
|
||||
}
|
||||
|
||||
}
|
||||
Regular → Executable
+98
-11
@@ -1,7 +1,4 @@
|
||||
package org.keycloak.broker.oidc.util;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
package org.keycloak.broker.provider.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -21,14 +18,13 @@ import java.util.zip.GZIPInputStream;
|
||||
*/
|
||||
public class SimpleHttp {
|
||||
|
||||
private static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
private String url;
|
||||
private String method;
|
||||
private Map<String, String> headers;
|
||||
private Map<String, String> params;
|
||||
|
||||
private SimpleHttp(String url, String method) {
|
||||
protected SimpleHttp(String url, String method) {
|
||||
this.url = url;
|
||||
this.method = method;
|
||||
}
|
||||
@@ -57,9 +53,6 @@ public class SimpleHttp {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JsonNode asJson() throws IOException {
|
||||
return mapper.readTree(asString());
|
||||
}
|
||||
|
||||
public String asString() throws IOException {
|
||||
boolean get = method.equals("GET");
|
||||
@@ -126,11 +119,105 @@ public class SimpleHttp {
|
||||
return toString(is);
|
||||
} finally {
|
||||
if (os != null) {
|
||||
os.close();
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (is != null) {
|
||||
is.close();
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.disconnect();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int asStatus() throws IOException {
|
||||
boolean get = method.equals("GET");
|
||||
boolean post = method.equals("POST");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (get) {
|
||||
sb.append(url);
|
||||
}
|
||||
|
||||
if (params != null) {
|
||||
boolean f = true;
|
||||
for (Map.Entry<String, String> p : params.entrySet()) {
|
||||
if (f) {
|
||||
f = false;
|
||||
if (get) {
|
||||
sb.append("?");
|
||||
}
|
||||
} else {
|
||||
sb.append("&");
|
||||
}
|
||||
sb.append(URLEncoder.encode(p.getKey(), "UTF-8"));
|
||||
sb.append("=");
|
||||
sb.append(URLEncoder.encode(p.getValue(), "UTF-8"));
|
||||
}
|
||||
}
|
||||
|
||||
if (get) {
|
||||
url = sb.toString();
|
||||
}
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
OutputStream os = null;
|
||||
InputStream is = null;
|
||||
|
||||
try {
|
||||
connection.setRequestMethod(method);
|
||||
|
||||
if (headers != null) {
|
||||
for (Map.Entry<String, String> h : headers.entrySet()) {
|
||||
connection.setRequestProperty(h.getKey(), h.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (post) {
|
||||
String data = sb.toString();
|
||||
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
connection.setRequestProperty("Content-Length", String.valueOf(data.length()));
|
||||
|
||||
os = connection.getOutputStream();
|
||||
os.write(data.getBytes());
|
||||
} else {
|
||||
connection.setDoOutput(false);
|
||||
}
|
||||
|
||||
is = connection.getInputStream();
|
||||
return connection.getResponseCode();
|
||||
} finally {
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
if (connection != null) {
|
||||
try {
|
||||
connection.disconnect();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
broker/core/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
Executable
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.broker.provider.HardcodedRoleMapper
|
||||
@@ -1 +1,2 @@
|
||||
org.keycloak.broker.provider.IdentityProviderSpi
|
||||
org.keycloak.broker.provider.IdentityProviderSpi
|
||||
org.keycloak.broker.provider.IdentityProviderMapperSpi
|
||||
+6
-5
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -18,12 +18,10 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-broker-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
@@ -38,13 +36,11 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-events-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -52,6 +48,11 @@
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
+41
-19
@@ -22,10 +22,10 @@ import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.broker.oidc.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.FederatedIdentity;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
@@ -33,8 +33,8 @@ import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.QueryParam;
|
||||
@@ -45,8 +45,6 @@ import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -87,7 +85,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response handleRequest(AuthenticationRequest request) {
|
||||
public Response performLogin(AuthenticationRequest request) {
|
||||
try {
|
||||
URI authorizationUrl = createAuthorizationUrl(request).build();
|
||||
|
||||
@@ -108,11 +106,22 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
}
|
||||
|
||||
protected String extractTokenFromResponse(String response, String tokenName) {
|
||||
if(response == null)
|
||||
return null;
|
||||
|
||||
if (response.startsWith("{")) {
|
||||
try {
|
||||
return mapper.readTree(response).get(tokenName).getTextValue();
|
||||
JsonNode node = mapper.readTree(response);
|
||||
if(node.has(tokenName)){
|
||||
String s = node.get(tokenName).getTextValue();
|
||||
if(s == null || s.trim().isEmpty())
|
||||
return null;
|
||||
return s;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IdentityBrokerException("Could not extract token [" + tokenName + "] from response [" + response + "].", e);
|
||||
throw new IdentityBrokerException("Could not extract token [" + tokenName + "] from response [" + response + "] due: " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
Matcher matcher = Pattern.compile(tokenName + "=([^&]+)").matcher(response);
|
||||
@@ -125,22 +134,21 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
return null;
|
||||
}
|
||||
|
||||
protected FederatedIdentity getFederatedIdentity(Map<String, String> notes, String response) {
|
||||
protected BrokeredIdentityContext getFederatedIdentity(String response) {
|
||||
String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
|
||||
|
||||
if (accessToken == null) {
|
||||
throw new IdentityBrokerException("No access token from server.");
|
||||
throw new IdentityBrokerException("No access token available in OAuth server response: " + response);
|
||||
}
|
||||
|
||||
return doGetFederatedIdentity(accessToken);
|
||||
}
|
||||
|
||||
|
||||
protected FederatedIdentity doGetFederatedIdentity(String accessToken) {
|
||||
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
|
||||
return UriBuilder.fromUri(getConfig().getAuthorizationUrl())
|
||||
@@ -151,9 +159,20 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
.queryParam(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get JSON property as text. JSON numbers and booleans are converted to text. Empty string is converted to null.
|
||||
*
|
||||
* @param jsonNode to get property from
|
||||
* @param name of property to get
|
||||
* @return string value of the property or null.
|
||||
*/
|
||||
protected String getJsonProperty(JsonNode jsonNode, String name) {
|
||||
if (jsonNode.has(name)) {
|
||||
return jsonNode.get(name).asText();
|
||||
if (jsonNode.has(name) && !jsonNode.get(name).isNull()) {
|
||||
String s = jsonNode.get(name).asText();
|
||||
if(s != null && !s.isEmpty())
|
||||
return s;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -196,7 +215,7 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
//logger.error("Failed " + getConfig().getAlias() + " broker login: " + error);
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(error);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -204,21 +223,24 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||
if (authorizationCode != null) {
|
||||
String response = generateTokenRequest(authorizationCode).asString();
|
||||
|
||||
HashMap<String, String> userNotes = new HashMap<String, String>();
|
||||
FederatedIdentity federatedIdentity = getFederatedIdentity(userNotes, response);
|
||||
BrokeredIdentityContext federatedIdentity = getFederatedIdentity(response);
|
||||
|
||||
if (getConfig().isStoreToken()) {
|
||||
federatedIdentity.setToken(response);
|
||||
}
|
||||
|
||||
return callback.authenticated(userNotes, getConfig(), federatedIdentity, state);
|
||||
federatedIdentity.setCode(state);
|
||||
federatedIdentity.setIdpConfig(getConfig());
|
||||
federatedIdentity.setIdp(AbstractOAuth2IdentityProvider.this);
|
||||
|
||||
return callback.authenticated(federatedIdentity);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to make identity provider oauth callback", e);
|
||||
}
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.IDENTITY_PROVIDER_LOGIN_FAILURE);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
|
||||
public SimpleHttp generateTokenRequest(String authorizationCode) {
|
||||
|
||||
+20
-6
@@ -1,17 +1,18 @@
|
||||
package org.keycloak.broker.oidc;
|
||||
|
||||
import org.keycloak.broker.oidc.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.constants.AdapterConstants;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
import org.keycloak.representations.adapters.action.AdminAction;
|
||||
import org.keycloak.representations.adapters.action.LogoutAction;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.PemUtils;
|
||||
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
@@ -25,6 +26,8 @@ import java.security.PublicKey;
|
||||
*/
|
||||
public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
|
||||
|
||||
public static final String VALIDATED_ACCESS_TOKEN = "VALIDATED_ACCESS_TOKEN";
|
||||
|
||||
public KeycloakOIDCIdentityProvider(OIDCIdentityProviderConfig config) {
|
||||
super(config);
|
||||
}
|
||||
@@ -34,6 +37,12 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
|
||||
return new KeycloakEndpoint(callback, realm, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processAccessTokenResponse(BrokeredIdentityContext context, PublicKey idpKey, AccessTokenResponse response) {
|
||||
JsonWebToken access = validateToken(idpKey, response.getToken());
|
||||
context.getContextData().put(VALIDATED_ACCESS_TOKEN, access);
|
||||
}
|
||||
|
||||
protected class KeycloakEndpoint extends OIDCEndpoint {
|
||||
public KeycloakEndpoint(AuthenticationCallback callback, RealmModel realm, EventBuilder event) {
|
||||
super(callback, realm, event);
|
||||
@@ -61,8 +70,11 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
|
||||
for (String sessionId : action.getKeycloakSessionIds()) {
|
||||
String brokerSessionId = getConfig().getAlias() + "." + sessionId;
|
||||
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
|
||||
if (userSession != null) {
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||
if (userSession != null
|
||||
&& userSession.getState() != UserSessionModel.State.LOGGING_OUT
|
||||
&& userSession.getState() != UserSessionModel.State.LOGGED_OUT
|
||||
) {
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,11 +102,13 @@ public class KeycloakOIDCIdentityProvider extends OIDCIdentityProvider {
|
||||
@Override
|
||||
public SimpleHttp generateTokenRequest(String authorizationCode) {
|
||||
return super.generateTokenRequest(authorizationCode)
|
||||
.param(AdapterConstants.APPLICATION_SESSION_STATE, "n/a"); // hack to get backchannel logout to work
|
||||
.param(AdapterConstants.CLIENT_SESSION_STATE, "n/a"); // hack to get backchannel logout to work
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-3
@@ -19,10 +19,7 @@ package org.keycloak.broker.oidc;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@@ -19,25 +19,27 @@ package org.keycloak.broker.oidc;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.RSATokenVerifier;
|
||||
import org.keycloak.broker.oidc.util.SimpleHttp;
|
||||
import org.keycloak.broker.oidc.util.JsonSimpleHttp;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.FederatedIdentity;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.representations.AccessTokenResponse;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.IdentityBrokerService;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.PemUtils;
|
||||
|
||||
@@ -50,7 +52,6 @@ import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Pedro Igor
|
||||
@@ -61,6 +62,9 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||
public static final String OAUTH2_PARAMETER_PROMPT = "prompt";
|
||||
public static final String SCOPE_OPENID = "openid";
|
||||
public static final String FEDERATED_ID_TOKEN = "FEDERATED_ID_TOKEN";
|
||||
public static final String USER_INFO = "UserInfo";
|
||||
public static final String FEDERATED_ACCESS_TOKEN_RESPONSE = "FEDERATED_ACCESS_TOKEN_RESPONSE";
|
||||
public static final String VALIDATED_ID_TOKEN = "VALIDATED_ID_TOKEN";
|
||||
|
||||
public OIDCIdentityProvider(OIDCIdentityProviderConfig config) {
|
||||
super(config);
|
||||
@@ -92,13 +96,6 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||
|
||||
}
|
||||
|
||||
protected boolean verify(JWSInput jws, PublicKey key) {
|
||||
if (key == null) return true;
|
||||
if (!getConfig().isValidateSignature()) return true;
|
||||
return RSAProvider.verify(jws, key);
|
||||
|
||||
}
|
||||
|
||||
protected class OIDCEndpoint extends Endpoint {
|
||||
public OIDCEndpoint(AuthenticationCallback callback, RealmModel realm, EventBuilder event) {
|
||||
super(callback, realm, event);
|
||||
@@ -116,33 +113,66 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||
EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
|
||||
logger.error("usersession in different state");
|
||||
EventBuilder event = new EventBuilder(realm, session, clientConnection);
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
|
||||
return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
|
||||
}
|
||||
return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void backchannelLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
if (getConfig().getLogoutUrl() == null || getConfig().getLogoutUrl().trim().equals("") || !getConfig().isBackchannelSupported()) return;
|
||||
String idToken = userSession.getNote(FEDERATED_ID_TOKEN);
|
||||
if (idToken == null) return;
|
||||
backchannelLogout(userSession, idToken);
|
||||
}
|
||||
|
||||
protected void backchannelLogout(UserSessionModel userSession, String idToken) {
|
||||
String sessionId = userSession.getId();
|
||||
UriBuilder logoutUri = UriBuilder.fromUri(getConfig().getLogoutUrl())
|
||||
.queryParam("state", sessionId);
|
||||
logoutUri.queryParam("id_token_hint", idToken);
|
||||
String url = logoutUri.build().toString();
|
||||
try {
|
||||
int status = JsonSimpleHttp.doGet(url).asStatus();
|
||||
boolean success = status >=200 && status < 400;
|
||||
if (!success) {
|
||||
logger.warn("Failed backchannel broker logout to: " + url);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed backchannel broker logout to: " + url, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
if (getConfig().getLogoutUrl() == null || getConfig().getLogoutUrl().trim().equals("")) return null;
|
||||
UriBuilder logoutUri = UriBuilder.fromUri(getConfig().getLogoutUrl())
|
||||
.queryParam("state", userSession.getId());
|
||||
String idToken = userSession.getNote(FEDERATED_ID_TOKEN);
|
||||
if (idToken != null) logoutUri.queryParam("id_token_hint", idToken);
|
||||
String redirect = RealmsResource.brokerUrl(uriInfo)
|
||||
.path(IdentityBrokerService.class, "getEndpoint")
|
||||
.path(OIDCEndpoint.class, "logoutResponse")
|
||||
.build(realm.getName(), getConfig().getAlias()).toString();
|
||||
logoutUri.queryParam("post_logout_redirect_uri", redirect);
|
||||
return Response.status(302).location(logoutUri.build()).build();
|
||||
if (idToken != null && getConfig().isBackchannelSupported()) {
|
||||
backchannelLogout(userSession, idToken);
|
||||
return null;
|
||||
} else {
|
||||
String sessionId = userSession.getId();
|
||||
UriBuilder logoutUri = UriBuilder.fromUri(getConfig().getLogoutUrl())
|
||||
.queryParam("state", sessionId);
|
||||
if (idToken != null) logoutUri.queryParam("id_token_hint", idToken);
|
||||
String redirect = RealmsResource.brokerUrl(uriInfo)
|
||||
.path(IdentityBrokerService.class, "getEndpoint")
|
||||
.path(OIDCEndpoint.class, "logoutResponse")
|
||||
.build(realm.getName(), getConfig().getAlias()).toString();
|
||||
logoutUri.queryParam("post_logout_redirect_uri", redirect);
|
||||
Response response = Response.status(302).location(logoutUri.build()).build();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -157,8 +187,12 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||
return authorizationUrl;
|
||||
}
|
||||
|
||||
protected void processAccessTokenResponse(BrokeredIdentityContext context, PublicKey idpKey, AccessTokenResponse response) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FederatedIdentity getFederatedIdentity(Map<String, String> notes, String response) {
|
||||
protected BrokeredIdentityContext getFederatedIdentity(String response) {
|
||||
AccessTokenResponse tokenResponse = null;
|
||||
try {
|
||||
tokenResponse = JsonSerialization.readValue(response, AccessTokenResponse.class);
|
||||
@@ -170,32 +204,31 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||
|
||||
String encodedIdToken = tokenResponse.getIdToken();
|
||||
|
||||
notes.put(FEDERATED_ACCESS_TOKEN, accessToken);
|
||||
notes.put(FEDERATED_ID_TOKEN, encodedIdToken);
|
||||
notes.put(FEDERATED_REFRESH_TOKEN, tokenResponse.getRefreshToken());
|
||||
notes.put(FEDERATED_TOKEN_EXPIRATION, Long.toString(tokenResponse.getExpiresIn()));
|
||||
|
||||
|
||||
IDToken idToken = validateIdToken(key, encodedIdToken);
|
||||
JsonWebToken idToken = validateToken(key, encodedIdToken);
|
||||
|
||||
try {
|
||||
String id = idToken.getSubject();
|
||||
String name = idToken.getName();
|
||||
String preferredUsername = idToken.getPreferredUsername();
|
||||
String email = idToken.getEmail();
|
||||
BrokeredIdentityContext identity = new BrokeredIdentityContext(id);
|
||||
String name = (String)idToken.getOtherClaims().get(IDToken.NAME);
|
||||
String preferredUsername = (String)idToken.getOtherClaims().get(IDToken.PREFERRED_USERNAME);
|
||||
String email = (String)idToken.getOtherClaims().get(IDToken.EMAIL);
|
||||
|
||||
if (id == null || name == null || preferredUsername == null || email == null && getConfig().getUserInfoUrl() != null) {
|
||||
JsonNode userInfo = SimpleHttp.doGet(getConfig().getUserInfoUrl())
|
||||
.header("Authorization", "Bearer " + accessToken)
|
||||
.asJson();
|
||||
if (getConfig().getUserInfoUrl() != null && (id == null || name == null || preferredUsername == null || email == null) ) {
|
||||
SimpleHttp request = JsonSimpleHttp.doGet(getConfig().getUserInfoUrl())
|
||||
.header("Authorization", "Bearer " + accessToken);
|
||||
JsonNode userInfo = JsonSimpleHttp.asJson(request);
|
||||
|
||||
id = getJsonProperty(userInfo, "sub");
|
||||
name = getJsonProperty(userInfo, "name");
|
||||
preferredUsername = getJsonProperty(userInfo, "preferred_username");
|
||||
email = getJsonProperty(userInfo, "email");
|
||||
identity.getContextData().put(USER_INFO, userInfo);
|
||||
}
|
||||
|
||||
FederatedIdentity identity = new FederatedIdentity(id);
|
||||
identity.getContextData().put(FEDERATED_ACCESS_TOKEN_RESPONSE, tokenResponse);
|
||||
identity.getContextData().put(VALIDATED_ID_TOKEN, idToken);
|
||||
processAccessTokenResponse(identity, key, tokenResponse);
|
||||
|
||||
identity.setId(id);
|
||||
identity.setName(name);
|
||||
@@ -235,23 +268,34 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
private IDToken validateIdToken(PublicKey key, String encodedToken) {
|
||||
protected boolean verify(JWSInput jws, PublicKey key) {
|
||||
if (key == null) return true;
|
||||
if (!getConfig().isValidateSignature()) return true;
|
||||
return RSAProvider.verify(jws, key);
|
||||
|
||||
}
|
||||
|
||||
protected JsonWebToken validateToken(PublicKey key, String encodedToken) {
|
||||
if (encodedToken == null) {
|
||||
throw new IdentityBrokerException("No id_token from server.");
|
||||
throw new IdentityBrokerException("No token from server.");
|
||||
}
|
||||
|
||||
try {
|
||||
JWSInput jws = new JWSInput(encodedToken);
|
||||
if (!verify(jws, key)) {
|
||||
throw new IdentityBrokerException("IDToken signature validation failed");
|
||||
throw new IdentityBrokerException("token signature validation failed");
|
||||
}
|
||||
IDToken idToken = jws.readJsonContent(IDToken.class);
|
||||
JsonWebToken token = jws.readJsonContent(JsonWebToken.class);
|
||||
|
||||
String aud = idToken.getAudience();
|
||||
String iss = idToken.getIssuer();
|
||||
String aud = token.getAudience();
|
||||
String iss = token.getIssuer();
|
||||
|
||||
if (aud != null && !aud.equals(getConfig().getClientId())) {
|
||||
throw new IdentityBrokerException("Wrong audience from id_token..");
|
||||
throw new IdentityBrokerException("Wrong audience from token.");
|
||||
}
|
||||
|
||||
if (!token.isActive()) {
|
||||
throw new IdentityBrokerException("Token is no longer valid");
|
||||
}
|
||||
|
||||
String trustedIssuers = getConfig().getIssuer();
|
||||
@@ -261,18 +305,25 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
|
||||
|
||||
for (String trustedIssuer : issuers) {
|
||||
if (iss != null && iss.equals(trustedIssuer.trim())) {
|
||||
return idToken;
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IdentityBrokerException("Wrong issuer from id_token. Got: " + iss + " expected: " + getConfig().getIssuer());
|
||||
throw new IdentityBrokerException("Wrong issuer from token. Got: " + iss + " expected: " + getConfig().getIssuer());
|
||||
}
|
||||
return idToken;
|
||||
return token;
|
||||
} catch (IOException e) {
|
||||
throw new IdentityBrokerException("Could not decode id token.", e);
|
||||
throw new IdentityBrokerException("Could not decode token.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context) {
|
||||
AccessTokenResponse tokenResponse = (AccessTokenResponse)context.getContextData().get(FEDERATED_ACCESS_TOKEN_RESPONSE);
|
||||
userSession.setNote(FEDERATED_ACCESS_TOKEN, tokenResponse.getToken());
|
||||
userSession.setNote(FEDERATED_ID_TOKEN, tokenResponse.getIdToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultScopes() {
|
||||
return "openid";
|
||||
|
||||
@@ -70,6 +70,14 @@ public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
|
||||
getConfig().put("validateSignature", String.valueOf(validateSignature));
|
||||
}
|
||||
|
||||
public boolean isBackchannelSupported() {
|
||||
return Boolean.valueOf(getConfig().get("backchannelSupported"));
|
||||
}
|
||||
|
||||
public void setBackchannelSupported(boolean backchannel) {
|
||||
getConfig().put("backchannelSupported", String.valueOf(backchannel));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
*/
|
||||
package org.keycloak.broker.oidc;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.keycloak.broker.oidc.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.jose.jwk.JWK;
|
||||
import org.keycloak.jose.jwk.JWKParser;
|
||||
@@ -27,7 +26,6 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.protocol.oidc.representations.JSONWebKeySet;
|
||||
import org.keycloak.protocol.oidc.representations.OIDCConfigurationRepresentation;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.PemUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -82,7 +80,7 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||
JSONWebKeySet keySet = JsonSerialization.readValue(keySetString, JSONWebKeySet.class);
|
||||
for (JWK jwk : keySet.getKeys()) {
|
||||
JWKParser parse = JWKParser.create(jwk);
|
||||
if (parse.getJwk().getPublicKeyUse().equals(JWK.SIG_USE)) {
|
||||
if (parse.getJwk().getPublicKeyUse().equals(JWK.SIG_USE) && keyTypeSupported(jwk.getKeyType())) {
|
||||
PublicKey key = parse.toPublicKey();
|
||||
config.setPublicKeySignatureVerifier(KeycloakModelUtils.getPemFromKey(key));
|
||||
config.setValidateSignature(true);
|
||||
@@ -97,4 +95,8 @@ public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||
}
|
||||
return config.getConfig();
|
||||
}
|
||||
|
||||
protected static boolean keyTypeSupported(String type) {
|
||||
return type != null && type.equals("RSA");
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
package org.keycloak.broker.oidc.mappers;
|
||||
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public abstract class AbstractClaimMapper extends AbstractIdentityProviderMapper {
|
||||
public static final String CLAIM = "claim";
|
||||
public static final String CLAIM_VALUE = "claim.value";
|
||||
|
||||
public static Object getClaimValue(JsonWebToken token, String claim) {
|
||||
String[] split = claim.split("\\.");
|
||||
Map<String, Object> jsonObject = token.getOtherClaims();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
if (i == split.length - 1) {
|
||||
return jsonObject.get(split[i]);
|
||||
} else {
|
||||
Object val = jsonObject.get(split[i]);
|
||||
if (!(val instanceof Map)) return null;
|
||||
jsonObject = (Map<String, Object>)val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Object getClaimValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String claim = mapperModel.getConfig().get(CLAIM);
|
||||
{ // search access token
|
||||
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
|
||||
if (token != null) {
|
||||
Object value = getClaimValue(token, claim);
|
||||
if (value != null) return value;
|
||||
}
|
||||
|
||||
}
|
||||
{ // search ID Token
|
||||
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ID_TOKEN);
|
||||
if (token != null) {
|
||||
Object value = getClaimValue(token, claim);
|
||||
if (value != null) return value;
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected boolean hasClaimValue(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
Object value = getClaimValue(mapperModel, context);
|
||||
String desiredValue = mapperModel.getConfig().get(CLAIM_VALUE);
|
||||
return valueEquals(desiredValue, value);
|
||||
}
|
||||
|
||||
public boolean valueEquals(String desiredValue, Object value) {
|
||||
if (value instanceof String) {
|
||||
if (desiredValue.equals(value)) return true;
|
||||
} else if (value instanceof Double) {
|
||||
try {
|
||||
if (Double.valueOf(desiredValue).equals(value)) return true;
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
} else if (value instanceof Integer) {
|
||||
try {
|
||||
if (Integer.valueOf(desiredValue).equals(value)) return true;
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
} else if (value instanceof Boolean) {
|
||||
try {
|
||||
if (Boolean.valueOf(desiredValue).equals(value)) return true;
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
} else if (value instanceof List) {
|
||||
List list = (List)value;
|
||||
for (Object val : list) {
|
||||
return valueEquals(desiredValue, val);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
package org.keycloak.broker.oidc.mappers;
|
||||
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.HardcodedRoleMapper;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClaimToRoleMapper extends AbstractClaimMapper {
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
ProviderConfigProperty property1;
|
||||
property1 = new ProviderConfigProperty();
|
||||
property1.setName(CLAIM);
|
||||
property1.setLabel("Claim");
|
||||
property1.setHelpText("Name of claim to search for in token. You can reference nested claims using a '.', i.e. 'address.locality'.");
|
||||
property1.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property1);
|
||||
property1 = new ProviderConfigProperty();
|
||||
property1.setName(CLAIM_VALUE);
|
||||
property1.setLabel("Claim Value");
|
||||
property1.setHelpText("Value the claim must have. If the claim is an array, then the value must be contained in the array.");
|
||||
property1.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property1);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(HardcodedRoleMapper.ROLE);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role to grant to user if claim is present. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "oidc-role-idp-mapper";
|
||||
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
return COMPATIBLE_PROVIDERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Role Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Claim to Role";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||
if (hasClaimValue(mapperModel, context)) {
|
||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
user.grantRole(role);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||
if (!hasClaimValue(mapperModel, context)) {
|
||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
user.deleteRoleMapping(role);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "If a claim exists, grant the user the specified realm or application role.";
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+118
@@ -0,0 +1,118 @@
|
||||
package org.keycloak.broker.oidc.mappers;
|
||||
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProvider;
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.HardcodedRoleMapper;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ExternalKeycloakRoleToRoleMapper extends AbstractClaimMapper {
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
private static final String EXTERNAL_ROLE = "external.role";
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
ProviderConfigProperty property1;
|
||||
property1 = new ProviderConfigProperty();
|
||||
property1.setName(EXTERNAL_ROLE);
|
||||
property1.setLabel("External role");
|
||||
property1.setHelpText("External role to check for. To reference an application role the syntax is appname.approle, i.e. myapp.myrole.");
|
||||
property1.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property1);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(HardcodedRoleMapper.ROLE);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role to grant to user if external role is present. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "keycloak-oidc-role-to-role-idp-mapper";
|
||||
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
return COMPATIBLE_PROVIDERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Role Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "External Role to Role";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
RoleModel role = hasRole(realm, mapperModel, context);
|
||||
if (role != null) {
|
||||
user.grantRole(role);
|
||||
}
|
||||
}
|
||||
|
||||
private RoleModel hasRole(RealmModel realm,IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
JsonWebToken token = (JsonWebToken)context.getContextData().get(KeycloakOIDCIdentityProvider.VALIDATED_ACCESS_TOKEN);
|
||||
//if (token == null) return;
|
||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||
String[] parseRole = HardcodedRoleMapper.parseRole(mapperModel.getConfig().get(EXTERNAL_ROLE));
|
||||
String externalRoleName = parseRole[1];
|
||||
String claimName = null;
|
||||
if (parseRole[0] == null) {
|
||||
claimName = "realm_access.roles";
|
||||
} else {
|
||||
claimName = "resource_access." + parseRole[0] + ".roles";
|
||||
}
|
||||
Object claim = getClaimValue(token, claimName);
|
||||
if (valueEquals(externalRoleName, claim)) {
|
||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
return role;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
RoleModel role = hasRole(realm, mapperModel, context);
|
||||
if (role == null) {
|
||||
user.deleteRoleMapping(role);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Looks for an external role in a keycloak access token. If external role exists, grant the user the specified realm or application role.";
|
||||
}
|
||||
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package org.keycloak.broker.oidc.mappers;
|
||||
|
||||
import org.keycloak.broker.oidc.KeycloakOIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.oidc.OIDCIdentityProviderFactory;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserAttributeMapper extends AbstractClaimMapper {
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {KeycloakOIDCIdentityProviderFactory.PROVIDER_ID, OIDCIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
public static final String USER_ATTRIBUTE = "user.attribute";
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
ProviderConfigProperty property1;
|
||||
property1 = new ProviderConfigProperty();
|
||||
property1.setName(CLAIM);
|
||||
property1.setLabel("Claim");
|
||||
property1.setHelpText("Name of claim to search for in token. You can reference nested claims using a '.', i.e. 'address.locality'.");
|
||||
property1.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property1);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(USER_ATTRIBUTE);
|
||||
property.setLabel("User Attribute Name");
|
||||
property.setHelpText("User attribute name to store claim.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "oidc-user-attribute-idp-mapper";
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
return COMPATIBLE_PROVIDERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Attribute Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Attribute Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||
Object value = getClaimValue(mapperModel, context);
|
||||
if (value != null) {
|
||||
user.setAttribute(attribute, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||
Object value = getClaimValue(mapperModel, context);
|
||||
String current = user.getAttribute(attribute);
|
||||
if (value != null && !value.equals(current)) {
|
||||
user.setAttribute(attribute, value.toString());
|
||||
} else if (value == null) {
|
||||
user.removeAttribute(attribute);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Import declared claim if it exists in ID or access token into the specified user attribute.";
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package org.keycloak.broker.oidc.util;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class JsonSimpleHttp extends SimpleHttp {
|
||||
public JsonSimpleHttp(String url, String method) {
|
||||
super(url, method);
|
||||
}
|
||||
|
||||
public static JsonSimpleHttp doGet(String url) {
|
||||
return new JsonSimpleHttp(url, "GET");
|
||||
}
|
||||
|
||||
public static JsonSimpleHttp doPost(String url) {
|
||||
return new JsonSimpleHttp(url, "POST");
|
||||
}
|
||||
|
||||
private static ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
public static JsonNode asJson(SimpleHttp request) throws IOException {
|
||||
return mapper.readTree(request.asString());
|
||||
}
|
||||
|
||||
}
|
||||
broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
Executable
+3
@@ -0,0 +1,3 @@
|
||||
org.keycloak.broker.oidc.mappers.ClaimToRoleMapper
|
||||
org.keycloak.broker.oidc.mappers.ExternalKeycloakRoleToRoleMapper
|
||||
org.keycloak.broker.oidc.mappers.UserAttributeMapper
|
||||
Executable
+133
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* JBoss, Home of Professional Open Source
|
||||
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
|
||||
* as indicated by the @authors tag. All rights reserved.
|
||||
*/
|
||||
package org.keycloak.broker.oidc;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Unit test for {@link AbstractOAuth2IdentityProvider}
|
||||
*
|
||||
* @author Vlastimil Elias (velias at redhat dot com)
|
||||
*/
|
||||
public class AbstractOAuth2IdentityProviderTest {
|
||||
|
||||
@Test
|
||||
public void constructor_defaultScopeHandling(){
|
||||
TestProvider tested = getTested();
|
||||
|
||||
//default scope is set from the provider if not provided in the configuration
|
||||
Assert.assertEquals( tested.getDefaultScopes(), tested.getConfig().getDefaultScope());
|
||||
|
||||
//default scope is preserved if provided in the configuration
|
||||
IdentityProviderModel model = new IdentityProviderModel();
|
||||
OAuth2IdentityProviderConfig config = new OAuth2IdentityProviderConfig(model);
|
||||
config.setDefaultScope("myscope");
|
||||
tested = new TestProvider(config);
|
||||
|
||||
Assert.assertEquals("myscope", tested.getConfig().getDefaultScope());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getJsonProperty_asJsonNode() throws IOException {
|
||||
TestProvider tested = getTested();
|
||||
|
||||
JsonNode jsonNode = tested
|
||||
.asJsonNode("{\"nullone\":null, \"emptyone\":\"\", \"blankone\": \" \", \"withvalue\" : \"my value\", \"withbooleanvalue\" : true, \"withnumbervalue\" : 10}");
|
||||
Assert.assertNull(tested.getJsonProperty(jsonNode, "nonexisting"));
|
||||
Assert.assertNull(tested.getJsonProperty(jsonNode, "nullone"));
|
||||
Assert.assertNull(tested.getJsonProperty(jsonNode, "emptyone"));
|
||||
Assert.assertEquals(" ", tested.getJsonProperty(jsonNode, "blankone"));
|
||||
Assert.assertEquals("my value", tested.getJsonProperty(jsonNode, "withvalue"));
|
||||
Assert.assertEquals("true", tested.getJsonProperty(jsonNode, "withbooleanvalue"));
|
||||
Assert.assertEquals("10", tested.getJsonProperty(jsonNode, "withnumbervalue"));
|
||||
}
|
||||
|
||||
@Test(expected = IdentityBrokerException.class)
|
||||
public void getFederatedIdentity_responseUrlLine_tokenNotFound() {
|
||||
TestProvider tested = getTested();
|
||||
tested.getFederatedIdentity("cosi=sss");
|
||||
}
|
||||
|
||||
@Test(expected = IdentityBrokerException.class)
|
||||
public void getFederatedIdentity_responseJSON_tokenNotFound() {
|
||||
TestProvider tested = getTested();
|
||||
Map<String, String> notes = new HashMap<>();
|
||||
tested.getFederatedIdentity("{\"cosi\":\"sss\"}");
|
||||
}
|
||||
|
||||
@Test(expected = IdentityBrokerException.class)
|
||||
public void getFederatedIdentity_responseJSON_invalidFormat() {
|
||||
TestProvider tested = getTested();
|
||||
Map<String, String> notes = new HashMap<>();
|
||||
tested.getFederatedIdentity("{\"cosi\":\"sss\"");
|
||||
}
|
||||
|
||||
@Test(expected = IdentityBrokerException.class)
|
||||
public void getFederatedIdentity_responseJSON_emptyTokenField() {
|
||||
TestProvider tested = getTested();
|
||||
tested.getFederatedIdentity("{\""
|
||||
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : \"\"}");
|
||||
}
|
||||
|
||||
@Test(expected = IdentityBrokerException.class)
|
||||
public void getFederatedIdentity_responseJSON_nullTokenField() {
|
||||
TestProvider tested = getTested();
|
||||
tested.getFederatedIdentity("{\""
|
||||
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : null}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFederatedIdentity_responseJSON() {
|
||||
TestProvider tested = getTested();
|
||||
BrokeredIdentityContext fi = tested.getFederatedIdentity("{\""
|
||||
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "\" : \"458rt\"}");
|
||||
Assert.assertNotNull(fi);
|
||||
Assert.assertEquals("458rt", fi.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFederatedIdentity_responseUrlLine() {
|
||||
TestProvider tested = getTested();
|
||||
BrokeredIdentityContext fi = tested.getFederatedIdentity("cosi=sss&"
|
||||
+ AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_ACCESS_TOKEN + "=458rtf&kdesi=ss}");
|
||||
Assert.assertNotNull(fi);
|
||||
Assert.assertEquals("458rtf", fi.getId());
|
||||
}
|
||||
|
||||
private TestProvider getTested() {
|
||||
IdentityProviderModel model = new IdentityProviderModel();
|
||||
OAuth2IdentityProviderConfig config = new OAuth2IdentityProviderConfig(model);
|
||||
return new TestProvider(config);
|
||||
}
|
||||
|
||||
private static class TestProvider extends AbstractOAuth2IdentityProvider<OAuth2IdentityProviderConfig> {
|
||||
|
||||
public TestProvider(OAuth2IdentityProviderConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultScopes() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
protected BrokeredIdentityContext doGetFederatedIdentity(String accessToken) {
|
||||
return new BrokeredIdentityContext(accessToken);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
+4
-7
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -18,29 +18,26 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-broker-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-protocol</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.picketlink</groupId>
|
||||
<artifactId>picketlink-federation</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-events-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -3,9 +3,20 @@ package org.keycloak.broker.saml;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.VerificationException;
|
||||
import org.keycloak.broker.provider.FederatedIdentity;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.IdentityProvider;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
||||
import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
|
||||
import org.keycloak.dom.saml.v2.protocol.RequestAbstractType;
|
||||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||
import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
@@ -17,31 +28,24 @@ import org.keycloak.protocol.saml.SAML2LogoutResponseBuilder;
|
||||
import org.keycloak.protocol.saml.SAMLRequestParser;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
import org.keycloak.protocol.saml.SamlProtocolUtils;
|
||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLConstants;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||
import org.keycloak.saml.common.util.DocumentUtil;
|
||||
import org.keycloak.saml.common.util.StaxParserUtil;
|
||||
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
|
||||
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
|
||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
|
||||
import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
|
||||
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
|
||||
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
|
||||
import org.keycloak.saml.processing.web.util.PostBindingUtil;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.picketlink.common.constants.GeneralConstants;
|
||||
import org.picketlink.common.constants.JBossSAMLConstants;
|
||||
import org.picketlink.common.constants.JBossSAMLURIConstants;
|
||||
import org.picketlink.common.exceptions.ConfigurationException;
|
||||
import org.picketlink.common.exceptions.ProcessingException;
|
||||
import org.picketlink.common.util.DocumentUtil;
|
||||
import org.picketlink.common.util.StaxParserUtil;
|
||||
import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
|
||||
import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
|
||||
import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.picketlink.identity.federation.core.util.JAXPValidationUtil;
|
||||
import org.picketlink.identity.federation.core.util.XMLEncryptionUtil;
|
||||
import org.picketlink.identity.federation.core.util.XMLSignatureUtil;
|
||||
import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
|
||||
import org.picketlink.identity.federation.saml.v2.assertion.AuthnStatementType;
|
||||
import org.picketlink.identity.federation.saml.v2.assertion.EncryptedAssertionType;
|
||||
import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
|
||||
import org.picketlink.identity.federation.saml.v2.assertion.SubjectType;
|
||||
import org.picketlink.identity.federation.saml.v2.protocol.LogoutRequestType;
|
||||
import org.picketlink.identity.federation.saml.v2.protocol.RequestAbstractType;
|
||||
import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
|
||||
import org.picketlink.identity.federation.saml.v2.protocol.StatusResponseType;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
@@ -62,9 +66,7 @@ import java.io.IOException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -75,10 +77,14 @@ public class SAMLEndpoint {
|
||||
public static final String SAML_FEDERATED_SESSION_INDEX = "SAML_FEDERATED_SESSION_INDEX";
|
||||
public static final String SAML_FEDERATED_SUBJECT = "SAML_FEDERATED_SUBJECT";
|
||||
public static final String SAML_FEDERATED_SUBJECT_NAMEFORMAT = "SAML_FEDERATED_SUBJECT_NAMEFORMAT";
|
||||
public static final String SAML_LOGIN_RESPONSE = "SAML_LOGIN_RESPONSE";
|
||||
public static final String SAML_ASSERTION = "SAML_ASSERTION";
|
||||
public static final String SAML_AUTHN_STATEMENT = "SAML_AUTHN_STATEMENT";
|
||||
protected RealmModel realm;
|
||||
protected EventBuilder event;
|
||||
protected SAMLIdentityProviderConfig config;
|
||||
protected IdentityProvider.AuthenticationCallback callback;
|
||||
protected SAMLIdentityProvider provider;
|
||||
|
||||
@Context
|
||||
private UriInfo uriInfo;
|
||||
@@ -93,10 +99,11 @@ public class SAMLEndpoint {
|
||||
private HttpHeaders headers;
|
||||
|
||||
|
||||
public SAMLEndpoint(RealmModel realm, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback) {
|
||||
public SAMLEndpoint(RealmModel realm, SAMLIdentityProvider provider, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback) {
|
||||
this.realm = realm;
|
||||
this.config = config;
|
||||
this.callback = callback;
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@GET
|
||||
@@ -130,18 +137,18 @@ public class SAMLEndpoint {
|
||||
if (!checkSsl()) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
|
||||
return ErrorPage.error(session, Messages.HTTPS_REQUIRED);
|
||||
}
|
||||
if (!realm.isEnabled()) {
|
||||
event.event(EventType.LOGIN_ERROR);
|
||||
event.error(Errors.REALM_DISABLED);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
|
||||
return ErrorPage.error(session, Messages.REALM_NOT_ENABLED);
|
||||
}
|
||||
|
||||
if (samlRequest == null && samlResponse == null) {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.INVALID_REQUEST);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST );
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
|
||||
}
|
||||
return null;
|
||||
@@ -177,7 +184,7 @@ public class SAMLEndpoint {
|
||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||
event.detail(Details.REASON, "invalid_destination");
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
if (config.isValidateSignature()) {
|
||||
try {
|
||||
@@ -186,7 +193,7 @@ public class SAMLEndpoint {
|
||||
logger.error("validation failed", e);
|
||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SIGNATURE);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUESTER);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUESTER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +206,7 @@ public class SAMLEndpoint {
|
||||
} else {
|
||||
event.event(EventType.LOGIN);
|
||||
event.error(Errors.INVALID_TOKEN);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||
return ErrorPage.error(session, Messages.INVALID_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +216,7 @@ public class SAMLEndpoint {
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessionByBrokerUserId(realm, brokerUserId);
|
||||
for (UserSessionModel userSession : userSessions) {
|
||||
try {
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
|
||||
} catch (Exception e) {
|
||||
logger.warn("failed to do backchannel logout for userSession", e);
|
||||
}
|
||||
@@ -221,7 +228,7 @@ public class SAMLEndpoint {
|
||||
UserSessionModel userSession = session.sessions().getUserSessionByBrokerSessionId(realm, brokerSessionId);
|
||||
if (userSession != null) {
|
||||
try {
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||
AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, clientConnection, headers, false);
|
||||
} catch (Exception e) {
|
||||
logger.warn("failed to do backchannel logout for userSession", e);
|
||||
}
|
||||
@@ -265,10 +272,11 @@ public class SAMLEndpoint {
|
||||
SubjectType subject = assertion.getSubject();
|
||||
SubjectType.STSubType subType = subject.getSubType();
|
||||
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
||||
Map<String, String> notes = new HashMap<>();
|
||||
notes.put(SAML_FEDERATED_SUBJECT, subjectNameID.getValue());
|
||||
if (subjectNameID.getFormat() != null) notes.put(SAML_FEDERATED_SUBJECT_NAMEFORMAT, subjectNameID.getFormat().toString());
|
||||
FederatedIdentity identity = new FederatedIdentity(subjectNameID.getValue());
|
||||
//Map<String, String> notes = new HashMap<>();
|
||||
BrokeredIdentityContext identity = new BrokeredIdentityContext(subjectNameID.getValue());
|
||||
identity.setCode(relayState);
|
||||
identity.getContextData().put(SAML_LOGIN_RESPONSE, responseType);
|
||||
identity.getContextData().put(SAML_ASSERTION, assertion);
|
||||
|
||||
identity.setUsername(subjectNameID.getValue());
|
||||
|
||||
@@ -284,24 +292,41 @@ public class SAMLEndpoint {
|
||||
for (Object statement : assertion.getStatements()) {
|
||||
if (statement instanceof AuthnStatementType) {
|
||||
authn = (AuthnStatementType)statement;
|
||||
identity.getContextData().put(SAML_AUTHN_STATEMENT, authn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (assertion.getAttributeStatements() != null ) {
|
||||
for (AttributeStatementType attrStatement : assertion.getAttributeStatements()) {
|
||||
for (AttributeStatementType.ASTChoiceType choice : attrStatement.getAttributes()) {
|
||||
AttributeType attribute = choice.getAttribute();
|
||||
if (X500SAMLProfileConstants.EMAIL.getFriendlyName().equals(attribute.getFriendlyName())
|
||||
|| X500SAMLProfileConstants.EMAIL.get().equals(attribute.getName())) {
|
||||
if (!attribute.getAttributeValue().isEmpty()) identity.setEmail(attribute.getAttributeValue().get(0).toString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
String brokerUserId = config.getAlias() + "." + subjectNameID.getValue();
|
||||
identity.setBrokerUserId(brokerUserId);
|
||||
identity.setIdpConfig(config);
|
||||
identity.setIdp(provider);
|
||||
if (authn != null && authn.getSessionIndex() != null) {
|
||||
identity.setBrokerSessionId(identity.getBrokerUserId() + "." + authn.getSessionIndex());
|
||||
notes.put(SAML_FEDERATED_SESSION_INDEX, authn.getSessionIndex());
|
||||
}
|
||||
return callback.authenticated(notes, config, identity, relayState);
|
||||
}
|
||||
|
||||
|
||||
return callback.authenticated(identity);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new IdentityBrokerException("Could not process response from SAML identity provider.", e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private AssertionType getAssertion(ResponseType responseType) throws ProcessingException {
|
||||
List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
|
||||
|
||||
@@ -327,7 +352,7 @@ public class SAMLEndpoint {
|
||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||
event.detail(Details.REASON, "invalid_destination");
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
|
||||
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
|
||||
}
|
||||
if (config.isValidateSignature()) {
|
||||
try {
|
||||
@@ -336,7 +361,7 @@ public class SAMLEndpoint {
|
||||
logger.error("validation failed", e);
|
||||
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||
event.error(Errors.INVALID_SIGNATURE);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
|
||||
return ErrorPage.error(session, Messages.INVALID_FEDERATED_IDENTITY_ACTION);
|
||||
}
|
||||
}
|
||||
if (statusResponse instanceof ResponseType) {
|
||||
@@ -355,20 +380,20 @@ public class SAMLEndpoint {
|
||||
logger.error("no valid user session");
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, relayState);
|
||||
if (userSession == null) {
|
||||
logger.error("no valid user session");
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
return ErrorPage.error(session, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||
}
|
||||
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
|
||||
logger.error("usersession in different state");
|
||||
event.event(EventType.LOGOUT);
|
||||
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
|
||||
return ErrorPage.error(session, Messages.SESSION_NOT_ACTIVE);
|
||||
}
|
||||
return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||
}
|
||||
@@ -420,7 +445,9 @@ public class SAMLEndpoint {
|
||||
}
|
||||
@Override
|
||||
protected SAMLDocumentHolder extractResponseDocument(String response) {
|
||||
return SAMLRequestParser.parseResponsePostBinding(response);
|
||||
byte[] samlBytes = PostBindingUtil.base64Decode(response);
|
||||
String xml = new String(samlBytes);
|
||||
return SAMLRequestParser.parseResponseDocument(samlBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,22 +17,36 @@
|
||||
*/
|
||||
package org.keycloak.broker.saml;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
||||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
|
||||
import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
|
||||
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
|
||||
import org.picketlink.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
@@ -41,17 +55,18 @@ import java.security.PublicKey;
|
||||
* @author Pedro Igor
|
||||
*/
|
||||
public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityProviderConfig> {
|
||||
protected static final Logger logger = Logger.getLogger(SAMLIdentityProvider.class);
|
||||
public SAMLIdentityProvider(SAMLIdentityProviderConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object callback(RealmModel realm, AuthenticationCallback callback, EventBuilder event) {
|
||||
return new SAMLEndpoint(realm, getConfig(), callback);
|
||||
return new SAMLEndpoint(realm, this, getConfig(), callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response handleRequest(AuthenticationRequest request) {
|
||||
public Response performLogin(AuthenticationRequest request) {
|
||||
try {
|
||||
UriInfo uriInfo = request.getUriInfo();
|
||||
RealmModel realm = request.getRealm();
|
||||
@@ -112,30 +127,78 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachUserSession(UserSessionModel userSession, ClientSessionModel clientSession, BrokeredIdentityContext context) {
|
||||
ResponseType responseType = (ResponseType)context.getContextData().get(SAMLEndpoint.SAML_LOGIN_RESPONSE);
|
||||
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
|
||||
SubjectType subject = assertion.getSubject();
|
||||
SubjectType.STSubType subType = subject.getSubType();
|
||||
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
||||
userSession.setNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT, subjectNameID.getValue());
|
||||
if (subjectNameID.getFormat() != null) userSession.setNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT, subjectNameID.getFormat().toString());
|
||||
AuthnStatementType authn = (AuthnStatementType)context.getContextData().get(SAMLEndpoint.SAML_AUTHN_STATEMENT);
|
||||
if (authn != null && authn.getSessionIndex() != null) {
|
||||
userSession.setNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX, authn.getSessionIndex());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response retrieveToken(FederatedIdentityModel identity) {
|
||||
return Response.ok(identity.getToken()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
if (getConfig().getSingleLogoutServiceUrl() == null || getConfig().getSingleLogoutServiceUrl().trim().equals("")) return null;
|
||||
public void backchannelLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
|
||||
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("") || !getConfig().isBackchannelSupported()) return;
|
||||
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
|
||||
try {
|
||||
int status = SimpleHttp.doPost(singleLogoutServiceUrl)
|
||||
.param(GeneralConstants.SAML_REQUEST_KEY, logoutBuilder.postBinding().encoded())
|
||||
.param(GeneralConstants.RELAY_STATE, userSession.getId()).asStatus();
|
||||
boolean success = status >=200 && status < 400;
|
||||
if (!success) {
|
||||
logger.warn("Failed saml backchannel broker logout to: " + singleLogoutServiceUrl);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed saml backchannel broker logout to: " + singleLogoutServiceUrl, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||
String singleLogoutServiceUrl = getConfig().getSingleLogoutServiceUrl();
|
||||
if (singleLogoutServiceUrl == null || singleLogoutServiceUrl.trim().equals("")) return null;
|
||||
|
||||
if (getConfig().isBackchannelSupported()) {
|
||||
backchannelLogout(userSession, uriInfo, realm);
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
SAML2LogoutRequestBuilder logoutBuilder = buildLogoutRequest(userSession, uriInfo, realm, singleLogoutServiceUrl);
|
||||
return logoutBuilder.postBinding().request();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected SAML2LogoutRequestBuilder buildLogoutRequest(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm, String singleLogoutServiceUrl) {
|
||||
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
|
||||
.assertionExpiration(realm.getAccessCodeLifespan())
|
||||
.issuer(getEntityId(uriInfo, realm))
|
||||
.sessionIndex(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SESSION_INDEX))
|
||||
.userPrincipal(userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT), userSession.getNote(SAMLEndpoint.SAML_FEDERATED_SUBJECT_NAMEFORMAT))
|
||||
.destination(getConfig().getSingleLogoutServiceUrl());
|
||||
.destination(singleLogoutServiceUrl)
|
||||
.relayState(userSession.getId());
|
||||
if (getConfig().isWantAuthnRequestsSigned()) {
|
||||
logoutBuilder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
|
||||
.signDocument();
|
||||
}
|
||||
try {
|
||||
return logoutBuilder.relayState(userSession.getId()).postBinding().request();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return logoutBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -110,4 +110,13 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
||||
public void setPostBindingResponse(boolean postBindingResponse) {
|
||||
getConfig().put("postBindingResponse", String.valueOf(postBindingResponse));
|
||||
}
|
||||
|
||||
public boolean isBackchannelSupported() {
|
||||
return Boolean.valueOf(getConfig().get("backchannelSupported"));
|
||||
}
|
||||
|
||||
public void setBackchannelSupported(boolean backchannel) {
|
||||
getConfig().put("backchannelSupported", String.valueOf(backchannel));
|
||||
}
|
||||
|
||||
}
|
||||
+10
-10
@@ -18,17 +18,17 @@
|
||||
package org.keycloak.broker.saml;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
|
||||
import org.keycloak.dom.saml.v2.metadata.EndpointType;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntitiesDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.IDPSSODescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.picketlink.common.constants.JBossSAMLURIConstants;
|
||||
import org.picketlink.common.exceptions.ParsingException;
|
||||
import org.picketlink.common.util.DocumentUtil;
|
||||
import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
|
||||
import org.picketlink.identity.federation.saml.v2.metadata.EndpointType;
|
||||
import org.picketlink.identity.federation.saml.v2.metadata.EntitiesDescriptorType;
|
||||
import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.picketlink.identity.federation.saml.v2.metadata.IDPSSODescriptorType;
|
||||
import org.picketlink.identity.federation.saml.v2.metadata.KeyDescriptorType;
|
||||
import org.picketlink.identity.federation.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||
import org.keycloak.saml.common.util.DocumentUtil;
|
||||
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
+140
@@ -0,0 +1,140 @@
|
||||
package org.keycloak.broker.saml.mappers;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.HardcodedRoleMapper;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.saml.SAMLEndpoint;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AttributeToRoleMapper extends AbstractIdentityProviderMapper {
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
public static final String ATTRIBUTE_NAME = "attribute.name";
|
||||
public static final String ATTRIBUTE_FRIENDLY_NAME = "attribute.friendly.name";
|
||||
public static final String ATTRIBUTE_VALUE = "attribute.value";
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(ATTRIBUTE_NAME);
|
||||
property.setLabel("Attribute Name");
|
||||
property.setHelpText("Name of attribute to search for in assertion. You can leave this blank and specify a friendly name instead.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(ATTRIBUTE_FRIENDLY_NAME);
|
||||
property.setLabel("Friendly Name");
|
||||
property.setHelpText("Friendly name of attribute to search for in assertion. You can leave this blank and specify a name instead.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(ATTRIBUTE_VALUE);
|
||||
property.setLabel("Attribute Value");
|
||||
property.setHelpText("Value the attribute must have. If the attribute is a list, then the value must be contained in the list.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(HardcodedRoleMapper.ROLE);
|
||||
property.setLabel("Role");
|
||||
property.setHelpText("Role to grant to user. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "saml-role-idp-mapper";
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
return COMPATIBLE_PROVIDERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Role Mapper";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "SAML Attribute to Role";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||
if (isAttributePresent(mapperModel, context)) {
|
||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
user.grantRole(role);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isAttributePresent(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String name = mapperModel.getConfig().get(ATTRIBUTE_NAME);
|
||||
if (name != null && name.trim().equals("")) name = null;
|
||||
String friendly = mapperModel.getConfig().get(ATTRIBUTE_FRIENDLY_NAME);
|
||||
if (friendly != null && friendly.trim().equals("")) friendly = null;
|
||||
String desiredValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
|
||||
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
|
||||
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
|
||||
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
|
||||
AttributeType attr = choice.getAttribute();
|
||||
if (name != null && !name.equals(attr.getName())) continue;
|
||||
if (friendly != null && !name.equals(attr.getFriendlyName())) continue;
|
||||
for (Object val : attr.getAttributeValue()) {
|
||||
if (val.equals(desiredValue)) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String roleName = mapperModel.getConfig().get(HardcodedRoleMapper.ROLE);
|
||||
if (!isAttributePresent(mapperModel, context)) {
|
||||
RoleModel role = HardcodedRoleMapper.getRoleFromString(realm, roleName);
|
||||
if (role == null) throw new IdentityBrokerException("Unable to find role: " + roleName);
|
||||
user.deleteRoleMapping(role);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "If a claim exists, grant the user the specified realm or application role.";
|
||||
}
|
||||
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package org.keycloak.broker.saml.mappers;
|
||||
|
||||
import org.keycloak.broker.provider.AbstractIdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.saml.SAMLEndpoint;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderFactory;
|
||||
import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.AttributeType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserAttributeMapper extends AbstractIdentityProviderMapper {
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
|
||||
|
||||
public static final String ATTRIBUTE_NAME = "attribute.name";
|
||||
public static final String ATTRIBUTE_FRIENDLY_NAME = "attribute.friendly.name";
|
||||
public static final String USER_ATTRIBUTE = "user.attribute";
|
||||
|
||||
static {
|
||||
ProviderConfigProperty property;
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(ATTRIBUTE_NAME);
|
||||
property.setLabel("Attribute Name");
|
||||
property.setHelpText("Name of attribute to search for in assertion. You can leave this blank and specify a friendly name instead.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(ATTRIBUTE_FRIENDLY_NAME);
|
||||
property.setLabel("Friendly Name");
|
||||
property.setHelpText("Friendly name of attribute to search for in assertion. You can leave this blank and specify a name instead.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
property = new ProviderConfigProperty();
|
||||
property.setName(USER_ATTRIBUTE);
|
||||
property.setLabel("User Attribute Name");
|
||||
property.setHelpText("User attribute name to store saml attribute.");
|
||||
property.setType(ProviderConfigProperty.STRING_TYPE);
|
||||
configProperties.add(property);
|
||||
}
|
||||
|
||||
public static final String PROVIDER_ID = "saml-user-attribute-idp-mapper";
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return configProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getCompatibleProviders() {
|
||||
return COMPATIBLE_PROVIDERS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayCategory() {
|
||||
return "Attribute Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Attribute Importer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||
Object value = getAttribute(mapperModel, context);
|
||||
if (value != null) {
|
||||
user.setAttribute(attribute, value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected String getAttribute(IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String name = mapperModel.getConfig().get(ATTRIBUTE_NAME);
|
||||
if (name != null && name.trim().equals("")) name = null;
|
||||
String friendly = mapperModel.getConfig().get(ATTRIBUTE_FRIENDLY_NAME);
|
||||
if (friendly != null && friendly.trim().equals("")) friendly = null;
|
||||
AssertionType assertion = (AssertionType)context.getContextData().get(SAMLEndpoint.SAML_ASSERTION);
|
||||
for (AttributeStatementType statement : assertion.getAttributeStatements()) {
|
||||
for (AttributeStatementType.ASTChoiceType choice : statement.getAttributes()) {
|
||||
AttributeType attr = choice.getAttribute();
|
||||
if (name != null && !name.equals(attr.getName())) continue;
|
||||
if (friendly != null && !name.equals(attr.getFriendlyName())) continue;
|
||||
|
||||
List<Object> attributeValue = attr.getAttributeValue();
|
||||
if (attributeValue == null || attributeValue.isEmpty()) return null;
|
||||
return attributeValue.get(0).toString();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
|
||||
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
|
||||
Object value = getAttribute(mapperModel, context);
|
||||
String current = user.getAttribute(attribute);
|
||||
if (value != null && !value.equals(current)) {
|
||||
user.setAttribute(attribute, value.toString());
|
||||
} else if (value == null) {
|
||||
user.removeAttribute(attribute);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Import declared saml attribute if it exists in assertion into the specified user attribute.";
|
||||
}
|
||||
|
||||
}
|
||||
broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderMapper
Executable
+2
@@ -0,0 +1,2 @@
|
||||
org.keycloak.broker.saml.mappers.AttributeToRoleMapper
|
||||
org.keycloak.broker.saml.mappers.UserAttributeMapper
|
||||
Regular → Executable
+1
-5
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -17,22 +17,18 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-export-import-single-file</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.jackson</groupId>
|
||||
|
||||
+21
-2
@@ -24,6 +24,8 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.codehaus.jackson.JsonToken;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.exportimport.Strategy;
|
||||
@@ -84,7 +86,22 @@ public class DefaultFileConnectionProviderFactory implements FileConnectionProvi
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
fis = new FileInputStream(kcdata);
|
||||
Model model = JsonSerialization.readValue(fis, Model.class);
|
||||
ImportUtils.importFromStream(session, JsonSerialization.mapper, fis, Strategy.IGNORE_EXISTING);
|
||||
session.realms().getMigrationModel().setStoredVersion(model.getModelVersion());
|
||||
|
||||
List<RealmRepresentation> realmReps = new ArrayList<RealmRepresentation>();
|
||||
for (RealmRepresentation realmRep : model.getRealms()) {
|
||||
if (Config.getAdminRealm().equals(realmRep.getRealm())) {
|
||||
realmReps.add(0, realmRep);
|
||||
} else {
|
||||
realmReps.add(realmRep);
|
||||
}
|
||||
}
|
||||
for (RealmRepresentation realmRep : realmReps) {
|
||||
ImportUtils.importRealm(session, realmRep, Strategy.IGNORE_EXISTING);
|
||||
}
|
||||
|
||||
} catch (IOException ioe) {
|
||||
logger.error("Unable to read model file " + kcdata.getAbsolutePath(), ioe);
|
||||
} finally {
|
||||
@@ -128,8 +145,10 @@ public class DefaultFileConnectionProviderFactory implements FileConnectionProvi
|
||||
for (RealmModel realm : realms) {
|
||||
reps.add(ExportUtils.exportRealm(session, realm, true));
|
||||
}
|
||||
|
||||
JsonSerialization.prettyMapper.writeValue(outStream, reps);
|
||||
Model model = new Model();
|
||||
model.setRealms(reps);
|
||||
model.setModelVersion(session.realms().getMigrationModel().getStoredVersion());
|
||||
JsonSerialization.prettyMapper.writeValue(outStream, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Regular → Executable
+10
@@ -37,6 +37,8 @@ public class InMemoryModel {
|
||||
// realmId, userId, userModel
|
||||
private final Map<String, Map<String,UserModel>> allUsers = new HashMap<String, Map<String,UserModel>>();
|
||||
|
||||
private String modelVersion;
|
||||
|
||||
public InMemoryModel() {
|
||||
}
|
||||
|
||||
@@ -45,6 +47,14 @@ public class InMemoryModel {
|
||||
allUsers.put(id, new HashMap<String, UserModel>());
|
||||
}
|
||||
|
||||
public String getModelVersion() {
|
||||
return modelVersion;
|
||||
}
|
||||
|
||||
public void setModelVersion(String modelVersion) {
|
||||
this.modelVersion = modelVersion;
|
||||
}
|
||||
|
||||
public RealmModel getRealm(String id) {
|
||||
return allRealms.get(id);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.keycloak.connections.file;
|
||||
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class Model {
|
||||
private String modelVersion;
|
||||
private List<RealmRepresentation> realms;
|
||||
|
||||
public String getModelVersion() {
|
||||
return modelVersion;
|
||||
}
|
||||
|
||||
public void setModelVersion(String modelVersion) {
|
||||
this.modelVersion = modelVersion;
|
||||
}
|
||||
|
||||
public List<RealmRepresentation> getRealms() {
|
||||
return realms;
|
||||
}
|
||||
|
||||
public void setRealms(List<RealmRepresentation> realms) {
|
||||
this.realms = realms;
|
||||
}
|
||||
}
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
<?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.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-connections-http-client</artifactId>
|
||||
<name>Keycloak Connections Apache HttpClient</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.EntityBuilder;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.util.EnvUtil;
|
||||
import org.keycloak.util.KeystoreUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultHttpClientFactory implements HttpClientFactory {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DefaultHttpClientFactory.class);
|
||||
|
||||
private volatile CloseableHttpClient httpClient;
|
||||
|
||||
@Override
|
||||
public HttpClientProvider create(KeycloakSession session) {
|
||||
return new HttpClientProvider() {
|
||||
@Override
|
||||
public HttpClient getHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int postText(String uri, String text) throws IOException {
|
||||
HttpPost request = new HttpPost(uri);
|
||||
request.setEntity(EntityBuilder.create().setText(text).setContentType(ContentType.TEXT_PLAIN).build());
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
try {
|
||||
return response.getStatusLine().getStatusCode();
|
||||
} finally {
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity != null) {
|
||||
InputStream is = entity.getContent();
|
||||
if (is != null) is.close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream get(String uri) throws IOException {
|
||||
HttpGet request = new HttpGet(uri);
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
HttpEntity entity = response.getEntity();
|
||||
if (entity == null) return null;
|
||||
return entity.getContent();
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
httpClient.close();
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
long socketTimeout = config.getLong("socket-timeout-millis", -1L);
|
||||
long establishConnectionTimeout = config.getLong("establish-connection-timeout-millis", -1L);
|
||||
int maxPooledPerRoute = config.getInt("max-pooled-per-route", 0);
|
||||
int connectionPoolSize = config.getInt("connection-pool-size", 200);
|
||||
boolean disableTrustManager = config.getBoolean("disable-trust-manager", false);
|
||||
boolean disableCookies = config.getBoolean("disable-cookies", true);
|
||||
String hostnameVerificationPolicy = config.get("hostname-verification-policy", "WILDCARD");
|
||||
HttpClientBuilder.HostnameVerificationPolicy hostnamePolicy = HttpClientBuilder.HostnameVerificationPolicy.valueOf(hostnameVerificationPolicy);
|
||||
String truststore = config.get("truststore");
|
||||
String truststorePassword = config.get("truststore-password");
|
||||
String clientKeystore = config.get("client-keystore");
|
||||
String clientKeystorePassword = config.get("client-keystore-password");
|
||||
String clientPrivateKeyPassword = config.get("client-key-password");
|
||||
|
||||
HttpClientBuilder builder = new HttpClientBuilder();
|
||||
builder.socketTimeout(socketTimeout, TimeUnit.MILLISECONDS)
|
||||
.establishConnectionTimeout(establishConnectionTimeout, TimeUnit.MILLISECONDS)
|
||||
.maxPooledPerRoute(maxPooledPerRoute)
|
||||
.connectionPoolSize(connectionPoolSize)
|
||||
.hostnameVerification(hostnamePolicy)
|
||||
.disableCookies(disableCookies);
|
||||
if (disableTrustManager) builder.disableTrustManager();
|
||||
if (truststore != null) {
|
||||
truststore = EnvUtil.replace(truststore);
|
||||
try {
|
||||
builder.trustStore(KeystoreUtil.loadKeyStore(truststore, truststorePassword));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load truststore", e);
|
||||
}
|
||||
}
|
||||
if (clientKeystore != null) {
|
||||
clientKeystore = EnvUtil.replace(clientKeystore);
|
||||
try {
|
||||
KeyStore clientCertKeystore = KeystoreUtil.loadKeyStore(clientKeystore, clientKeystorePassword);
|
||||
builder.keyStore(clientCertKeystore, clientPrivateKeyPassword);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load keystore", e);
|
||||
}
|
||||
}
|
||||
httpClient = builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Executable
+281
@@ -0,0 +1,281 @@
|
||||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.SSLContexts;
|
||||
import org.apache.http.conn.ssl.StrictHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.X509HostnameVerifier;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Abstraction for creating HttpClients. Allows SSL configuration.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HttpClientBuilder {
|
||||
public static enum HostnameVerificationPolicy {
|
||||
/**
|
||||
* Hostname verification is not done on the server's certificate
|
||||
*/
|
||||
ANY,
|
||||
/**
|
||||
* Allows wildcards in subdomain names i.e. *.foo.com
|
||||
*/
|
||||
WILDCARD,
|
||||
/**
|
||||
* CN must match hostname connecting to
|
||||
*/
|
||||
STRICT
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
private static class PassthroughTrustManager implements X509TrustManager {
|
||||
public void checkClientTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected KeyStore truststore;
|
||||
protected KeyStore clientKeyStore;
|
||||
protected String clientPrivateKeyPassword;
|
||||
protected boolean disableTrustManager;
|
||||
protected HostnameVerificationPolicy policy = HostnameVerificationPolicy.WILDCARD;
|
||||
protected SSLContext sslContext;
|
||||
protected int connectionPoolSize = 100;
|
||||
protected int maxPooledPerRoute = 0;
|
||||
protected long connectionTTL = -1;
|
||||
protected TimeUnit connectionTTLUnit = TimeUnit.MILLISECONDS;
|
||||
protected HostnameVerifier verifier = null;
|
||||
protected long socketTimeout = -1;
|
||||
protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS;
|
||||
protected long establishConnectionTimeout = -1;
|
||||
protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS;
|
||||
protected boolean disableCookies = false;
|
||||
|
||||
|
||||
/**
|
||||
* Socket inactivity timeout
|
||||
*
|
||||
* @param timeout
|
||||
* @param unit
|
||||
* @return
|
||||
*/
|
||||
public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit)
|
||||
{
|
||||
this.socketTimeout = timeout;
|
||||
this.socketTimeoutUnits = unit;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* When trying to make an initial socket connection, what is the timeout?
|
||||
*
|
||||
* @param timeout
|
||||
* @param unit
|
||||
* @return
|
||||
*/
|
||||
public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit)
|
||||
{
|
||||
this.establishConnectionTimeout = timeout;
|
||||
this.establishConnectionTimeoutUnits = unit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder connectionTTL(long ttl, TimeUnit unit) {
|
||||
this.connectionTTL = ttl;
|
||||
this.connectionTTLUnit = unit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder maxPooledPerRoute(int maxPooledPerRoute) {
|
||||
this.maxPooledPerRoute = maxPooledPerRoute;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder connectionPoolSize(int connectionPoolSize) {
|
||||
this.connectionPoolSize = connectionPoolSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable trust management and hostname verification. <i>NOTE</i> this is a security
|
||||
* hole, so only set this option if you cannot or do not want to verify the identity of the
|
||||
* host you are communicating with.
|
||||
*/
|
||||
public HttpClientBuilder disableTrustManager() {
|
||||
this.disableTrustManager = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable cookie management.
|
||||
*/
|
||||
public HttpClientBuilder disableCookies(boolean disable) {
|
||||
this.disableTrustManager = disable;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* SSL policy used to verify hostnames
|
||||
*
|
||||
* @param policy
|
||||
* @return
|
||||
*/
|
||||
public HttpClientBuilder hostnameVerification(HostnameVerificationPolicy policy) {
|
||||
this.policy = policy;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public HttpClientBuilder sslContext(SSLContext sslContext) {
|
||||
this.sslContext = sslContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder trustStore(KeyStore truststore) {
|
||||
this.truststore = truststore;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder keyStore(KeyStore keyStore, String password) {
|
||||
this.clientKeyStore = keyStore;
|
||||
this.clientPrivateKeyPassword = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HttpClientBuilder keyStore(KeyStore keyStore, char[] password) {
|
||||
this.clientKeyStore = keyStore;
|
||||
this.clientPrivateKeyPassword = new String(password);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
static class VerifierWrapper implements X509HostnameVerifier {
|
||||
protected HostnameVerifier verifier;
|
||||
|
||||
VerifierWrapper(HostnameVerifier verifier) {
|
||||
this.verifier = verifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(String host, SSLSocket ssl) throws IOException {
|
||||
if (!verifier.verify(host, ssl.getSession())) throw new SSLException("Hostname verification failure");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(String host, X509Certificate cert) throws SSLException {
|
||||
throw new SSLException("This verification path not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
|
||||
throw new SSLException("This verification path not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String s, SSLSession sslSession) {
|
||||
return verifier.verify(s, sslSession);
|
||||
}
|
||||
}
|
||||
|
||||
public CloseableHttpClient build() {
|
||||
X509HostnameVerifier verifier = null;
|
||||
if (this.verifier != null) verifier = new VerifierWrapper(this.verifier);
|
||||
else {
|
||||
switch (policy) {
|
||||
case ANY:
|
||||
verifier = new AllowAllHostnameVerifier();
|
||||
break;
|
||||
case WILDCARD:
|
||||
verifier = new BrowserCompatHostnameVerifier();
|
||||
break;
|
||||
case STRICT:
|
||||
verifier = new StrictHostnameVerifier();
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
SSLConnectionSocketFactory sslsf = null;
|
||||
SSLContext theContext = sslContext;
|
||||
if (disableTrustManager) {
|
||||
theContext = SSLContext.getInstance("TLS");
|
||||
theContext.init(null, new TrustManager[]{new PassthroughTrustManager()},
|
||||
new SecureRandom());
|
||||
verifier = new AllowAllHostnameVerifier();
|
||||
sslsf = new SSLConnectionSocketFactory(theContext, verifier);
|
||||
} else if (theContext != null) {
|
||||
sslsf = new SSLConnectionSocketFactory(theContext, verifier);
|
||||
} else if (clientKeyStore != null || truststore != null) {
|
||||
theContext = createSslContext("TLS", clientKeyStore, clientPrivateKeyPassword, truststore, null);
|
||||
sslsf = new SSLConnectionSocketFactory(theContext, verifier);
|
||||
} else {
|
||||
final SSLContext tlsContext = SSLContext.getInstance("TLS");
|
||||
tlsContext.init(null, null, null);
|
||||
sslsf = new SSLConnectionSocketFactory(tlsContext, verifier);
|
||||
}
|
||||
RequestConfig requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout((int) establishConnectionTimeout)
|
||||
.setSocketTimeout((int) socketTimeout).build();
|
||||
|
||||
org.apache.http.impl.client.HttpClientBuilder builder = HttpClients.custom()
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.setSSLSocketFactory(sslsf)
|
||||
.setMaxConnTotal(connectionPoolSize)
|
||||
.setMaxConnPerRoute(maxPooledPerRoute);
|
||||
if (disableCookies) builder.disableCookieManagement();
|
||||
return builder.build();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private SSLContext createSslContext(
|
||||
final String algorithm,
|
||||
final KeyStore keystore,
|
||||
final String keyPassword,
|
||||
final KeyStore truststore,
|
||||
final SecureRandom random)
|
||||
throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
|
||||
return SSLContexts.custom()
|
||||
.useProtocol(algorithm)
|
||||
.setSecureRandom(random)
|
||||
.loadKeyMaterial(keystore, keyPassword != null ? keyPassword.toCharArray() : null)
|
||||
.loadTrustMaterial(truststore)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface HttpClientFactory extends ProviderFactory<HttpClientProvider> {
|
||||
}
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public interface HttpClientProvider extends Provider {
|
||||
HttpClient getHttpClient();
|
||||
|
||||
/**
|
||||
* Helper method
|
||||
*
|
||||
* @param uri
|
||||
* @param text
|
||||
* @return http response status
|
||||
* @throws IOException
|
||||
*/
|
||||
public int postText(String uri, String text) throws IOException;
|
||||
|
||||
/**
|
||||
* Helper method
|
||||
*
|
||||
* @param uri
|
||||
* @return response stream, you must close this stream or leaks will happen
|
||||
* @throws IOException
|
||||
*/
|
||||
public InputStream get(String uri) throws IOException;
|
||||
}
|
||||
+7
-5
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.picketlink;
|
||||
package org.keycloak.connections.httpclient;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
@@ -7,19 +7,21 @@ import org.keycloak.provider.Spi;
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class PartitionManagerSpi implements Spi {
|
||||
public class HttpClientSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "picketlink-idm";
|
||||
return "connectionsHttpClient";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return PartitionManagerProvider.class;
|
||||
return HttpClientProvider.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return PartitionManagerProviderFactory.class;
|
||||
return HttpClientFactory.class;
|
||||
}
|
||||
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.connections.httpclient.DefaultHttpClientFactory
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.keycloak.connections.httpclient.HttpClientSpi
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -17,12 +17,10 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.infinispan</groupId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -17,12 +17,10 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-jpa</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.liquibase</groupId>
|
||||
@@ -38,7 +36,14 @@
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-services</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -59,13 +64,6 @@
|
||||
<referenceUsername>${username}</referenceUsername>
|
||||
<referencePassword>${password}</referencePassword>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>${h2.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
+15
-1
@@ -15,14 +15,18 @@ import liquibase.resource.ResourceAccessor;
|
||||
import liquibase.snapshot.SnapshotGeneratorFactory;
|
||||
import liquibase.statement.SqlStatement;
|
||||
import liquibase.structure.core.Table;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.ThreadLocalSessionContext;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.services.DefaultKeycloakSessionFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public abstract class CustomKeycloakTask implements CustomSqlChange {
|
||||
|
||||
private final Logger logger = Logger.getLogger(getClass());
|
||||
|
||||
protected KeycloakSession kcSession;
|
||||
|
||||
protected Database database;
|
||||
@@ -49,8 +53,18 @@ public abstract class CustomKeycloakTask implements CustomSqlChange {
|
||||
@Override
|
||||
public void setUp() throws SetupException {
|
||||
this.kcSession = ThreadLocalSessionContext.getCurrentSession();
|
||||
|
||||
if (this.kcSession == null) {
|
||||
throw new SetupException("No KeycloakSession provided in ThreadLocal");
|
||||
// Probably running Liquibase from maven plugin. Try to create kcSession programmatically
|
||||
logger.info("No KeycloakSession provided in ThreadLocal. Initializing KeycloakSessionFactory");
|
||||
|
||||
try {
|
||||
DefaultKeycloakSessionFactory factory = new DefaultKeycloakSessionFactory();
|
||||
factory.init();
|
||||
this.kcSession = factory.create();
|
||||
} catch (Exception e) {
|
||||
throw new SetupException("Exception when initializing factory", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package org.keycloak.connections.jpa.updater.liquibase.custom;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
import liquibase.exception.CustomChangeException;
|
||||
import liquibase.statement.core.InsertStatement;
|
||||
import liquibase.structure.core.Table;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class JpaUpdate1_2_0_CR1 extends CustomKeycloakTask {
|
||||
|
||||
@Override
|
||||
protected void generateStatementsImpl() throws CustomChangeException {
|
||||
String realmClientTableName = database.correctObjectName("REALM_CLIENT", Table.class);
|
||||
|
||||
try {
|
||||
PreparedStatement statement = jdbcConnection.prepareStatement("select CLIENT.REALM_ID, CLIENT.ID CLIENT_ID from CLIENT where CLIENT.CONSENT_REQUIRED = true");
|
||||
try {
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
try {
|
||||
while (resultSet.next()) {
|
||||
String realmId = resultSet.getString("REALM_ID");
|
||||
String oauthClientId = resultSet.getString("CLIENT_ID");
|
||||
|
||||
InsertStatement realmClientInsert = new InsertStatement(null, null, realmClientTableName)
|
||||
.addColumnValue("REALM_ID", realmId)
|
||||
.addColumnValue("CLIENT_ID", oauthClientId);
|
||||
statements.add(realmClientInsert);
|
||||
}
|
||||
} finally {
|
||||
resultSet.close();
|
||||
}
|
||||
} finally {
|
||||
statement.close();
|
||||
}
|
||||
|
||||
confirmationMessage.append("Inserted " + statements.size() + " OAuth Clients to REALM_CLIENT table");
|
||||
} catch (Exception e) {
|
||||
throw new CustomChangeException(getTaskId() + ": Exception when updating data from previous version", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTaskId() {
|
||||
return "Update 1.2.0.CR1";
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<changeSet author="bburke@redhat.com" id="1.2.0.RC1">
|
||||
<delete tableName="CLIENT_SESSION_ROLE"/>
|
||||
<delete tableName="CLIENT_SESSION_NOTE"/>
|
||||
<delete tableName="CLIENT_SESSION"/>
|
||||
<delete tableName="USER_SESSION_NOTE"/>
|
||||
<delete tableName="USER_SESSION"/>
|
||||
|
||||
<createTable tableName="MIGRATION_MODEL">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="VERSION" type="VARCHAR(36)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</createTable>
|
||||
|
||||
<createTable tableName="IDENTITY_PROVIDER_MAPPER">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="IDP_ALIAS" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="IDP_MAPPER_NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="REALM_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="IDP_MAPPER_CONFIG">
|
||||
<column name="IDP_MAPPER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="VALUE" type="CLOB"/>
|
||||
<column name="NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addColumn tableName="CREDENTIAL">
|
||||
<column name="CREATED_DATE" type="BIGINT"/>
|
||||
</addColumn>
|
||||
<createTable tableName="USER_CONSENT">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="CLIENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="USER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="USER_CONSENT_ROLE">
|
||||
<column name="USER_CONSENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="ROLE_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="USER_CONSENT_PROT_MAPPER">
|
||||
<column name="USER_CONSENT_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="PROTOCOL_MAPPER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<createTable tableName="CLIENT_SESSION_PROT_MAPPER">
|
||||
<column name="PROTOCOL_MAPPER_ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="CLIENT_SESSION" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</createTable>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_MIGMOD" tableName="MIGRATION_MODEL"/>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_IDPM" tableName="IDENTITY_PROVIDER_MAPPER"/>
|
||||
<addPrimaryKey columnNames="IDP_MAPPER_ID, NAME" constraintName="CONSTRAINT_IDPMConfig" tableName="IDP_MAPPER_CONFIG"/>
|
||||
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_GRNTCSNT_PM" tableName="USER_CONSENT"/>
|
||||
<addPrimaryKey columnNames="USER_CONSENT_ID, ROLE_ID" constraintName="CONSTRAINT_GRNTCSNT_ROLE_PM" tableName="USER_CONSENT_ROLE"/>
|
||||
<addPrimaryKey columnNames="USER_CONSENT_ID, PROTOCOL_MAPPER_ID" constraintName="CONSTRAINT_GRNTCSNT_PRM_PM" tableName="USER_CONSENT_PROT_MAPPER"/>
|
||||
<addPrimaryKey columnNames="CLIENT_SESSION, PROTOCOL_MAPPER_ID" constraintName="CONSTRAINT_CS_PMP_PK" tableName="CLIENT_SESSION_PROT_MAPPER"/>
|
||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER_MAPPER" constraintName="FK_IDPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||
<addForeignKeyConstraint baseColumnNames="IDP_MAPPER_ID" baseTableName="IDP_MAPPER_CONFIG" constraintName="FK_IDPMConfig" referencedColumnNames="ID" referencedTableName="IDENTITY_PROVIDER_MAPPER"/>
|
||||
<addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_CONSENT" constraintName="FK_GRNTCSNT_USER" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
|
||||
<addForeignKeyConstraint baseColumnNames="USER_CONSENT_ID" baseTableName="USER_CONSENT_ROLE" constraintName="FK_GRNTCSNT_ROLE_GR" referencedColumnNames="ID" referencedTableName="USER_CONSENT"/>
|
||||
<addForeignKeyConstraint baseColumnNames="USER_CONSENT_ID" baseTableName="USER_CONSENT_PROT_MAPPER" constraintName="FK_GRNTCSNT_PRM_GR" referencedColumnNames="ID" referencedTableName="USER_CONSENT"/>
|
||||
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_PROT_MAPPER" constraintName="FK_33A8SGQW18I532811V7O2DK89" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
|
||||
|
||||
<renameColumn tableName="CLIENT" newColumnName="CLIENT_ID" oldColumnName="NAME" columnDataType="VARCHAR(255)"/>
|
||||
<addColumn tableName="CLIENT">
|
||||
<column name="CONSENT_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
<column name="NAME" type="VARCHAR(255)" />
|
||||
</addColumn>
|
||||
<addColumn tableName="IDENTITY_PROVIDER">
|
||||
<column name="ADD_TOKEN_ROLE" type="BOOLEAN" defaultValueBoolean="true">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<update tableName="CLIENT">
|
||||
<column name="CONSENT_REQUIRED" valueBoolean="true"/>
|
||||
<where>DTYPE = 'OAuthClientEntity'</where>
|
||||
</update>
|
||||
<dropColumn tableName="CLIENT" columnName="DTYPE"/>
|
||||
|
||||
<dropForeignKeyConstraint baseTableName="REALM" constraintName="FK_RSAF444KK6QRKMS7N56AIWQ5Y" />
|
||||
<renameColumn tableName="REALM" newColumnName="MASTER_ADMIN_CLIENT" oldColumnName="MASTER_ADMIN_APP" columnDataType="VARCHAR(36)"/>
|
||||
<addForeignKeyConstraint baseColumnNames="MASTER_ADMIN_CLIENT" baseTableName="REALM" constraintName="FK_TRAF444KK6QRKMS7N56AIWQ5Y" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
||||
|
||||
<dropForeignKeyConstraint baseTableName="REALM_APPLICATION" constraintName="FK_82S3P0DIUXAWWQQSA528UBY2Q" />
|
||||
<renameTable oldTableName="REALM_APPLICATION" newTableName="REALM_CLIENT"/>
|
||||
<renameColumn tableName="REALM_CLIENT" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID" columnDataType="VARCHAR(36)"/>
|
||||
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="REALM_CLIENT" constraintName="FK_93S3P0DIUXAWWQQSA528UBY2Q" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
||||
|
||||
<dropForeignKeyConstraint baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_MAYLTS7KLWQW2H8M2B5JOYTKY" />
|
||||
<renameTable oldTableName="APPLICATION_DEFAULT_ROLES" newTableName="CLIENT_DEFAULT_ROLES"/>
|
||||
<renameColumn tableName="CLIENT_DEFAULT_ROLES" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID" columnDataType="VARCHAR(36)"/>
|
||||
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_DEFAULT_ROLES" constraintName="FK_NUILTS7KLWQW2H8M2B5JOYTKY" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
||||
|
||||
<dropForeignKeyConstraint baseTableName="APP_NODE_REGISTRATIONS" constraintName="FK8454723BA992F594" />
|
||||
<renameTable oldTableName="APP_NODE_REGISTRATIONS" newTableName="CLIENT_NODE_REGISTRATIONS"/>
|
||||
<renameColumn tableName="CLIENT_NODE_REGISTRATIONS" newColumnName="CLIENT_ID" oldColumnName="APPLICATION_ID" columnDataType="VARCHAR(36)"/>
|
||||
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_NODE_REGISTRATIONS" constraintName="FK4129723BA992F594" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
||||
|
||||
<dropForeignKeyConstraint baseTableName="KEYCLOAK_ROLE" constraintName="FK_PIMO5LE2C0RAL09FL8CM9WFW9" />
|
||||
<renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT" oldColumnName="APPLICATION" columnDataType="VARCHAR(36)"/>
|
||||
<renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT_ROLE" oldColumnName="APPLICATION_ROLE" columnDataType="BOOLEAN"/>
|
||||
<renameColumn tableName="KEYCLOAK_ROLE" newColumnName="CLIENT_REALM_CONSTRAINT" oldColumnName="APP_REALM_CONSTRAINT" columnDataType="VARCHAR(36)"/>
|
||||
<addForeignKeyConstraint baseColumnNames="CLIENT" baseTableName="KEYCLOAK_ROLE" constraintName="FK_KJHO5LE2C0RAL09FL8CM9WFW9" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
||||
|
||||
<dropUniqueConstraint tableName="KEYCLOAK_ROLE" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2"/>
|
||||
<addUniqueConstraint columnNames="NAME,CLIENT_REALM_CONSTRAINT" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2-2" tableName="KEYCLOAK_ROLE"/>
|
||||
<addUniqueConstraint columnNames="CLIENT_ID, USER_ID" constraintName="UK_JKUWUVD56ONTGSUHOGM8UEWRT" tableName="USER_CONSENT"/>
|
||||
|
||||
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate1_2_0_CR1"/>
|
||||
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
|
||||
<changeSet author="keycloak" id="1.2.0.Final">
|
||||
<!-- KEYCLOAK-1277 -->
|
||||
<update tableName="CLIENT">
|
||||
<column name="DIRECT_GRANTS_ONLY" valueBoolean="false"/>
|
||||
<where>DIRECT_GRANTS_ONLY is null</where>
|
||||
</update>
|
||||
<update tableName="CLIENT">
|
||||
<column name="BEARER_ONLY" valueBoolean="false"/>
|
||||
<where>BEARER_ONLY is null</where>
|
||||
</update>
|
||||
<update tableName="CLIENT">
|
||||
<column name="SURROGATE_AUTH_REQUIRED" valueBoolean="false"/>
|
||||
<where>SURROGATE_AUTH_REQUIRED is null</where>
|
||||
</update>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
Regular → Executable
+2
@@ -4,4 +4,6 @@
|
||||
<include file="META-INF/jpa-changelog-1.1.0.Beta1.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.1.0.Final.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.2.0.Beta1.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.2.0.CR1.xml"/>
|
||||
<include file="META-INF/jpa-changelog-1.2.0.Final.xml"/>
|
||||
</databaseChangeLog>
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -17,12 +17,10 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.javax.persistence</groupId>
|
||||
@@ -32,7 +30,6 @@
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-entitymanager</artifactId>
|
||||
<version>${hibernate.entitymanager.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ public interface JpaUpdaterProvider extends Provider {
|
||||
|
||||
public String FIRST_VERSION = "1.0.0.Final";
|
||||
|
||||
public String LAST_VERSION = "1.2.0.Beta1";
|
||||
public String LAST_VERSION = "1.2.0.RC1";
|
||||
|
||||
public String getCurrentVersionSql();
|
||||
|
||||
|
||||
@@ -3,27 +3,32 @@
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
|
||||
version="1.0">
|
||||
<persistence-unit name="keycloak-default" transaction-type="RESOURCE_LOCAL">
|
||||
<class>org.keycloak.models.jpa.entities.ApplicationEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ClientEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.CredentialEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.OAuthClientEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RealmEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RealmAttributeEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RequiredCredentialEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserFederationProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.RoleEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.FederatedIdentityEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.MigrationModelEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserRequiredActionEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserAttributeEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserRoleMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ScopeMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.IdentityProviderEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.IdentityProviderMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.ProtocolMapperEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentRoleEntity</class>
|
||||
<class>org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity</class>
|
||||
|
||||
<!-- JpaUserSessionProvider -->
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionRoleEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionProtocolMapperEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.UserSessionNoteEntity</class>
|
||||
<class>org.keycloak.models.sessions.jpa.entities.UserSessionEntity</class>
|
||||
|
||||
Regular → Executable
+1
-4
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -17,17 +17,14 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-connections-mongo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
|
||||
+3
-1
@@ -10,6 +10,7 @@ import org.keycloak.connections.mongo.updater.impl.updates.Update;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.Update1_0_0_Final;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.Update1_1_0_Beta1;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_Beta1;
|
||||
import org.keycloak.connections.mongo.updater.impl.updates.Update1_2_0_CR1;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import java.util.Date;
|
||||
@@ -28,7 +29,8 @@ public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
|
||||
private Class<? extends Update>[] updates = new Class[]{
|
||||
Update1_0_0_Final.class,
|
||||
Update1_1_0_Beta1.class,
|
||||
Update1_2_0_Beta1.class
|
||||
Update1_2_0_Beta1.class,
|
||||
Update1_2_0_CR1.class
|
||||
};
|
||||
|
||||
@Override
|
||||
|
||||
+2
-9
@@ -53,15 +53,8 @@ public abstract class Update {
|
||||
log.debugv("Deleted entries from {0}", collection);
|
||||
}
|
||||
|
||||
protected String insertApplicationRole(DBCollection roles, String roleName, String applicationId) {
|
||||
BasicDBObject role = new BasicDBObject();
|
||||
String roleId = KeycloakModelUtils.generateId();
|
||||
role.append("_id", roleId);
|
||||
role.append("name", roleName);
|
||||
role.append("applicationId", applicationId);
|
||||
role.append("nameIndex", applicationId + "//" + roleName);
|
||||
roles.insert(role);
|
||||
return roleId;
|
||||
protected void renameCollection(String collection, String newName) {
|
||||
db.getCollection(collection).rename(newName);
|
||||
}
|
||||
|
||||
public void setLog(Logger log) {
|
||||
|
||||
+11
@@ -266,4 +266,15 @@ public class Update1_2_0_Beta1 extends Update {
|
||||
}
|
||||
}
|
||||
|
||||
private String insertApplicationRole(DBCollection roles, String roleName, String applicationId) {
|
||||
BasicDBObject role = new BasicDBObject();
|
||||
String roleId = KeycloakModelUtils.generateId();
|
||||
role.append("_id", roleId);
|
||||
role.append("name", roleName);
|
||||
role.append("applicationId", applicationId);
|
||||
role.append("nameIndex", applicationId + "//" + roleName);
|
||||
roles.insert(role);
|
||||
return roleId;
|
||||
}
|
||||
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package org.keycloak.connections.mongo.updater.impl.updates;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class Update1_2_0_CR1 extends Update {
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "1.2.0.CR1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(KeycloakSession session) {
|
||||
deleteEntries("clientSessions");
|
||||
deleteEntries("sessions");
|
||||
|
||||
convertApplicationsToClients();
|
||||
convertOAuthClientsToClients();
|
||||
|
||||
db.getCollection("realms").update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("adminAppId", "masterAdminClient")), false, true);
|
||||
|
||||
ensureIndex("userConsents", new String[]{"clientId", "userId"}, true, false);
|
||||
}
|
||||
|
||||
private void convertApplicationsToClients() {
|
||||
DBCollection applications = db.getCollection("applications");
|
||||
applications.dropIndex("realmId_1_name_1");
|
||||
|
||||
applications.update(new BasicDBObject(), new BasicDBObject("$set", new BasicDBObject("consentRequired", false)), false, true);
|
||||
applications.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("name", "clientId")), false, true);
|
||||
renameCollection("applications", "clients");
|
||||
log.debugv("Converted applications to clients");
|
||||
|
||||
DBCollection roles = db.getCollection("roles");
|
||||
roles.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("applicationId", "clientId")), false, true);
|
||||
log.debugv("Renamed roles.applicationId to roles.clientId");
|
||||
|
||||
ensureIndex("clients", new String[]{"realmId", "clientId"}, true, false);
|
||||
}
|
||||
|
||||
private void convertOAuthClientsToClients() {
|
||||
DBCollection clients = db.getCollection("clients");
|
||||
DBCollection oauthClients = db.getCollection("oauthClients");
|
||||
oauthClients.dropIndex("realmId_1_name_1");
|
||||
|
||||
oauthClients.update(new BasicDBObject(), new BasicDBObject("$rename", new BasicDBObject("name", "clientId")), false, true);
|
||||
oauthClients.update(new BasicDBObject(), new BasicDBObject("$set", new BasicDBObject("consentRequired", true)), false, true);
|
||||
|
||||
DBCursor curs = oauthClients.find();
|
||||
while (curs.hasNext()) {
|
||||
clients.insert(curs.next());
|
||||
}
|
||||
|
||||
oauthClients.drop();
|
||||
log.debugv("Converted oauthClients to clients");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -17,12 +17,10 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-model-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
|
||||
+41
-20
@@ -16,6 +16,7 @@ import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
@@ -33,13 +34,15 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
"org.keycloak.models.entities.RequiredCredentialEntity",
|
||||
"org.keycloak.models.entities.CredentialEntity",
|
||||
"org.keycloak.models.entities.FederatedIdentityEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoClientEntity",
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity",
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity",
|
||||
"org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity",
|
||||
"org.keycloak.models.entities.UserFederationProviderEntity",
|
||||
"org.keycloak.models.entities.ProtocolMapperEntity"
|
||||
"org.keycloak.models.entities.ProtocolMapperEntity",
|
||||
"org.keycloak.models.entities.IdentityProviderMapperEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
|
||||
"org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity"
|
||||
};
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
|
||||
@@ -48,7 +51,7 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
|
||||
private MongoStore mongoStore;
|
||||
private DB db;
|
||||
private Config.Scope config;
|
||||
protected Config.Scope config;
|
||||
|
||||
@Override
|
||||
public MongoConnectionProvider create(KeycloakSession session) {
|
||||
@@ -75,21 +78,9 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
synchronized (this) {
|
||||
if (client == null) {
|
||||
try {
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
this.client = createMongoClient();
|
||||
|
||||
String dbName = config.get("db", "keycloak");
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
|
||||
MongoClientOptions clientOptions = getClientOptions();
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
|
||||
} else {
|
||||
client = new MongoClient(new ServerAddress(host, port), clientOptions);
|
||||
}
|
||||
|
||||
this.db = client.getDB(dbName);
|
||||
|
||||
String databaseSchema = config.get("databaseSchema");
|
||||
@@ -108,8 +99,6 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
}
|
||||
|
||||
this.mongoStore = new MongoStoreImpl(db, getManagedEntities());
|
||||
|
||||
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@@ -138,6 +127,38 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
|
||||
return "default";
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method if you want more possibility to configure Mongo client. It can be also used to inject mongo client
|
||||
* from different source.
|
||||
*
|
||||
* This method can assume that "config" is already set and can use it.
|
||||
*
|
||||
* @return mongoClient instance, which will be shared for whole Keycloak
|
||||
*
|
||||
* @throws UnknownHostException
|
||||
*/
|
||||
protected MongoClient createMongoClient() throws UnknownHostException {
|
||||
String host = config.get("host", ServerAddress.defaultHost());
|
||||
int port = config.getInt("port", ServerAddress.defaultPort());
|
||||
String dbName = config.get("db", "keycloak");
|
||||
|
||||
String user = config.get("user");
|
||||
String password = config.get("password");
|
||||
|
||||
MongoClientOptions clientOptions = getClientOptions();
|
||||
|
||||
MongoClient client;
|
||||
if (user != null && password != null) {
|
||||
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
|
||||
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
|
||||
} else {
|
||||
client = new MongoClient(new ServerAddress(host, port), clientOptions);
|
||||
}
|
||||
|
||||
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
|
||||
return client;
|
||||
}
|
||||
|
||||
protected MongoClientOptions getClientOptions() {
|
||||
MongoClientOptions.Builder builder = MongoClientOptions.builder();
|
||||
checkIntOption("connectionsPerHost", builder);
|
||||
|
||||
@@ -24,13 +24,35 @@ public interface MongoStore {
|
||||
*/
|
||||
void updateEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
|
||||
|
||||
/**
|
||||
* Bulk update of more entities of some type
|
||||
*
|
||||
* @param type
|
||||
* @param query
|
||||
* @param update
|
||||
* @param context
|
||||
* @return count of updated entities
|
||||
*/
|
||||
<T extends MongoIdentifiableEntity> int updateEntities(Class<T> type, DBObject query, DBObject update, MongoStoreInvocationContext context);
|
||||
|
||||
<T extends MongoIdentifiableEntity> T loadEntity(Class<T> type, String id, MongoStoreInvocationContext context);
|
||||
|
||||
<T extends MongoIdentifiableEntity> T loadSingleEntity(Class<T> type, DBObject query, MongoStoreInvocationContext context);
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* @param query
|
||||
* @param context
|
||||
* @return query result or empty list if no results available for the query. Doesn't return null
|
||||
*/
|
||||
<T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
|
||||
|
||||
/**
|
||||
* @param type
|
||||
* @param query
|
||||
* @param context
|
||||
* @return query result or empty list if no results available for the query. Doesn't return null
|
||||
*/
|
||||
<T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, DBObject sort, int firstResult, int maxResults, MongoStoreInvocationContext context);
|
||||
|
||||
<T extends MongoIdentifiableEntity> int countEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
|
||||
@@ -39,7 +61,16 @@ public interface MongoStore {
|
||||
|
||||
boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context);
|
||||
|
||||
boolean removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context);
|
||||
/**
|
||||
*
|
||||
* @param type
|
||||
* @param query
|
||||
* @param callback if true, then store will first load all entities, then call "afterRemove" for every entity. If false, the entities are removed directly without load and calling "afterRemove" callback
|
||||
* false has better performance (especially if we are going to remove big number of entities)
|
||||
* @param context
|
||||
* @return count of removed entities
|
||||
*/
|
||||
int removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, boolean callback, MongoStoreInvocationContext context);
|
||||
|
||||
<S> boolean pushItemToList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context);
|
||||
|
||||
|
||||
+2
@@ -22,6 +22,8 @@ public interface MongoStoreInvocationContext {
|
||||
|
||||
void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType);
|
||||
|
||||
void beforeDBBulkUpdateOrRemove(Class<? extends MongoIdentifiableEntity> entityType);
|
||||
|
||||
void begin();
|
||||
|
||||
void commit();
|
||||
|
||||
+36
-12
@@ -7,6 +7,7 @@ import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBCursor;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.MongoException;
|
||||
import com.mongodb.WriteResult;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.mongo.api.MongoCollection;
|
||||
import org.keycloak.connections.mongo.api.MongoEntity;
|
||||
@@ -127,7 +128,7 @@ public class MongoStoreImpl implements MongoStore {
|
||||
throw convertException(e);
|
||||
}
|
||||
|
||||
// Treat object as created in this transaction (It is already submited to transaction)
|
||||
// Treat object as created in this transaction (It is already submitted to transaction)
|
||||
context.addCreatedEntity(entity);
|
||||
}
|
||||
|
||||
@@ -170,6 +171,16 @@ public class MongoStoreImpl implements MongoStore {
|
||||
context.addUpdateTask(entity, fullUpdateTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends MongoIdentifiableEntity> int updateEntities(Class<T> type, DBObject query, DBObject update, MongoStoreInvocationContext context) {
|
||||
context.beforeDBBulkUpdateOrRemove(type);
|
||||
|
||||
DBCollection collection = getDBCollectionForType(type);
|
||||
WriteResult wr = collection.update(query, update, false, true);
|
||||
|
||||
logger.debugf("Updated %d collections of type %s", wr.getN(), type);
|
||||
return wr.getN();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends MongoIdentifiableEntity> T loadEntity(Class<T> type, String id, MongoStoreInvocationContext context) {
|
||||
@@ -275,19 +286,32 @@ public class MongoStoreImpl implements MongoStore {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context) {
|
||||
List<? extends MongoIdentifiableEntity> foundObjects = loadEntities(type, query, context);
|
||||
if (foundObjects.size() == 0) {
|
||||
return false;
|
||||
} else {
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
dbCollection.remove(query);
|
||||
//logger.debug("Removed %d" + foundObjects.size() + " entities of type: " + type + ", query: " + query);
|
||||
public int removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, boolean callback, MongoStoreInvocationContext context) {
|
||||
if (callback) {
|
||||
List<? extends MongoIdentifiableEntity> foundObjects = loadEntities(type, query, context);
|
||||
if (foundObjects.size() == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
dbCollection.remove(query);
|
||||
|
||||
for (MongoIdentifiableEntity found : foundObjects) {
|
||||
context.addRemovedEntity(found);;
|
||||
logger.debugf("Removed %d entities of type: %s, query: %s", foundObjects.size(), type, query);
|
||||
|
||||
for (MongoIdentifiableEntity found : foundObjects) {
|
||||
context.addRemovedEntity(found);;
|
||||
}
|
||||
return foundObjects.size();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
|
||||
context.beforeDBBulkUpdateOrRemove(type);
|
||||
|
||||
DBCollection dbCollection = getDBCollectionForType(type);
|
||||
WriteResult writeResult = dbCollection.remove(query);
|
||||
int removedCount = writeResult.getN();
|
||||
|
||||
logger.debugf("Removed directly %d entities of type: %s, query: %s", removedCount, type, query);
|
||||
return removedCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -45,6 +45,10 @@ public class SimpleMongoStoreInvocationContext implements MongoStoreInvocationCo
|
||||
public void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeDBBulkUpdateOrRemove(Class<? extends MongoIdentifiableEntity> entityType) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
}
|
||||
|
||||
+18
-4
@@ -50,10 +50,6 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat
|
||||
|
||||
@Override
|
||||
public void addUpdateTask(MongoIdentifiableEntity entityToUpdate, MongoTask task) {
|
||||
if (!loadedObjects.containsValue(entityToUpdate)) {
|
||||
throw new IllegalStateException("Entity " + entityToUpdate + " not found in loaded objects");
|
||||
}
|
||||
|
||||
Set<MongoTask> currentObjectTasks = pendingUpdateTasks.get(entityToUpdate);
|
||||
if (currentObjectTasks == null) {
|
||||
currentObjectTasks = new LinkedHashSet<MongoTask>();
|
||||
@@ -106,6 +102,24 @@ public class TransactionMongoStoreInvocationContext implements MongoStoreInvocat
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeDBBulkUpdateOrRemove(Class<? extends MongoIdentifiableEntity> entityType) {
|
||||
beforeDBSearch(entityType);
|
||||
Set<String> toRemove = new HashSet<String>();
|
||||
|
||||
for (Map.Entry<String, MongoIdentifiableEntity> entry : loadedObjects.entrySet()) {
|
||||
MongoIdentifiableEntity entity = entry.getValue();
|
||||
if (entity.getClass().equals(entityType)) {
|
||||
toRemove.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
// Now remove all loadedObjects
|
||||
for (String objectId : toRemove) {
|
||||
loadedObjects.remove(objectId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void begin() {
|
||||
loadedObjects.clear();
|
||||
|
||||
+2
-1
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
</parent>
|
||||
<name>Connections Parent</name>
|
||||
<description/>
|
||||
@@ -19,6 +19,7 @@
|
||||
<module>mongo</module>
|
||||
<module>file</module>
|
||||
<module>mongo-update</module>
|
||||
<module>http-client</module>
|
||||
</modules>
|
||||
|
||||
<build>
|
||||
|
||||
+1
-2
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -22,7 +22,6 @@
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
+1
-7
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<artifactId>keycloak-parent</artifactId>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<version>1.2.0.Beta1</version>
|
||||
<version>1.2.0.Final-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
@@ -56,12 +56,6 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
<artifactId>nimbus-jose-jwt</artifactId>
|
||||
<version>3.9</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<resources>
|
||||
|
||||
@@ -39,7 +39,7 @@ public class RSATokenVerifier {
|
||||
throw new VerificationException("Realm URL is null. Make sure to add auth-server-url to the configuration of your adapter!");
|
||||
}
|
||||
if (!realmUrl.equals(token.getIssuer())) {
|
||||
throw new VerificationException("Token audience doesn't match domain.");
|
||||
throw new VerificationException("Token audience doesn't match domain. Token issuer is " + token.getIssuer() + ", but URL from configuration is " + realmUrl);
|
||||
|
||||
}
|
||||
if (checkActive && !token.isActive()) {
|
||||
|
||||
@@ -19,13 +19,13 @@ public interface AdapterConstants {
|
||||
String AUTH_DATA_PARAM_NAME = "org.keycloak.json.adapterConfig";
|
||||
|
||||
// Attribute passed in codeToToken request from adapter to Keycloak and saved in ClientSession. Contains ID of HttpSession on adapter
|
||||
public static final String APPLICATION_SESSION_STATE = "application_session_state";
|
||||
public static final String CLIENT_SESSION_STATE = "client_session_state";
|
||||
|
||||
// Attribute passed in codeToToken request from adapter to Keycloak and saved in ClientSession. Contains hostname of adapter where HttpSession is served
|
||||
public static final String APPLICATION_SESSION_HOST = "application_session_host";
|
||||
public static final String CLIENT_SESSION_HOST = "client_session_host";
|
||||
|
||||
// Attribute passed in registerNode request for register new application cluster node once he joined cluster
|
||||
public static final String APPLICATION_CLUSTER_HOST = "application_cluster_host";
|
||||
public static final String CLIENT_CLUSTER_HOST = "client_cluster_host";
|
||||
|
||||
// Cookie used on adapter side to store token info. Used only when tokenStore is 'COOKIE'
|
||||
public static final String KEYCLOAK_ADAPTER_STATE_COOKIE = "KEYCLOAK_ADAPTER_STATE";
|
||||
|
||||
@@ -7,22 +7,29 @@ import org.codehaus.jackson.annotate.JsonProperty;
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AddressClaimSet {
|
||||
@JsonProperty("formatted")
|
||||
public static final String FORMATTED = "formatted";
|
||||
public static final String STREET_ADDRESS = "street_address";
|
||||
public static final String LOCALITY = "locality";
|
||||
public static final String REGION = "region";
|
||||
public static final String POSTAL_CODE = "postal_code";
|
||||
public static final String COUNTRY = "country";
|
||||
|
||||
@JsonProperty(FORMATTED)
|
||||
protected String formattedAddress;
|
||||
|
||||
@JsonProperty("street_address")
|
||||
@JsonProperty(STREET_ADDRESS)
|
||||
protected String streetAddress;
|
||||
|
||||
@JsonProperty("locality")
|
||||
@JsonProperty(LOCALITY)
|
||||
protected String locality;
|
||||
|
||||
@JsonProperty("region")
|
||||
@JsonProperty(REGION)
|
||||
protected String region;
|
||||
|
||||
@JsonProperty("postal_code")
|
||||
@JsonProperty(POSTAL_CODE)
|
||||
protected String postalCode;
|
||||
|
||||
@JsonProperty("country")
|
||||
@JsonProperty(COUNTRY)
|
||||
protected String country;
|
||||
|
||||
public String getFormattedAddress() {
|
||||
|
||||
@@ -13,76 +13,96 @@ import java.util.Map;
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class IDToken extends JsonWebToken {
|
||||
public static final String NONCE = "nonce";
|
||||
public static final String SESSION_STATE = "session_state";
|
||||
public static final String NAME = "name";
|
||||
public static final String GIVEN_NAME = "given_name";
|
||||
public static final String FAMILY_NAME = "family_name";
|
||||
public static final String MIDDLE_NAME = "middle_name";
|
||||
public static final String NICKNAME = "nickname";
|
||||
public static final String PREFERRED_USERNAME = "preferred_username";
|
||||
public static final String PROFILE = "profile";
|
||||
public static final String PICTURE = "picture";
|
||||
public static final String WEBSITE = "website";
|
||||
public static final String EMAIL = "email";
|
||||
public static final String EMAIL_VERIFIED = "email_verified";
|
||||
public static final String GENDER = "gender";
|
||||
public static final String BIRTHDATE = "birthdate";
|
||||
public static final String ZONEINFO = "zoneinfo";
|
||||
public static final String LOCALE = "locale";
|
||||
public static final String PHONE_NUMBER = "phone_number";
|
||||
public static final String PHONE_NUMBER_VERIFIED = "phone_number_verified";
|
||||
public static final String ADDRESS = "address";
|
||||
public static final String UPDATED_AT = "updated_at";
|
||||
public static final String CLAIMS_LOCALES = "claims_locales";
|
||||
// NOTE!!! WE used to use @JsonUnwrapped on a UserClaimSet object. This screws up otherClaims and the won't work
|
||||
// anymore. So don't have any @JsonUnwrapped!
|
||||
@JsonProperty("nonce")
|
||||
@JsonProperty(NONCE)
|
||||
protected String nonce;
|
||||
|
||||
@JsonProperty("session_state")
|
||||
@JsonProperty(SESSION_STATE)
|
||||
protected String sessionState;
|
||||
|
||||
@JsonProperty("name")
|
||||
@JsonProperty(NAME)
|
||||
protected String name;
|
||||
|
||||
@JsonProperty("given_name")
|
||||
@JsonProperty(GIVEN_NAME)
|
||||
protected String givenName;
|
||||
|
||||
@JsonProperty("family_name")
|
||||
@JsonProperty(FAMILY_NAME)
|
||||
protected String familyName;
|
||||
|
||||
@JsonProperty("middle_name")
|
||||
@JsonProperty(MIDDLE_NAME)
|
||||
protected String middleName;
|
||||
|
||||
@JsonProperty("nickname")
|
||||
@JsonProperty(NICKNAME)
|
||||
protected String nickName;
|
||||
|
||||
@JsonProperty("preferred_username")
|
||||
@JsonProperty(PREFERRED_USERNAME)
|
||||
protected String preferredUsername;
|
||||
|
||||
@JsonProperty("profile")
|
||||
@JsonProperty(PROFILE)
|
||||
protected String profile;
|
||||
|
||||
@JsonProperty("picture")
|
||||
@JsonProperty(PICTURE)
|
||||
protected String picture;
|
||||
|
||||
@JsonProperty("website")
|
||||
@JsonProperty(WEBSITE)
|
||||
protected String website;
|
||||
|
||||
@JsonProperty("email")
|
||||
@JsonProperty(EMAIL)
|
||||
protected String email;
|
||||
|
||||
@JsonProperty("email_verified")
|
||||
@JsonProperty(EMAIL_VERIFIED)
|
||||
protected Boolean emailVerified;
|
||||
|
||||
@JsonProperty("gender")
|
||||
@JsonProperty(GENDER)
|
||||
protected String gender;
|
||||
|
||||
@JsonProperty("birthdate")
|
||||
@JsonProperty(BIRTHDATE)
|
||||
protected String birthdate;
|
||||
|
||||
@JsonProperty("zoneinfo")
|
||||
@JsonProperty(ZONEINFO)
|
||||
protected String zoneinfo;
|
||||
|
||||
@JsonProperty("locale")
|
||||
@JsonProperty(LOCALE)
|
||||
protected String locale;
|
||||
|
||||
@JsonProperty("phone_number")
|
||||
@JsonProperty(PHONE_NUMBER)
|
||||
protected String phoneNumber;
|
||||
|
||||
@JsonProperty("phone_number_verified")
|
||||
@JsonProperty(PHONE_NUMBER_VERIFIED)
|
||||
protected Boolean phoneNumberVerified;
|
||||
|
||||
@JsonProperty("address")
|
||||
@JsonProperty(ADDRESS)
|
||||
protected AddressClaimSet address;
|
||||
|
||||
@JsonProperty("updated_at")
|
||||
@JsonProperty(UPDATED_AT)
|
||||
protected Long updatedAt;
|
||||
|
||||
@JsonProperty("claims_locales")
|
||||
@JsonProperty(CLAIMS_LOCALES)
|
||||
protected String claimsLocales;
|
||||
|
||||
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
|
||||
|
||||
public String getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
@@ -259,18 +279,4 @@ public class IDToken extends JsonWebToken {
|
||||
this.claimsLocales = claimsLocales;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a map of any other claims and data that might be in the IDToken. Could be custom claims set up by the auth server
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> getOtherClaims() {
|
||||
return otherClaims;
|
||||
}
|
||||
|
||||
@JsonAnySetter
|
||||
public void setOtherClaims(String name, Object value) {
|
||||
otherClaims.put(name, value);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package org.keycloak.representations;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonAnyGetter;
|
||||
import org.codehaus.jackson.annotate.JsonAnySetter;
|
||||
import org.codehaus.jackson.annotate.JsonIgnore;
|
||||
import org.codehaus.jackson.annotate.JsonProperty;
|
||||
import org.keycloak.util.Time;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -29,6 +33,7 @@ public class JsonWebToken implements Serializable {
|
||||
protected String type;
|
||||
@JsonProperty("azp")
|
||||
public String issuedFor;
|
||||
protected Map<String, Object> otherClaims = new HashMap<String, Object>();
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
@@ -153,4 +158,19 @@ public class JsonWebToken implements Serializable {
|
||||
this.issuedFor = issuedFor;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a map of any other claims and data that might be in the IDToken. Could be custom claims set up by the auth server
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@JsonAnyGetter
|
||||
public Map<String, Object> getOtherClaims() {
|
||||
return otherClaims;
|
||||
}
|
||||
|
||||
@JsonAnySetter
|
||||
public void setOtherClaims(String name, Object value) {
|
||||
otherClaims.put(name, value);
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ApplicationMappingsRepresentation {
|
||||
protected String applicationId;
|
||||
protected String application;
|
||||
|
||||
protected List<RoleRepresentation> mappings;
|
||||
|
||||
public String getApplicationId() {
|
||||
return applicationId;
|
||||
}
|
||||
|
||||
public void setApplicationId(String applicationId) {
|
||||
this.applicationId = applicationId;
|
||||
}
|
||||
|
||||
public String getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
public void setApplication(String application) {
|
||||
this.application = application;
|
||||
}
|
||||
|
||||
public List<RoleRepresentation> getMappings() {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public void setMappings(List<RoleRepresentation> mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
}
|
||||
+2
-184
@@ -1,44 +1,14 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ApplicationRepresentation {
|
||||
protected String id;
|
||||
@Deprecated
|
||||
public class ApplicationRepresentation extends ClientRepresentation {
|
||||
protected String name;
|
||||
protected String adminUrl;
|
||||
protected String baseUrl;
|
||||
protected Boolean surrogateAuthRequired;
|
||||
protected Boolean enabled;
|
||||
protected String secret;
|
||||
protected String[] defaultRoles;
|
||||
protected List<String> redirectUris;
|
||||
protected List<String> webOrigins;
|
||||
@Deprecated
|
||||
protected ClaimRepresentation claims;
|
||||
protected Integer notBefore;
|
||||
protected Boolean bearerOnly;
|
||||
protected Boolean publicClient;
|
||||
protected Boolean frontchannelLogout;
|
||||
protected String protocol;
|
||||
protected Map<String, String> attributes;
|
||||
protected Boolean fullScopeAllowed;
|
||||
protected Integer nodeReRegistrationTimeout;
|
||||
protected Map<String, Integer> registeredNodes;
|
||||
protected List<ClientIdentityProviderMappingRepresentation> identityProviders;
|
||||
protected List<ProtocolMapperRepresentation> protocolMappers;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
@@ -48,70 +18,6 @@ public class ApplicationRepresentation {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Boolean isSurrogateAuthRequired() {
|
||||
return surrogateAuthRequired;
|
||||
}
|
||||
|
||||
public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
|
||||
this.surrogateAuthRequired = surrogateAuthRequired;
|
||||
}
|
||||
|
||||
public String getAdminUrl() {
|
||||
return adminUrl;
|
||||
}
|
||||
|
||||
public void setAdminUrl(String adminUrl) {
|
||||
this.adminUrl = adminUrl;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public List<String> getRedirectUris() {
|
||||
return redirectUris;
|
||||
}
|
||||
|
||||
public void setRedirectUris(List<String> redirectUris) {
|
||||
this.redirectUris = redirectUris;
|
||||
}
|
||||
|
||||
public List<String> getWebOrigins() {
|
||||
return webOrigins;
|
||||
}
|
||||
|
||||
public void setWebOrigins(List<String> webOrigins) {
|
||||
this.webOrigins = webOrigins;
|
||||
}
|
||||
|
||||
public String[] getDefaultRoles() {
|
||||
return defaultRoles;
|
||||
}
|
||||
|
||||
public void setDefaultRoles(String[] defaultRoles) {
|
||||
this.defaultRoles = defaultRoles;
|
||||
}
|
||||
|
||||
public ClaimRepresentation getClaims() {
|
||||
return claims;
|
||||
}
|
||||
@@ -119,92 +25,4 @@ public class ApplicationRepresentation {
|
||||
public void setClaims(ClaimRepresentation claims) {
|
||||
this.claims = claims;
|
||||
}
|
||||
|
||||
public Integer getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(Integer notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public Boolean isBearerOnly() {
|
||||
return bearerOnly;
|
||||
}
|
||||
|
||||
public void setBearerOnly(Boolean bearerOnly) {
|
||||
this.bearerOnly = bearerOnly;
|
||||
}
|
||||
|
||||
public Boolean isPublicClient() {
|
||||
return publicClient;
|
||||
}
|
||||
|
||||
public void setPublicClient(Boolean publicClient) {
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
public Boolean isFullScopeAllowed() {
|
||||
return fullScopeAllowed;
|
||||
}
|
||||
|
||||
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
|
||||
this.fullScopeAllowed = fullScopeAllowed;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public Integer getNodeReRegistrationTimeout() {
|
||||
return nodeReRegistrationTimeout;
|
||||
}
|
||||
|
||||
public void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout) {
|
||||
this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getRegisteredNodes() {
|
||||
return registeredNodes;
|
||||
}
|
||||
|
||||
public void setRegisteredNodes(Map<String, Integer> registeredNodes) {
|
||||
this.registeredNodes = registeredNodes;
|
||||
}
|
||||
|
||||
public Boolean isFrontchannelLogout() {
|
||||
return frontchannelLogout;
|
||||
}
|
||||
|
||||
public void setFrontchannelLogout(Boolean frontchannelLogout) {
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
|
||||
return this.identityProviders;
|
||||
}
|
||||
|
||||
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
|
||||
this.identityProviders = identityProviders;
|
||||
}
|
||||
|
||||
public List<ProtocolMapperRepresentation> getProtocolMappers() {
|
||||
return protocolMappers;
|
||||
}
|
||||
|
||||
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
|
||||
this.protocolMappers = protocolMappers;
|
||||
}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientMappingsRepresentation {
|
||||
protected String id;
|
||||
protected String client;
|
||||
|
||||
protected List<RoleRepresentation> mappings;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(String client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public List<RoleRepresentation> getMappings() {
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public void setMappings(List<RoleRepresentation> mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
}
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientRepresentation {
|
||||
protected String id;
|
||||
protected String clientId;
|
||||
protected String name;
|
||||
protected String adminUrl;
|
||||
protected String baseUrl;
|
||||
protected Boolean surrogateAuthRequired;
|
||||
protected Boolean enabled;
|
||||
protected String secret;
|
||||
protected String[] defaultRoles;
|
||||
protected List<String> redirectUris;
|
||||
protected List<String> webOrigins;
|
||||
protected Integer notBefore;
|
||||
protected Boolean bearerOnly;
|
||||
protected Boolean consentRequired;
|
||||
protected Boolean directGrantsOnly;
|
||||
protected Boolean publicClient;
|
||||
protected Boolean frontchannelLogout;
|
||||
protected String protocol;
|
||||
protected Map<String, String> attributes;
|
||||
protected Boolean fullScopeAllowed;
|
||||
protected Integer nodeReRegistrationTimeout;
|
||||
protected Map<String, Integer> registeredNodes;
|
||||
protected List<ProtocolMapperRepresentation> protocolMappers;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Boolean isSurrogateAuthRequired() {
|
||||
return surrogateAuthRequired;
|
||||
}
|
||||
|
||||
public void setSurrogateAuthRequired(Boolean surrogateAuthRequired) {
|
||||
this.surrogateAuthRequired = surrogateAuthRequired;
|
||||
}
|
||||
|
||||
public String getAdminUrl() {
|
||||
return adminUrl;
|
||||
}
|
||||
|
||||
public void setAdminUrl(String adminUrl) {
|
||||
this.adminUrl = adminUrl;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public List<String> getRedirectUris() {
|
||||
return redirectUris;
|
||||
}
|
||||
|
||||
public void setRedirectUris(List<String> redirectUris) {
|
||||
this.redirectUris = redirectUris;
|
||||
}
|
||||
|
||||
public List<String> getWebOrigins() {
|
||||
return webOrigins;
|
||||
}
|
||||
|
||||
public void setWebOrigins(List<String> webOrigins) {
|
||||
this.webOrigins = webOrigins;
|
||||
}
|
||||
|
||||
public String[] getDefaultRoles() {
|
||||
return defaultRoles;
|
||||
}
|
||||
|
||||
public void setDefaultRoles(String[] defaultRoles) {
|
||||
this.defaultRoles = defaultRoles;
|
||||
}
|
||||
|
||||
public Integer getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(Integer notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public Boolean isBearerOnly() {
|
||||
return bearerOnly;
|
||||
}
|
||||
|
||||
public void setBearerOnly(Boolean bearerOnly) {
|
||||
this.bearerOnly = bearerOnly;
|
||||
}
|
||||
|
||||
public Boolean isConsentRequired() {
|
||||
return consentRequired;
|
||||
}
|
||||
|
||||
public void setConsentRequired(Boolean consentRequired) {
|
||||
this.consentRequired = consentRequired;
|
||||
}
|
||||
|
||||
public Boolean isDirectGrantsOnly() {
|
||||
return directGrantsOnly;
|
||||
}
|
||||
|
||||
public void setDirectGrantsOnly(Boolean directGrantsOnly) {
|
||||
this.directGrantsOnly = directGrantsOnly;
|
||||
}
|
||||
|
||||
public Boolean isPublicClient() {
|
||||
return publicClient;
|
||||
}
|
||||
|
||||
public void setPublicClient(Boolean publicClient) {
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
public Boolean isFullScopeAllowed() {
|
||||
return fullScopeAllowed;
|
||||
}
|
||||
|
||||
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
|
||||
this.fullScopeAllowed = fullScopeAllowed;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public Integer getNodeReRegistrationTimeout() {
|
||||
return nodeReRegistrationTimeout;
|
||||
}
|
||||
|
||||
public void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout) {
|
||||
this.nodeReRegistrationTimeout = nodeReRegistrationTimeout;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getRegisteredNodes() {
|
||||
return registeredNodes;
|
||||
}
|
||||
|
||||
public void setRegisteredNodes(Map<String, Integer> registeredNodes) {
|
||||
this.registeredNodes = registeredNodes;
|
||||
}
|
||||
|
||||
public Boolean isFrontchannelLogout() {
|
||||
return frontchannelLogout;
|
||||
}
|
||||
|
||||
public void setFrontchannelLogout(Boolean frontchannelLogout) {
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<ProtocolMapperRepresentation> getProtocolMappers() {
|
||||
return protocolMappers;
|
||||
}
|
||||
|
||||
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
|
||||
this.protocolMappers = protocolMappers;
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ConfigPropertyRepresentation {
|
||||
protected String name;
|
||||
protected String label;
|
||||
protected String helpText;
|
||||
protected String type;
|
||||
protected Object defaultValue;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Object getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public String getHelpText() {
|
||||
return helpText;
|
||||
}
|
||||
|
||||
public void setHelpText(String helpText) {
|
||||
this.helpText = helpText;
|
||||
}
|
||||
}
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class IdentityProviderMapperRepresentation {
|
||||
protected String id;
|
||||
protected String name;
|
||||
protected String identityProviderAlias;
|
||||
protected String identityProviderMapper;
|
||||
protected Map<String, String> config = new HashMap<String, String>();
|
||||
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getIdentityProviderAlias() {
|
||||
return identityProviderAlias;
|
||||
}
|
||||
|
||||
public void setIdentityProviderAlias(String identityProviderAlias) {
|
||||
this.identityProviderAlias = identityProviderAlias;
|
||||
}
|
||||
|
||||
public String getIdentityProviderMapper() {
|
||||
return identityProviderMapper;
|
||||
}
|
||||
|
||||
public void setIdentityProviderMapper(String identityProviderMapper) {
|
||||
this.identityProviderMapper = identityProviderMapper;
|
||||
}
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public void setConfig(Map<String, String> config) {
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
Executable
+57
@@ -0,0 +1,57 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class IdentityProviderMapperTypeRepresentation {
|
||||
protected String id;
|
||||
protected String name;
|
||||
protected String category;
|
||||
protected String helpText;
|
||||
|
||||
protected List<ConfigPropertyRepresentation> properties = new LinkedList<>();
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getHelpText() {
|
||||
return helpText;
|
||||
}
|
||||
|
||||
public void setHelpText(String helpText) {
|
||||
this.helpText = helpText;
|
||||
}
|
||||
|
||||
public List<ConfigPropertyRepresentation> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(List<ConfigPropertyRepresentation> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
||||
+9
@@ -31,6 +31,7 @@ public class IdentityProviderRepresentation {
|
||||
protected boolean enabled = true;
|
||||
protected boolean updateProfileFirstLogin = true;
|
||||
protected boolean storeToken;
|
||||
protected boolean addReadTokenRoleOnCreate;
|
||||
protected boolean authenticateByDefault;
|
||||
protected Map<String, String> config = new HashMap<String, String>();
|
||||
|
||||
@@ -97,4 +98,12 @@ public class IdentityProviderRepresentation {
|
||||
public void setStoreToken(boolean storeToken) {
|
||||
this.storeToken = storeToken;
|
||||
}
|
||||
|
||||
public boolean isAddReadTokenRoleOnCreate() {
|
||||
return addReadTokenRoleOnCreate;
|
||||
}
|
||||
|
||||
public void setAddReadTokenRoleOnCreate(boolean addReadTokenRoleOnCreate) {
|
||||
this.addReadTokenRoleOnCreate = addReadTokenRoleOnCreate;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import java.util.Map;
|
||||
*/
|
||||
public class MappingsRepresentation {
|
||||
protected List<RoleRepresentation> realmMappings;
|
||||
protected Map<String, ApplicationMappingsRepresentation> applicationMappings;
|
||||
protected Map<String, ClientMappingsRepresentation> clientMappings;
|
||||
|
||||
public List<RoleRepresentation> getRealmMappings() {
|
||||
return realmMappings;
|
||||
@@ -19,11 +19,11 @@ public class MappingsRepresentation {
|
||||
this.realmMappings = realmMappings;
|
||||
}
|
||||
|
||||
public Map<String, ApplicationMappingsRepresentation> getApplicationMappings() {
|
||||
return applicationMappings;
|
||||
public Map<String, ClientMappingsRepresentation> getClientMappings() {
|
||||
return clientMappings;
|
||||
}
|
||||
|
||||
public void setApplicationMappings(Map<String, ApplicationMappingsRepresentation> applicationMappings) {
|
||||
this.applicationMappings = applicationMappings;
|
||||
public void setClientMappings(Map<String, ClientMappingsRepresentation> clientMappings) {
|
||||
this.clientMappings = clientMappings;
|
||||
}
|
||||
}
|
||||
+2
-150
@@ -1,157 +1,9 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class OAuthClientRepresentation {
|
||||
protected String id;
|
||||
protected String name;
|
||||
protected List<String> redirectUris;
|
||||
protected List<String> webOrigins;
|
||||
protected Boolean enabled;
|
||||
protected String secret;
|
||||
@Deprecated
|
||||
protected ClaimRepresentation claims;
|
||||
protected Integer notBefore;
|
||||
protected Boolean publicClient;
|
||||
protected String protocol;
|
||||
protected Map<String, String> attributes;
|
||||
protected Boolean directGrantsOnly;
|
||||
protected Boolean fullScopeAllowed;
|
||||
protected Boolean frontchannelLogout;
|
||||
protected List<ProtocolMapperRepresentation> protocolMappers;
|
||||
private List<ClientIdentityProviderMappingRepresentation> identityProviders;
|
||||
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public List<String> getRedirectUris() {
|
||||
return redirectUris;
|
||||
}
|
||||
|
||||
public void setRedirectUris(List<String> redirectUris) {
|
||||
this.redirectUris = redirectUris;
|
||||
}
|
||||
|
||||
public List<String> getWebOrigins() {
|
||||
return webOrigins;
|
||||
}
|
||||
|
||||
public void setWebOrigins(List<String> webOrigins) {
|
||||
this.webOrigins = webOrigins;
|
||||
}
|
||||
|
||||
public String getSecret() {
|
||||
return secret;
|
||||
}
|
||||
|
||||
public void setSecret(String secret) {
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public ClaimRepresentation getClaims() {
|
||||
return claims;
|
||||
}
|
||||
|
||||
public void setClaims(ClaimRepresentation claims) {
|
||||
this.claims = claims;
|
||||
}
|
||||
|
||||
public Integer getNotBefore() {
|
||||
return notBefore;
|
||||
}
|
||||
|
||||
public void setNotBefore(Integer notBefore) {
|
||||
this.notBefore = notBefore;
|
||||
}
|
||||
|
||||
public Boolean isPublicClient() {
|
||||
return publicClient;
|
||||
}
|
||||
|
||||
public void setPublicClient(Boolean publicClient) {
|
||||
this.publicClient = publicClient;
|
||||
}
|
||||
|
||||
public Boolean isDirectGrantsOnly() {
|
||||
return directGrantsOnly;
|
||||
}
|
||||
|
||||
public void setDirectGrantsOnly(Boolean directGrantsOnly) {
|
||||
this.directGrantsOnly = directGrantsOnly;
|
||||
}
|
||||
|
||||
public Boolean isFullScopeAllowed() {
|
||||
return fullScopeAllowed;
|
||||
}
|
||||
|
||||
public void setFullScopeAllowed(Boolean fullScopeAllowed) {
|
||||
this.fullScopeAllowed = fullScopeAllowed;
|
||||
}
|
||||
|
||||
public String getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setProtocol(String protocol) {
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public Map<String, String> getAttributes() {
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public void setAttributes(Map<String, String> attributes) {
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
public Boolean isFrontchannelLogout() {
|
||||
return frontchannelLogout;
|
||||
}
|
||||
|
||||
public void setFrontchannelLogout(Boolean frontchannelLogout) {
|
||||
this.frontchannelLogout = frontchannelLogout;
|
||||
}
|
||||
|
||||
public List<ClientIdentityProviderMappingRepresentation> getIdentityProviders() {
|
||||
return this.identityProviders;
|
||||
}
|
||||
|
||||
public void setIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders) {
|
||||
this.identityProviders = identityProviders;
|
||||
}
|
||||
|
||||
public List<ProtocolMapperRepresentation> getProtocolMappers() {
|
||||
return protocolMappers;
|
||||
}
|
||||
|
||||
public void setProtocolMappers(List<ProtocolMapperRepresentation> protocolMappers) {
|
||||
this.protocolMappers = protocolMappers;
|
||||
}
|
||||
@Deprecated
|
||||
public class OAuthClientRepresentation extends ApplicationRepresentation {
|
||||
}
|
||||
+3
-51
@@ -12,55 +12,7 @@ public class ProtocolMapperTypeRepresentation {
|
||||
protected String category;
|
||||
protected String helpText;
|
||||
|
||||
public static class ConfigProperty {
|
||||
protected String name;
|
||||
protected String label;
|
||||
protected String helpText;
|
||||
protected String type;
|
||||
protected Object defaultValue;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Object getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(Object defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public String getHelpText() {
|
||||
return helpText;
|
||||
}
|
||||
|
||||
public void setHelpText(String helpText) {
|
||||
this.helpText = helpText;
|
||||
}
|
||||
}
|
||||
|
||||
protected List<ConfigProperty> properties;
|
||||
protected List<ConfigPropertyRepresentation> properties;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
@@ -94,11 +46,11 @@ public class ProtocolMapperTypeRepresentation {
|
||||
this.helpText = helpText;
|
||||
}
|
||||
|
||||
public List<ConfigProperty> getProperties() {
|
||||
public List<ConfigPropertyRepresentation> getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(List<ConfigProperty> properties) {
|
||||
public void setProperties(List<ConfigPropertyRepresentation> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
}
|
||||
@@ -25,13 +25,6 @@ public class RealmRepresentation {
|
||||
protected Boolean verifyEmail;
|
||||
protected Boolean resetPasswordAllowed;
|
||||
|
||||
@Deprecated
|
||||
protected Boolean social;
|
||||
@Deprecated
|
||||
protected Boolean updateProfileOnInitialSocialLogin;
|
||||
@Deprecated
|
||||
protected Map<String, String> socialProviders;
|
||||
|
||||
protected Boolean userCacheEnabled;
|
||||
protected Boolean realmCacheEnabled;
|
||||
|
||||
@@ -55,9 +48,8 @@ public class RealmRepresentation {
|
||||
protected String passwordPolicy;
|
||||
protected List<UserRepresentation> users;
|
||||
protected List<ScopeMappingRepresentation> scopeMappings;
|
||||
protected Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings;
|
||||
protected List<ApplicationRepresentation> applications;
|
||||
protected List<OAuthClientRepresentation> oauthClients;
|
||||
protected Map<String, List<ScopeMappingRepresentation>> clientScopeMappings;
|
||||
protected List<ClientRepresentation> clients;
|
||||
protected Map<String, String> browserSecurityHeaders;
|
||||
protected Map<String, String> smtpServer;
|
||||
protected List<UserFederationProviderRepresentation> userFederationProviders;
|
||||
@@ -70,12 +62,25 @@ public class RealmRepresentation {
|
||||
protected List<String> eventsListeners;
|
||||
protected List<String> enabledEventTypes;
|
||||
private List<IdentityProviderRepresentation> identityProviders;
|
||||
private List<IdentityProviderMapperRepresentation> identityProviderMappers;
|
||||
private List<ProtocolMapperRepresentation> protocolMappers;
|
||||
private Boolean identityFederationEnabled;
|
||||
protected Boolean internationalizationEnabled;
|
||||
protected Set<String> supportedLocales;
|
||||
protected String defaultLocale;
|
||||
|
||||
@Deprecated
|
||||
protected Boolean social;
|
||||
@Deprecated
|
||||
protected Boolean updateProfileOnInitialSocialLogin;
|
||||
@Deprecated
|
||||
protected Map<String, String> socialProviders;
|
||||
@Deprecated
|
||||
protected Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings;
|
||||
@Deprecated
|
||||
protected List<ApplicationRepresentation> applications;
|
||||
@Deprecated
|
||||
protected List<OAuthClientRepresentation> oauthClients;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
@@ -101,14 +106,6 @@ public class RealmRepresentation {
|
||||
return applications;
|
||||
}
|
||||
|
||||
public ApplicationRepresentation resource(String name) {
|
||||
ApplicationRepresentation resource = new ApplicationRepresentation();
|
||||
if (applications == null) applications = new ArrayList<ApplicationRepresentation>();
|
||||
applications.add(resource);
|
||||
resource.setName(name);
|
||||
return resource;
|
||||
}
|
||||
|
||||
public void setUsers(List<UserRepresentation> users) {
|
||||
this.users = users;
|
||||
}
|
||||
@@ -121,8 +118,12 @@ public class RealmRepresentation {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setApplications(List<ApplicationRepresentation> applications) {
|
||||
this.applications = applications;
|
||||
public List<ClientRepresentation> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
public void setClients(List<ClientRepresentation> clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
public Boolean isEnabled() {
|
||||
@@ -321,22 +322,16 @@ public class RealmRepresentation {
|
||||
this.resetPasswordAllowed = resetPassword;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Boolean isSocial() {
|
||||
return social;
|
||||
}
|
||||
|
||||
public void setSocial(Boolean social) {
|
||||
this.social = social;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Boolean isUpdateProfileOnInitialSocialLogin() {
|
||||
return updateProfileOnInitialSocialLogin;
|
||||
}
|
||||
|
||||
public void setUpdateProfileOnInitialSocialLogin(Boolean updateProfileOnInitialSocialLogin) {
|
||||
this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
|
||||
}
|
||||
|
||||
public Map<String, String> getBrowserSecurityHeaders() {
|
||||
return browserSecurityHeaders;
|
||||
}
|
||||
@@ -345,14 +340,11 @@ public class RealmRepresentation {
|
||||
this.browserSecurityHeaders = browserSecurityHeaders;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Map<String, String> getSocialProviders() {
|
||||
return socialProviders;
|
||||
}
|
||||
|
||||
public void setSocialProviders(Map<String, String> socialProviders) {
|
||||
this.socialProviders = socialProviders;
|
||||
}
|
||||
|
||||
public Map<String, String> getSmtpServer() {
|
||||
return smtpServer;
|
||||
}
|
||||
@@ -361,22 +353,24 @@ public class RealmRepresentation {
|
||||
this.smtpServer = smtpServer;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public List<OAuthClientRepresentation> getOauthClients() {
|
||||
return oauthClients;
|
||||
}
|
||||
|
||||
public void setOauthClients(List<OAuthClientRepresentation> oauthClients) {
|
||||
this.oauthClients = oauthClients;
|
||||
public Map<String, List<ScopeMappingRepresentation>> getClientScopeMappings() {
|
||||
return clientScopeMappings;
|
||||
}
|
||||
|
||||
public void setClientScopeMappings(Map<String, List<ScopeMappingRepresentation>> clientScopeMappings) {
|
||||
this.clientScopeMappings = clientScopeMappings;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Map<String, List<ScopeMappingRepresentation>> getApplicationScopeMappings() {
|
||||
return applicationScopeMappings;
|
||||
}
|
||||
|
||||
public void setApplicationScopeMappings(Map<String, List<ScopeMappingRepresentation>> applicationScopeMappings) {
|
||||
this.applicationScopeMappings = applicationScopeMappings;
|
||||
}
|
||||
|
||||
public RolesRepresentation getRoles() {
|
||||
return roles;
|
||||
}
|
||||
@@ -577,4 +571,19 @@ public class RealmRepresentation {
|
||||
public void setDefaultLocale(String defaultLocale) {
|
||||
this.defaultLocale = defaultLocale;
|
||||
}
|
||||
|
||||
public List<IdentityProviderMapperRepresentation> getIdentityProviderMappers() {
|
||||
return identityProviderMappers;
|
||||
}
|
||||
|
||||
public void setIdentityProviderMappers(List<IdentityProviderMapperRepresentation> identityProviderMappers) {
|
||||
this.identityProviderMappers = identityProviderMappers;
|
||||
}
|
||||
|
||||
public void addIdentityProviderMapper(IdentityProviderMapperRepresentation rep) {
|
||||
if (identityProviderMappers == null) identityProviderMappers = new LinkedList<>();
|
||||
identityProviderMappers.add(rep);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -17,9 +17,10 @@ public class RoleRepresentation {
|
||||
|
||||
public static class Composites {
|
||||
protected Set<String> realm;
|
||||
protected Map<String, List<String>> client;
|
||||
@Deprecated
|
||||
protected Map<String, List<String>> application;
|
||||
|
||||
|
||||
public Set<String> getRealm() {
|
||||
return realm;
|
||||
}
|
||||
@@ -28,12 +29,17 @@ public class RoleRepresentation {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getApplication() {
|
||||
return application;
|
||||
public Map<String, List<String>> getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setApplication(Map<String, List<String>> application) {
|
||||
this.application = application;
|
||||
public void setClient(Map<String, List<String>> client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Map<String, List<String>> getApplication() {
|
||||
return application;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import java.util.Map;
|
||||
*/
|
||||
public class RolesRepresentation {
|
||||
protected List<RoleRepresentation> realm;
|
||||
protected Map<String, List<RoleRepresentation>> client;
|
||||
@Deprecated
|
||||
protected Map<String, List<RoleRepresentation>> application;
|
||||
|
||||
public List<RoleRepresentation> getRealm() {
|
||||
@@ -19,11 +21,16 @@ public class RolesRepresentation {
|
||||
this.realm = realm;
|
||||
}
|
||||
|
||||
public Map<String, List<RoleRepresentation>> getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
public void setClient(Map<String, List<RoleRepresentation>> client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Map<String, List<RoleRepresentation>> getApplication() {
|
||||
return application;
|
||||
}
|
||||
|
||||
public void setApplication(Map<String, List<RoleRepresentation>> application) {
|
||||
this.application = application;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.keycloak.representations.idm;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class UserConsentRepresentation {
|
||||
|
||||
protected String clientId;
|
||||
|
||||
// Key is protocol, Value is list of granted consents for this protocol
|
||||
protected Map<String, List<String>> grantedProtocolMappers;
|
||||
|
||||
protected List<String> grantedRealmRoles;
|
||||
|
||||
// Key is clientId, Value is list of granted roles of this client
|
||||
protected Map<String, List<String>> grantedClientRoles;
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getGrantedProtocolMappers() {
|
||||
return grantedProtocolMappers;
|
||||
}
|
||||
|
||||
public void setGrantedProtocolMappers(Map<String, List<String>> grantedProtocolMappers) {
|
||||
this.grantedProtocolMappers = grantedProtocolMappers;
|
||||
}
|
||||
|
||||
public List<String> getGrantedRealmRoles() {
|
||||
return grantedRealmRoles;
|
||||
}
|
||||
|
||||
public void setGrantedRealmRoles(List<String> grantedRealmRoles) {
|
||||
this.grantedRealmRoles = grantedRealmRoles;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getGrantedClientRoles() {
|
||||
return grantedClientRoles;
|
||||
}
|
||||
|
||||
public void setGrantedClientRoles(Map<String, List<String>> grantedClientRoles) {
|
||||
this.grantedClientRoles = grantedClientRoles;
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,14 @@ public class UserRepresentation {
|
||||
protected List<CredentialRepresentation> credentials;
|
||||
protected List<String> requiredActions;
|
||||
protected List<FederatedIdentityRepresentation> federatedIdentities;
|
||||
protected List<String> realmRoles;
|
||||
protected Map<String, List<String>> clientRoles;
|
||||
protected List<UserConsentRepresentation> clientConsents;
|
||||
|
||||
@Deprecated
|
||||
protected Map<String, List<String>> applicationRoles;
|
||||
@Deprecated
|
||||
protected List<SocialLinkRepresentation> socialLinks;
|
||||
protected List<String> realmRoles;
|
||||
protected Map<String, List<String>> applicationRoles;
|
||||
|
||||
public String getSelf() {
|
||||
return self;
|
||||
@@ -165,12 +169,25 @@ public class UserRepresentation {
|
||||
this.realmRoles = realmRoles;
|
||||
}
|
||||
|
||||
public Map<String, List<String>> getApplicationRoles() {
|
||||
return applicationRoles;
|
||||
public Map<String, List<String>> getClientRoles() {
|
||||
return clientRoles;
|
||||
}
|
||||
|
||||
public void setApplicationRoles(Map<String, List<String>> applicationRoles) {
|
||||
this.applicationRoles = applicationRoles;
|
||||
public void setClientRoles(Map<String, List<String>> clientRoles) {
|
||||
this.clientRoles = clientRoles;
|
||||
}
|
||||
|
||||
public List<UserConsentRepresentation> getClientConsents() {
|
||||
return clientConsents;
|
||||
}
|
||||
|
||||
public void setClientConsents(List<UserConsentRepresentation> clientConsents) {
|
||||
this.clientConsents = clientConsents;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Map<String, List<String>> getApplicationRoles() {
|
||||
return applicationRoles;
|
||||
}
|
||||
|
||||
public String getFederationLink() {
|
||||
|
||||
@@ -15,7 +15,6 @@ public class UserSessionRepresentation {
|
||||
private String ipAddress;
|
||||
private long start;
|
||||
private long lastAccess;
|
||||
private Map<String, String> applications = new HashMap<String, String>();
|
||||
private Map<String, String> clients = new HashMap<String, String>();
|
||||
|
||||
public String getId() {
|
||||
@@ -58,14 +57,6 @@ public class UserSessionRepresentation {
|
||||
this.lastAccess = lastAccess;
|
||||
}
|
||||
|
||||
public Map<String, String> getApplications() {
|
||||
return applications;
|
||||
}
|
||||
|
||||
public void setApplications(Map<String, String> applications) {
|
||||
this.applications = applications;
|
||||
}
|
||||
|
||||
public Map<String, String> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package org.keycloak.adapters;
|
||||
package org.keycloak.util;
|
||||
|
||||
import org.keycloak.constants.GenericConstants;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package org.keycloak.util;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class SystemEnvProperties extends Properties {
|
||||
|
||||
@Override
|
||||
public String getProperty(String key) {
|
||||
if (key.startsWith("env.")) {
|
||||
return System.getenv().get(key.substring(4));
|
||||
} else {
|
||||
return System.getProperty(key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
String value = getProperty(key);
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package org.keycloak.util;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.codehaus.jackson.JsonParser;
|
||||
import org.codehaus.jackson.io.IOContext;
|
||||
@@ -16,6 +18,8 @@ import org.codehaus.jackson.util.JsonParserDelegate;
|
||||
*/
|
||||
public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
|
||||
|
||||
private static final Properties properties = new SystemEnvProperties();
|
||||
|
||||
@Override
|
||||
protected JsonParser _createJsonParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
|
||||
JsonParser delegate = super._createJsonParser(data, offset, len, ctxt);
|
||||
@@ -34,8 +38,6 @@ public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
|
||||
return new SystemPropertiesAwareJsonParser(delegate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class SystemPropertiesAwareJsonParser extends JsonParserDelegate {
|
||||
|
||||
public SystemPropertiesAwareJsonParser(JsonParser d) {
|
||||
@@ -45,7 +47,7 @@ public class SystemPropertiesJsonParserFactory extends MappingJsonFactory {
|
||||
@Override
|
||||
public String getText() throws IOException {
|
||||
String orig = super.getText();
|
||||
return StringPropertyReplacer.replaceProperties(orig);
|
||||
return StringPropertyReplacer.replaceProperties(orig, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,10 @@ public class Time {
|
||||
return new Date(((long) time ) * 1000);
|
||||
}
|
||||
|
||||
public static long toMillis(int time) {
|
||||
return ((long) time) * 1000;
|
||||
}
|
||||
|
||||
public static void setOffset(int offset) {
|
||||
Time.offset = offset;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@ public class UriUtils {
|
||||
|
||||
public static String getOrigin(String uri) {
|
||||
String u = uri.toString();
|
||||
return u.substring(0, u.indexOf('/', 8));
|
||||
int e = u.indexOf('/', 8);
|
||||
return e != -1 ? u.substring(0, u.indexOf('/', 8)) : u;
|
||||
}
|
||||
|
||||
public static boolean isOrigin(String url) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonAnyGetter;
|
||||
import org.codehaus.jackson.annotate.JsonAnySetter;
|
||||
@@ -12,6 +13,7 @@ import org.codehaus.jackson.annotate.JsonUnwrapped;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.representations.IDToken;
|
||||
import org.keycloak.representations.JsonWebToken;
|
||||
import org.keycloak.representations.adapters.config.AdapterConfig;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
||||
@@ -20,6 +22,32 @@ import org.keycloak.util.JsonSerialization;
|
||||
*/
|
||||
public class JsonParserTest {
|
||||
|
||||
@Test
|
||||
public void regex() throws Exception {
|
||||
Pattern p = Pattern.compile(".*(?!\\.pdf)");
|
||||
if (p.matcher("foo.pdf").matches()) {
|
||||
System.out.println(".pdf no match");
|
||||
}
|
||||
if (p.matcher("foo.txt").matches()) {
|
||||
System.out.println("foo.txt matches");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtherClaims() throws Exception {
|
||||
String json = "{ \"floatData\" : 555.5," +
|
||||
"\"boolData\": true, " +
|
||||
"\"intData\": 1234," +
|
||||
"\"array\": [ \"val\", \"val2\"] }";
|
||||
JsonWebToken token = JsonSerialization.readValue(json, JsonWebToken.class);
|
||||
System.out.println(token.getOtherClaims().get("floatData").getClass().getName());
|
||||
System.out.println(token.getOtherClaims().get("boolData").getClass().getName());
|
||||
System.out.println(token.getOtherClaims().get("intData").getClass().getName());
|
||||
System.out.println(token.getOtherClaims().get("array").getClass().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnwrap() throws Exception {
|
||||
// just experimenting with unwrapped and any properties
|
||||
|
||||
Loaded 100 of 1612 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user