keycloak-developers
Changes
broker/core/pom.xml 48(+48 -0)
broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java 51(+51 -0)
broker/oidc/pom.xml 35(+35 -0)
broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory 1(+1 -0)
broker/pom.xml 23(+23 -0)
broker/saml/pom.xml 30(+30 -0)
broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory 1(+1 -0)
dependencies/server-all/pom.xml 12(+11 -1)
examples/cors/cors-realm.json 2(+0 -2)
export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java 24(+12 -12)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java 14(+7 -7)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/AccountFederatedIdentityBean.java 94(+46 -48)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java 10(+5 -5)
forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js 174(+130 -44)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider.html 45(+13 -32)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-facebook.html 1(+1 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-github.html 1(+1 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-google.html 1(+1 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html 117(+117 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html 100(+100 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html 68(+68 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-twitter.html 1(+1 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-login-settings.html 14(+0 -14)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/role-mappings.html 2(+1 -1)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-credentials.html 2(+1 -1)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-detail.html 4(+2 -2)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-federated-identity.html 16(+8 -8)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-sessions.html 2(+1 -1)
forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation.html 2(+1 -1)
forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java 4(+2 -2)
forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/IdentityProviderBean.java 60(+33 -27)
integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java 6(+3 -3)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java 22(+11 -11)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java 27(+11 -16)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java 22(+11 -11)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java 54(+25 -29)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java 68(+34 -34)
pom.xml 3(+2 -1)
services/pom.xml 5(+5 -0)
services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java 145(+145 -0)
services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java 54(+44 -10)
services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java 356(+356 -0)
social/core/pom.xml 20(+1 -19)
social/facebook/pom.xml 4(+2 -2)
social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProviderFactory.java 44(+44 -0)
social/facebook/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory 1(+1 -0)
social/github/pom.xml 9(+7 -2)
social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProviderFactory.java 44(+44 -0)
social/github/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory 1(+1 -0)
social/google/pom.xml 9(+7 -2)
social/google/src/main/java/org/keycloak/social/google/GoogleIdentityProviderFactory.java 44(+44 -0)
social/google/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory 1(+1 -0)
social/twitter/pom.xml 14(+12 -2)
social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProviderFactory.java 44(+44 -0)
social/twitter/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory 1(+1 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java 1(+0 -1)
testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java 8(+4 -4)
Details
broker/core/pom.xml 48(+48 -0)
diff --git a/broker/core/pom.xml b/broker/core/pom.xml
new file mode 100755
index 0000000..ab4389d
--- /dev/null
+++ b/broker/core/pom.xml
@@ -0,0 +1,48 @@
+<?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-broker-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.2.0.Beta1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-broker-core</artifactId>
+ <name>Keycloak Broker Core</name>
+ <description/>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <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.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
new file mode 100644
index 0000000..f38d840
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.provider;
+
+import org.keycloak.models.IdentityProviderModel;
+
+/**
+ * @author Pedro Igor
+ */
+public abstract class AbstractIdentityProvider<C extends IdentityProviderModel> implements IdentityProvider<C> {
+
+ private final C config;
+
+ public AbstractIdentityProvider(C config) {
+ this.config = config;
+ }
+
+ public C getConfig() {
+ return this.config;
+ }
+
+ @Override
+ public void close() {
+ // no-op
+ }
+
+}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java
new file mode 100644
index 0000000..b615f38
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java
@@ -0,0 +1,51 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.provider;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+public abstract class AbstractIdentityProviderFactory<T extends IdentityProvider> implements IdentityProviderFactory<T> {
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public T create(KeycloakSession session) {
+ return null;
+ }
+
+ @Override
+ public Map<String, String> parseConfig(InputStream inputStream) {
+ return new HashMap<String, String>();
+ }
+}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationRequest.java b/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationRequest.java
new file mode 100644
index 0000000..bd0898f
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationRequest.java
@@ -0,0 +1,76 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.provider;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.RealmModel;
+
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * @author Pedro Igor
+ */
+public class AuthenticationRequest {
+
+ private final UriInfo uriInfo;
+ private final String state;
+ private final HttpRequest httpRequest;
+ private final RealmModel realm;
+ private final String redirectUri;
+ private final ClientSessionModel clientSession;
+
+ public AuthenticationRequest(RealmModel realm, ClientSessionModel clientSession, HttpRequest httpRequest, UriInfo uriInfo, String state, String redirectUri) {
+ this.realm = realm;
+ this.httpRequest = httpRequest;
+ this.uriInfo = uriInfo;
+ this.state = state;
+ this.redirectUri = redirectUri;
+ this.clientSession = clientSession;
+ }
+
+ public UriInfo getUriInfo() {
+ return this.uriInfo;
+ }
+
+ public String getState() {
+ return this.state;
+ }
+
+ public HttpRequest getHttpRequest() {
+ return this.httpRequest;
+ }
+
+ public RealmModel getRealm() {
+ return this.realm;
+ }
+
+ /**
+ * <p>Returns the redirect url that must be included in an authentication request in order to process responses from an
+ * identity provider.</p>
+ *
+ * @return
+ */
+ public String getRedirectUri() {
+ return this.redirectUri;
+ }
+
+ public ClientSessionModel getClientSession() {
+ return this.clientSession;
+ }
+}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationResponse.java b/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationResponse.java
new file mode 100644
index 0000000..641c7da
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AuthenticationResponse.java
@@ -0,0 +1,57 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.provider;
+
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author Pedro Igor
+ */
+public class AuthenticationResponse {
+
+ private final Response response;
+ private final FederatedIdentity user;
+
+ private AuthenticationResponse(FederatedIdentity user) {
+ this.user = user;
+ this.response = null;
+ }
+
+ private AuthenticationResponse(Response response) {
+ this.user = null;
+ this.response = response;
+ }
+
+ public Response getResponse() {
+ return this.response;
+ }
+
+ public FederatedIdentity getUser() {
+ return this.user;
+ }
+
+ public static AuthenticationResponse end(FederatedIdentity identity) {
+ return new AuthenticationResponse(identity);
+ }
+
+ public static AuthenticationResponse temporaryRedirect(URI url) {
+ return new AuthenticationResponse(Response.temporaryRedirect(url).build());
+ }
+
+}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
new file mode 100644
index 0000000..cfe6f41
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProvider.java
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.provider;
+
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author Pedro Igor
+ */
+public interface IdentityProvider<C extends IdentityProviderModel> extends Provider {
+
+ /**
+ * <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
+ */
+ AuthenticationResponse handleRequest(AuthenticationRequest request);
+
+ /**
+ * <p>Obtains state information sent to the identity provider during the authentication request. Implementations must always
+ * return the same state in order to check the validity of a response from the identity provider.</p>
+ *
+ * <p>This method is invoked on each response from the identity provider.</p>
+ *
+ * @param request The request sent by the identity provider in a response to an authentication request.
+ * @return
+ */
+ String getRelayState(AuthenticationRequest request);
+
+ /**
+ * <p>Handles a response from the identity provider after a successful authentication request is made. Usually, the response will
+ * contain all the necessary information in order to trust the authentication performed by the identity provider and resolve
+ * the identity information for the authenticating user.</p>
+ *
+ * <p>If the response is trusted and proves user's authenticity, this method may return a
+ * {@link FederatedIdentity} in the response. In this case, the authentication flow stops.</p>
+ *
+ * @param request
+ * @return
+ */
+ AuthenticationResponse handleResponse(AuthenticationRequest request);
+}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java
new file mode 100644
index 0000000..1f1bdcc
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.provider;
+
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.provider.ProviderFactory;
+
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+public interface IdentityProviderFactory<T extends IdentityProvider> extends ProviderFactory<T> {
+
+ /**
+ * <p>A friendly name for this factory.</p>
+ *
+ * @return
+ */
+ String getName();
+
+ /**
+ * <p>Creates an {@link IdentityProvider} based on the configuration contained in
+ * <code>model</code>.</p>
+ *
+ * @param model The configuration to be used to create the identity provider.
+ * @return
+ */
+ T create(IdentityProviderModel model);
+
+ /**
+ * <p>Creates an {@link IdentityProvider} based on the configuration from
+ * <code>inputStream</code>.</p>
+ *
+ * @param model The model containing the common abd basic configuration for an identity provider.
+ * @param inputStream The input stream from where configuration will be loaded from..
+ * @return
+ */
+ Map<String, String> parseConfig(InputStream inputStream);
+}
\ No newline at end of file
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java
new file mode 100644
index 0000000..de9872a
--- /dev/null
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java
@@ -0,0 +1,45 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.provider;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author Pedro Igor
+ */
+public class IdentityProviderSpi implements Spi {
+
+ public static final String IDENTITY_PROVIDER_SPI_NAME = "identity_provider";
+
+ @Override
+ public String getName() {
+ return IDENTITY_PROVIDER_SPI_NAME;
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return IdentityProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return IdentityProviderFactory.class;
+ }
+}
diff --git a/broker/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/broker/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100755
index 0000000..d4ef41b
--- /dev/null
+++ b/broker/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1 @@
+org.keycloak.broker.provider.IdentityProviderSpi
\ No newline at end of file
broker/oidc/pom.xml 35(+35 -0)
diff --git a/broker/oidc/pom.xml b/broker/oidc/pom.xml
new file mode 100755
index 0000000..313db2d
--- /dev/null
+++ b/broker/oidc/pom.xml
@@ -0,0 +1,35 @@
+<?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-broker-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.2.0.Beta1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-broker-oidc</artifactId>
+ <name>Keycloak Broker - OpenID Connect Identity Provider</name>
+ <description/>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-broker-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
new file mode 100644
index 0000000..a34438e
--- /dev/null
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
@@ -0,0 +1,156 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.oidc;
+
+import org.codehaus.jackson.JsonNode;
+import org.codehaus.jackson.map.ObjectMapper;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.broker.oidc.util.SimpleHttp;
+import org.keycloak.broker.provider.AbstractIdentityProvider;
+import org.keycloak.broker.provider.AuthenticationRequest;
+import org.keycloak.broker.provider.AuthenticationResponse;
+import org.keycloak.broker.provider.FederatedIdentity;
+
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.net.URI;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Pedro Igor
+ */
+public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityProviderConfig> extends AbstractIdentityProvider<C> {
+
+ public static final String OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code";
+ protected static ObjectMapper mapper = new ObjectMapper();
+
+ public static final String OAUTH2_PARAMETER_ACCESS_TOKEN = "access_token";
+ public static final String OAUTH2_PARAMETER_SCOPE = "scope";
+ public static final String OAUTH2_PARAMETER_STATE = "state";
+ public static final String OAUTH2_PARAMETER_RESPONSE_TYPE = "response_type";
+ public static final String OAUTH2_PARAMETER_REDIRECT_URI = "redirect_uri";
+ public static final String OAUTH2_PARAMETER_CODE = "code";
+ public static final String OAUTH2_PARAMETER_CLIENT_ID = "client_id";
+ public static final String OAUTH2_PARAMETER_CLIENT_SECRET = "client_secret";
+ public static final String OAUTH2_PARAMETER_GRANT_TYPE = "grant_type";
+
+ public AbstractOAuth2IdentityProvider(C config) {
+ super(config);
+ }
+
+ @Override
+ public AuthenticationResponse handleRequest(AuthenticationRequest request) {
+ try {
+ URI authorizationUrl = createAuthorizationUrl(request).build();
+
+ return AuthenticationResponse.temporaryRedirect(authorizationUrl);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not create authentication request.", e);
+ }
+ }
+
+ @Override
+ public String getRelayState(AuthenticationRequest request) {
+ UriInfo uriInfo = request.getUriInfo();
+ return uriInfo.getQueryParameters().getFirst(OAUTH2_PARAMETER_STATE);
+ }
+
+ @Override
+ public AuthenticationResponse handleResponse(AuthenticationRequest request) {
+ UriInfo uriInfo = request.getUriInfo();
+ String error = uriInfo.getQueryParameters().getFirst(OAuth2Constants.ERROR);
+
+ if (error != null) {
+ if (error.equals("access_denied")) {
+ throw new RuntimeException("Access denied.");
+ } else {
+ throw new RuntimeException(error);
+ }
+ }
+
+ try {
+ String authorizationCode = uriInfo.getQueryParameters().getFirst(OAUTH2_PARAMETER_CODE);
+
+ if (authorizationCode != null) {
+ String response = SimpleHttp.doPost(getConfig().getTokenUrl())
+ .param(OAUTH2_PARAMETER_CODE, authorizationCode)
+ .param(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
+ .param(OAUTH2_PARAMETER_CLIENT_SECRET, getConfig().getClientSecret())
+ .param(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri())
+ .param(OAUTH2_PARAMETER_GRANT_TYPE, OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE).asString();
+
+ return doHandleResponse(response);
+ }
+
+ throw new RuntimeException("No authorization code from identity provider.");
+ } catch (Exception e) {
+ throw new RuntimeException("Could not process response from identity provider.", e);
+ }
+ }
+
+ protected AuthenticationResponse doHandleResponse(String response) throws IOException {
+ String token = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
+
+ if (token == null) {
+ throw new RuntimeException("No access token from server.");
+ }
+
+ return AuthenticationResponse.end(getFederatedIdentity(token));
+ }
+
+ protected String extractTokenFromResponse(String response, String tokenName) throws IOException {
+ if (response.startsWith("{")) {
+ return mapper.readTree(response).get(tokenName).getTextValue();
+ } else {
+ Matcher matcher = Pattern.compile(tokenName + "=([^&]+)").matcher(response);
+
+ if (matcher.find()) {
+ return matcher.group(1);
+ }
+ }
+
+ return null;
+ }
+
+ protected FederatedIdentity getFederatedIdentity(String accessToken) {
+ throw new RuntimeException("Not implemented.");
+ };
+
+ protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
+ return UriBuilder.fromPath(getConfig().getAuthorizationUrl())
+ .queryParam(OAUTH2_PARAMETER_SCOPE, getConfig().getDefaultScope())
+ .queryParam(OAUTH2_PARAMETER_STATE, request.getState())
+ .queryParam(OAUTH2_PARAMETER_RESPONSE_TYPE, "code")
+ .queryParam(OAUTH2_PARAMETER_CLIENT_ID, getConfig().getClientId())
+ .queryParam(OAUTH2_PARAMETER_REDIRECT_URI, request.getRedirectUri());
+ }
+
+ protected String getJsonProperty(JsonNode jsonNode, String name) {
+ if (jsonNode.has(name)) {
+ return jsonNode.get(name).asText();
+ }
+
+ return null;
+ }
+
+ protected JsonNode asJsonNode(String json) throws IOException {
+ return mapper.readTree(json);
+ }
+}
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OAuth2IdentityProviderConfig.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OAuth2IdentityProviderConfig.java
new file mode 100644
index 0000000..52be1cd
--- /dev/null
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OAuth2IdentityProviderConfig.java
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.oidc;
+
+import org.keycloak.models.IdentityProviderModel;
+
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+public class OAuth2IdentityProviderConfig extends IdentityProviderModel {
+
+ public OAuth2IdentityProviderConfig(String providerId, String id, String name, Map<String, String> config) {
+ super(providerId, id, name, config);
+ }
+
+ public String getAuthorizationUrl() {
+ return getConfig().get("authorizationUrl");
+ }
+
+ public void setAuthorizationUrl(String authorizationUrl) {
+ getConfig().put("authorizationUrl", authorizationUrl);
+ }
+
+ public String getTokenUrl() {
+ return getConfig().get("tokenUrl");
+ }
+
+ public void setTokenUrl(String tokenUrl) {
+ getConfig().put("tokenUrl", tokenUrl);
+ }
+
+ public String getUserInfoUrl() {
+ return getConfig().get("userInfoUrl");
+ }
+
+ public void setUserInfoUrl(String userInfoUrl) {
+ getConfig().put("userInfoUrl", userInfoUrl);
+ }
+
+ public String getClientId() {
+ return getConfig().get("clientId");
+ }
+
+ public void setClientId(String clientId) {
+ getConfig().put("clientId", clientId);
+ }
+
+ public String getClientSecret() {
+ return getConfig().get("clientSecret");
+ }
+
+ public void setClientSecret(String clientSecret) {
+ getConfig().put("clientSecret", clientSecret);
+ }
+
+ public String getDefaultScope() {
+ return getConfig().get("defaultScope");
+ }
+
+ public void setDefaultScope(String defaultScope) {
+ getConfig().put("defaultScope", defaultScope);
+ }
+}
\ No newline at end of file
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
new file mode 100644
index 0000000..f82ccc6
--- /dev/null
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProvider.java
@@ -0,0 +1,128 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.oidc;
+
+import org.codehaus.jackson.JsonNode;
+import org.keycloak.broker.oidc.util.SimpleHttp;
+import org.keycloak.broker.provider.AuthenticationRequest;
+import org.keycloak.broker.provider.AuthenticationResponse;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.jose.jws.JWSInput;
+
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+
+/**
+ * @author Pedro Igor
+ */
+public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIdentityProviderConfig> {
+
+ public static final String OAUTH2_PARAMETER_PROMPT = "prompt";
+ public static final String OIDC_PARAMETER_ID_TOKEN = "id_token";
+
+ public OIDCIdentityProvider(OIDCIdentityProviderConfig config) {
+ super(config);
+ }
+
+ @Override
+ protected UriBuilder createAuthorizationUrl(AuthenticationRequest request) {
+ return super.createAuthorizationUrl(request)
+ .queryParam(OAUTH2_PARAMETER_PROMPT, getConfig().getPrompt());
+ }
+
+ @Override
+ protected AuthenticationResponse doHandleResponse(String response) throws IOException {
+ String accessToken = extractTokenFromResponse(response, OAUTH2_PARAMETER_ACCESS_TOKEN);
+
+ if (accessToken == null) {
+ throw new RuntimeException("No access_token from server.");
+ }
+
+ String idToken = extractTokenFromResponse(response, OIDC_PARAMETER_ID_TOKEN);
+
+ validateIdToken(idToken);
+
+ try {
+ JsonNode userInfo = SimpleHttp.doGet(getConfig().getUserInfoUrl())
+ .header("Authorization", "Bearer " + accessToken)
+ .asJson();
+
+ String id = getJsonProperty(userInfo, "sub");
+ String name = getJsonProperty(userInfo, "name");
+ String preferredUsername = getJsonProperty(userInfo, "preferred_username");
+ String email = getJsonProperty(userInfo, "email");
+
+ FederatedIdentity identity = new FederatedIdentity(id);
+
+ identity.setId(id);
+ identity.setName(name);
+ identity.setEmail(email);
+
+ if (preferredUsername == null) {
+ preferredUsername = email;
+ }
+
+ if (preferredUsername == null) {
+ preferredUsername = id;
+ }
+
+ identity.setUsername(preferredUsername);
+
+ return AuthenticationResponse.end(identity);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not fetch attributes from userinfo endpoint.", e);
+ }
+ }
+
+ private void validateIdToken(String idToken) {
+ if (idToken == null) {
+ throw new RuntimeException("No id_token from server.");
+ }
+
+ try {
+ JsonNode idTokenInfo = asJsonNode(decodeJWS(idToken));
+
+ String aud = getJsonProperty(idTokenInfo, "aud");
+ String iss = getJsonProperty(idTokenInfo, "iss");
+
+ if (aud != null && !aud.equals(getConfig().getClientId())) {
+ throw new RuntimeException("Wrong audience from id_token..");
+ }
+
+ String trustedIssuers = getConfig().getIssuer();
+
+ if (trustedIssuers != null) {
+ String[] issuers = trustedIssuers.split(",");
+
+ for (String trustedIssuer : issuers) {
+ if (iss != null && iss.equals(trustedIssuer.trim())) {
+ return;
+ }
+ }
+
+ throw new RuntimeException("Wrong issuer from id_token..");
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Could not decode id token.", e);
+ }
+ }
+
+ private String decodeJWS(String token) {
+ return new JWSInput(token).readContentAsString();
+ }
+}
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
new file mode 100644
index 0000000..302221a
--- /dev/null
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderConfig.java
@@ -0,0 +1,56 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.oidc;
+
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+public class OIDCIdentityProviderConfig extends OAuth2IdentityProviderConfig {
+
+ public OIDCIdentityProviderConfig(String providerId, String id, String name, Map<String, String> config) {
+ super(providerId, id, name, config);
+ }
+
+ public String getPrompt() {
+ String prompt = getConfig().get("prompt");
+
+ if (prompt == null || "".equals(prompt)) {
+ return "none";
+ }
+
+ return prompt;
+ }
+
+ @Override
+ public String getDefaultScope() {
+ String scope = super.getDefaultScope();
+
+ if (scope == null || "".equals(scope)) {
+ scope = "openid";
+ }
+
+ return scope;
+ }
+
+ public String getIssuer() {
+ return getConfig().get("issuer");
+ }
+
+}
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
new file mode 100644
index 0000000..10316d9
--- /dev/null
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/OIDCIdentityProviderFactory.java
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.oidc;
+
+import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.models.IdentityProviderModel;
+
+/**
+ * @author Pedro Igor
+ */
+public class OIDCIdentityProviderFactory extends AbstractIdentityProviderFactory<OIDCIdentityProvider> {
+
+ @Override
+ public String getName() {
+ return "OpenID Connect v1.0";
+ }
+
+ @Override
+ public OIDCIdentityProvider create(IdentityProviderModel model) {
+ return new OIDCIdentityProvider(new OIDCIdentityProviderConfig(getId(), model.getId(), model.getName(), model.getConfig()));
+ }
+
+ @Override
+ public String getId() {
+ return "oidc";
+ }
+}
diff --git a/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory b/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory
new file mode 100644
index 0000000..50071ed
--- /dev/null
+++ b/broker/oidc/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory
@@ -0,0 +1 @@
+org.keycloak.broker.oidc.OIDCIdentityProviderFactory
\ No newline at end of file
broker/pom.xml 23(+23 -0)
diff --git a/broker/pom.xml b/broker/pom.xml
new file mode 100755
index 0000000..7121b2b
--- /dev/null
+++ b/broker/pom.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.2.0.Beta1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-broker-parent</artifactId>
+ <name>Keycloak Broker Parent</name>
+ <description/>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>core</module>
+ <module>oidc</module>
+ <module>saml</module>
+ </modules>
+
+</project>
broker/saml/pom.xml 30(+30 -0)
diff --git a/broker/saml/pom.xml b/broker/saml/pom.xml
new file mode 100755
index 0000000..a0e6706
--- /dev/null
+++ b/broker/saml/pom.xml
@@ -0,0 +1,30 @@
+<?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-broker-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.2.0.Beta1-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-broker-saml</artifactId>
+ <name>Keycloak Broker - SAML Identity Provider</name>
+ <description/>
+ <packaging>jar</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-broker-core</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.picketlink</groupId>
+ <artifactId>picketlink-federation</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
new file mode 100644
index 0000000..59863f8
--- /dev/null
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -0,0 +1,255 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.saml;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.broker.provider.AbstractIdentityProvider;
+import org.keycloak.broker.provider.AuthenticationRequest;
+import org.keycloak.broker.provider.AuthenticationResponse;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.picketlink.common.constants.JBossSAMLConstants;
+import org.picketlink.common.constants.JBossSAMLURIConstants;
+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.request.SAML2Request;
+import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
+import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature;
+import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
+import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
+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.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.assertion.SubjectType.STSubType;
+import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType;
+import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
+import org.picketlink.identity.federation.saml.v2.protocol.ResponseType.RTChoiceType;
+import org.picketlink.identity.federation.saml.v2.protocol.StatusCodeType;
+import org.picketlink.identity.federation.saml.v2.protocol.StatusDetailType;
+import org.picketlink.identity.federation.saml.v2.protocol.StatusType;
+import org.picketlink.identity.federation.web.util.PostBindingUtil;
+import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import javax.xml.namespace.QName;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * @author Pedro Igor
+ */
+public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityProviderConfig> {
+
+ private static final String SAML_REQUEST_PARAMETER = "SAMLRequest";
+ private static final String SAML_RESPONSE_PARAMETER = "SAMLResponse";
+ private static final String RELAY_STATE_PARAMETER = "RelayState";
+
+ private SAML2Signature saml2Signature = new SAML2Signature();
+
+ public SAMLIdentityProvider(SAMLIdentityProviderConfig config) {
+ super(config);
+ }
+
+ @Override
+ public AuthenticationResponse handleRequest(AuthenticationRequest request) {
+ try {
+ UriInfo uriInfo = request.getUriInfo();
+ String issuerURL = UriBuilder.fromUri(uriInfo.getBaseUri()).build().toString();
+ String destinationUrl = getConfig().getSingleSignOnServiceUrl();
+ SAML2Request samlRequest = new SAML2Request();
+ String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
+
+ if (nameIDPolicyFormat == null) {
+ nameIDPolicyFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
+ }
+
+ samlRequest.setNameIDFormat(nameIDPolicyFormat);
+
+ AuthnRequestType authn = samlRequest
+ .createAuthnRequestType(IDGenerator.create("ID_"), request.getRedirectUri(), destinationUrl, issuerURL);
+
+ authn.setProtocolBinding(URI.create(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get()));
+ authn.setForceAuthn(getConfig().isForceAuthn());
+
+ Document authnDoc = samlRequest.convert(authn);
+
+ if (getConfig().isWantAuthnRequestsSigned()) {
+ PrivateKey privateKey = request.getRealm().getPrivateKey();
+ PublicKey publicKey = request.getRealm().getPublicKey();
+
+ if (privateKey == null) {
+ throw new RuntimeException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a private key.");
+ }
+
+ if (publicKey == null) {
+ throw new RuntimeException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + request.getRealm().getName() + "] does not have a public key.");
+ }
+
+ KeyPair keypair = new KeyPair(publicKey, privateKey);
+
+ this.saml2Signature.signSAMLDocument(authnDoc, keypair);
+ }
+
+ byte[] responseBytes = DocumentUtil.getDocumentAsString(authnDoc).getBytes("UTF-8");
+ String urlEncodedResponse = RedirectBindingUtil.deflateBase64URLEncode(responseBytes);
+ URI redirectUri = UriBuilder.fromPath(destinationUrl)
+ .queryParam(SAML_REQUEST_PARAMETER, urlEncodedResponse)
+ .queryParam(RELAY_STATE_PARAMETER, request.getState()).build();
+
+ return AuthenticationResponse.temporaryRedirect(redirectUri);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not create authentication request.", e);
+ }
+ }
+
+ @Override
+ public String getRelayState(AuthenticationRequest request) {
+ HttpRequest httpRequest = request.getHttpRequest();
+ return httpRequest.getFormParameters().getFirst(RELAY_STATE_PARAMETER);
+ }
+
+ @Override
+ public AuthenticationResponse handleResponse(AuthenticationRequest request) {
+ HttpRequest httpRequest = request.getHttpRequest();
+ String samlResponse = httpRequest.getFormParameters().getFirst(SAML_RESPONSE_PARAMETER);
+
+ if (samlResponse == null) {
+ throw new RuntimeException("No response from SAML identity provider.");
+ }
+
+ try {
+ SAML2Request saml2Request = new SAML2Request();
+ ResponseType responseType = (ResponseType) saml2Request
+ .getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(samlResponse, "UTF-8")));
+ AssertionType assertion = getAssertion(request, saml2Request, responseType);
+
+ SubjectType subject = assertion.getSubject();
+ STSubType subType = subject.getSubType();
+ NameIDType subjectNameID = (NameIDType) subType.getBaseID();
+
+ FederatedIdentity user = new FederatedIdentity(subjectNameID.getValue());
+
+ user.setUsername(subjectNameID.getValue());
+
+ if (subjectNameID.getFormat().toString().equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
+ user.setEmail(subjectNameID.getValue());
+ }
+
+ return AuthenticationResponse.end(user);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not process response from SAML identity provider.", e);
+ }
+ }
+
+ private AssertionType getAssertion(AuthenticationRequest request, SAML2Request saml2Request, ResponseType responseType) throws ProcessingException {
+ validateStatusResponse(responseType);
+ validateSignature(saml2Request);
+
+ List<RTChoiceType> assertions = responseType.getAssertions();
+
+ if (assertions.isEmpty()) {
+ throw new RuntimeException("No assertion from response.");
+ }
+
+ RTChoiceType rtChoiceType = assertions.get(0);
+ EncryptedAssertionType encryptedAssertion = rtChoiceType.getEncryptedAssertion();
+
+ if (encryptedAssertion != null) {
+ decryptAssertion(responseType, request.getRealm().getPrivateKey());
+
+ }
+
+ return responseType.getAssertions().get(0).getAssertion();
+ }
+
+ private void validateSignature(SAML2Request saml2Request) throws ProcessingException {
+ if (getConfig().isValidateSignature()) {
+ X509Certificate certificate = XMLSignatureUtil.getX509CertificateFromKeyInfoString(getConfig().getSigningPublicKey().replaceAll("\\s", ""));
+ SAMLDocumentHolder samlDocumentHolder = saml2Request.getSamlDocumentHolder();
+ Document samlDocument = samlDocumentHolder.getSamlDocument();
+
+ this.saml2Signature.validate(samlDocument, certificate.getPublicKey());
+ }
+ }
+
+ private void validateStatusResponse(ResponseType responseType) {
+ StatusType status = responseType.getStatus();
+ StatusCodeType statusCode = status.getStatusCode();
+
+ if (!JBossSAMLURIConstants.STATUS_SUCCESS.get().equals(statusCode.getValue().toString())) {
+ StatusDetailType statusDetailType = status.getStatusDetail();
+ StringBuilder detailMessage = new StringBuilder();
+
+ if (statusDetailType != null) {
+ for (Object statusDetail : statusDetailType.getAny()) {
+ detailMessage.append(statusDetail);
+ }
+ } else {
+ detailMessage.append("none");
+ }
+
+ throw new RuntimeException("Authentication failed with code [" + statusCode.getValue() + " and detail [" + detailMessage.toString() + ".");
+ }
+ }
+
+ private ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ProcessingException {
+ SAML2Response saml2Response = new SAML2Response();
+
+ try {
+ Document doc = saml2Response.convert(responseType);
+ Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
+
+ if (enc == null) {
+ throw new RuntimeException("No encrypted assertion found.");
+ }
+
+ String oldID = enc.getAttribute(JBossSAMLConstants.ID.get());
+ Document newDoc = DocumentUtil.createDocument();
+ Node importedNode = newDoc.importNode(enc, true);
+ newDoc.appendChild(importedNode);
+
+ Element decryptedDocumentElement = XMLEncryptionUtil.decryptElementInDocument(newDoc, privateKey);
+ SAMLParser parser = new SAMLParser();
+
+ JAXPValidationUtil.checkSchemaValidation(decryptedDocumentElement);
+ AssertionType assertion = (AssertionType) parser.parse(StaxParserUtil.getXMLEventReader(DocumentUtil
+ .getNodeAsStream(decryptedDocumentElement)));
+
+ responseType.replaceAssertion(oldID, new RTChoiceType(assertion));
+
+ return responseType;
+ } catch (Exception e) {
+ throw new RuntimeException("Could not decrypt assertion.", e);
+ }
+ }
+
+}
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
new file mode 100644
index 0000000..b0b8724
--- /dev/null
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
@@ -0,0 +1,92 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.saml;
+
+import org.keycloak.models.IdentityProviderModel;
+
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+public class SAMLIdentityProviderConfig extends IdentityProviderModel {
+
+ public SAMLIdentityProviderConfig() {
+ super();
+ }
+
+ public SAMLIdentityProviderConfig(String providerId, String id, String name, Map<String, String> config) {
+ super(providerId, id, name, config);
+ }
+
+ public String getSingleSignOnServiceUrl() {
+ return getConfig().get("singleSignOnServiceUrl");
+ }
+
+ public void setSingleSignOnServiceUrl(String singleSignOnServiceUrl) {
+ getConfig().put("singleSignOnServiceUrl", singleSignOnServiceUrl);
+ }
+
+ public boolean isValidateSignature() {
+ return Boolean.valueOf(getConfig().get("validateSignature"));
+ }
+
+ public void setValidateSignature(boolean validateSignature) {
+ getConfig().put("validateSignature", String.valueOf(validateSignature));
+ }
+
+ public boolean isForceAuthn() {
+ return Boolean.valueOf(getConfig().get("forceAuthn"));
+ }
+
+ public void setForceAuthn(boolean forceAuthn) {
+ getConfig().put("forceAuthn", String.valueOf(forceAuthn));
+ }
+
+ public String getSigningPublicKey() {
+ return getConfig().get("signingPublicKey");
+ }
+
+ public void setSigningPublicKey(String signingPublicKey) {
+ getConfig().put("signingPublicKey", signingPublicKey);
+ }
+
+ public String getNameIDPolicyFormat() {
+ return getConfig().get("nameIDPolicyFormat");
+ }
+
+ public void setNameIDPolicyFormat(String signingPublicKey) {
+ getConfig().put("nameIDPolicyFormat", signingPublicKey);
+ }
+
+ public boolean isWantAuthnRequestsSigned() {
+ return Boolean.valueOf(getConfig().get("wantAuthnRequestsSigned"));
+ }
+
+ public void setWantAuthnRequestsSigned(boolean wantAuthnRequestsSigned) {
+ getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
+ }
+
+ public String getEncryptionPublicKey() {
+ return getConfig().get("encryptionPublicKey");
+ }
+
+ public void setEncryptionPublicKey(String encryptionPublicKey) {
+ getConfig().put("encryptionPublicKey", encryptionPublicKey);
+ }
+}
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
new file mode 100644
index 0000000..9cbd107
--- /dev/null
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java
@@ -0,0 +1,125 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.broker.saml;
+
+import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.models.IdentityProviderModel;
+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.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.w3c.dom.Element;
+
+import javax.xml.namespace.QName;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory<SAMLIdentityProvider> {
+
+ @Override
+ public String getName() {
+ return "SAML v2.0";
+ }
+
+ @Override
+ public SAMLIdentityProvider create(IdentityProviderModel model) {
+ return new SAMLIdentityProvider(new SAMLIdentityProviderConfig(getId(), model.getId(), model.getName(), model.getConfig()));
+ }
+
+ @Override
+ public Map<String, String> parseConfig(InputStream inputStream) {
+ try {
+ Object parsedObject = new SAMLParser().parse(inputStream);
+ EntityDescriptorType entityType;
+
+ if (EntitiesDescriptorType.class.isInstance(parsedObject)) {
+ entityType = (EntityDescriptorType) ((EntitiesDescriptorType) parsedObject).getEntityDescriptor().get(0);
+ } else {
+ entityType = (EntityDescriptorType) parsedObject;
+ }
+
+ List<EntityDescriptorType.EDTChoiceType> choiceType = entityType.getChoiceType();
+
+ if (!choiceType.isEmpty()) {
+ EntityDescriptorType.EDTChoiceType edtChoiceType = choiceType.get(0);
+ List<EntityDescriptorType.EDTDescriptorChoiceType> descriptors = edtChoiceType.getDescriptors();
+
+ if (!descriptors.isEmpty()) {
+ EntityDescriptorType.EDTDescriptorChoiceType edtDescriptorChoiceType = descriptors.get(0);
+ IDPSSODescriptorType idpDescriptor = edtDescriptorChoiceType.getIdpDescriptor();
+
+ if (idpDescriptor != null) {
+ SAMLIdentityProviderConfig samlIdentityProviderConfig = new SAMLIdentityProviderConfig();
+
+ samlIdentityProviderConfig.setSingleSignOnServiceUrl(idpDescriptor.getSingleSignOnService().get(0).getLocation().toString());
+ samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned());
+ samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned());
+
+ List<KeyDescriptorType> keyDescriptor = idpDescriptor.getKeyDescriptor();
+ String defaultPublicKey = null;
+
+ if (keyDescriptor != null) {
+ for (KeyDescriptorType keyDescriptorType : keyDescriptor) {
+ Element keyInfo = keyDescriptorType.getKeyInfo();
+ Element x509KeyInfo = DocumentUtil.getChildElement(keyInfo, new QName("dsig", "X509Certificate"));
+
+ if (KeyTypes.SIGNING.equals(keyDescriptorType.getUse())) {
+ samlIdentityProviderConfig.setSigningPublicKey(x509KeyInfo.getTextContent());
+ } else if (KeyTypes.ENCRYPTION.equals(keyDescriptorType.getUse())) {
+ samlIdentityProviderConfig.setEncryptionPublicKey(x509KeyInfo.getTextContent());
+ } else if (keyDescriptorType.getUse() == null) {
+ defaultPublicKey = x509KeyInfo.getTextContent();
+ }
+ }
+ }
+
+ if (defaultPublicKey != null) {
+ if (samlIdentityProviderConfig.getSigningPublicKey() == null) {
+ samlIdentityProviderConfig.setSigningPublicKey(defaultPublicKey);
+ }
+
+ if (samlIdentityProviderConfig.getEncryptionPublicKey() == null) {
+ samlIdentityProviderConfig.setEncryptionPublicKey(defaultPublicKey);
+ }
+ }
+
+ return samlIdentityProviderConfig.getConfig();
+ }
+ }
+ }
+ } catch (ParsingException pe) {
+ throw new RuntimeException("Could not parse IdP SAML Metadata", pe);
+ }
+
+ return new HashMap<String, String>();
+ }
+
+ @Override
+ public String getId() {
+ return "saml";
+ }
+}
diff --git a/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory b/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory
new file mode 100644
index 0000000..afd7cd0
--- /dev/null
+++ b/broker/saml/src/main/resources/META-INF/services/org.keycloak.broker.provider.IdentityProviderFactory
@@ -0,0 +1 @@
+org.keycloak.broker.saml.SAMLIdentityProviderFactory
\ No newline at end of file
diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml
index a506953..ddf4396 100755
--- a/connections/jpa/src/main/resources/META-INF/persistence.xml
+++ b/connections/jpa/src/main/resources/META-INF/persistence.xml
@@ -11,12 +11,13 @@
<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.SocialLinkEntity</class>
+ <class>org.keycloak.models.jpa.entities.FederatedIdentityEntity</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>
<!-- JpaUserSessionProvider -->
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class>
@@ -29,7 +30,7 @@
<class>org.keycloak.events.jpa.EventEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
-
+
<properties>
<property name="jboss.as.jpa.managed" value="false"/>
</properties>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
new file mode 100644
index 0000000..e8acd71
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.2.0.Beta1.xml
@@ -0,0 +1,42 @@
+<?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="psilva@redhat.com" id="1.2.0.Beta1">
+ <createTable tableName="FEDERATED_IDENTITY">
+ <column name="IDENTITY_PROVIDER" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="REALM_ID" type="VARCHAR(255)"/>
+ <column name="FEDERATED_USER_ID" type="VARCHAR(255)"/>
+ <column name="FEDERATED_USERNAME" type="VARCHAR(255)"/>
+ <column name="USER_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <createTable tableName="IDENTITY_PROVIDER">
+ <column name="INTERNAL_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="ENABLED" type="BOOLEAN(1)"/>
+ <column name="PROVIDER_NONIMAL_ID" type="VARCHAR(255)"/>
+ <column name="PROVIDER_NAME" type="VARCHAR(255)"/>
+ <column name="PROVIDER_ID" type="VARCHAR(255)"/>
+ <column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN(1)"/>
+ <column name="REALM_ID" type="VARCHAR(36)"/>
+ </createTable>
+ <createTable tableName="IDENTITY_PROVIDER_CONFIG">
+ <column name="IDENTITY_PROVIDER_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="VALUE" type="CLOB"/>
+ <column name="NAME" type="VARCHAR(255)">
+ <constraints nullable="false"/>
+ </column>
+ </createTable>
+ <addPrimaryKey columnNames="INTERNAL_ID" constraintName="CONSTRAINT_2B" tableName="IDENTITY_PROVIDER"/>
+ <addPrimaryKey columnNames="IDENTITY_PROVIDER, USER_ID" constraintName="CONSTRAINT_40" tableName="FEDERATED_IDENTITY"/>
+ <addPrimaryKey columnNames="IDENTITY_PROVIDER_ID, NAME" constraintName="CONSTRAINT_D" tableName="IDENTITY_PROVIDER_CONFIG"/>
+ <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="IDENTITY_PROVIDER" constraintName="FK2B4EBC52AE5C3B34" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+ <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="FEDERATED_IDENTITY" constraintName="FK404288B92EF007A6" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="IDENTITY_PROVIDER_CONFIG" constraintName="FKDC4897CF864C4E43" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
+ </changeSet>
+</databaseChangeLog>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
index 1a1e0a7..3d8b671 100644
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -3,4 +3,5 @@
<include file="META-INF/jpa-changelog-1.0.0.Final.xml"/>
<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"/>
</databaseChangeLog>
diff --git a/core/src/main/java/org/keycloak/jose/jws/JWSHeader.java b/core/src/main/java/org/keycloak/jose/jws/JWSHeader.java
index 30fcb5d..1d0f8ea 100755
--- a/core/src/main/java/org/keycloak/jose/jws/JWSHeader.java
+++ b/core/src/main/java/org/keycloak/jose/jws/JWSHeader.java
@@ -21,6 +21,9 @@ public class JWSHeader implements Serializable {
@JsonProperty("cty")
private String contentType;
+ @JsonProperty("kid")
+ private String keyId;
+
public JWSHeader() {
}
@@ -42,6 +45,9 @@ public class JWSHeader implements Serializable {
return contentType;
}
+ public String getKeyId() {
+ return keyId;
+ }
private static final ObjectMapper mapper = new ObjectMapper();
diff --git a/core/src/main/java/org/keycloak/representations/idm/FederatedIdentityRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/FederatedIdentityRepresentation.java
new file mode 100644
index 0000000..0a31648
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/FederatedIdentityRepresentation.java
@@ -0,0 +1,35 @@
+package org.keycloak.representations.idm;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class FederatedIdentityRepresentation {
+
+ protected String identityProvider;
+ protected String userId;
+ protected String userName;
+
+ public String getIdentityProvider() {
+ return identityProvider;
+ }
+
+ public void setIdentityProvider(String identityProvider) {
+ this.identityProvider = identityProvider;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
new file mode 100644
index 0000000..023bd01
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
@@ -0,0 +1,91 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.representations.idm;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+public class IdentityProviderRepresentation {
+
+ protected String id;
+ protected String providerId;
+ protected String name;
+ protected boolean enabled = true;
+ protected boolean updateProfileFirstLogin = true;
+ protected String groupName;
+ protected Map<String, String> config = new HashMap<String, String>();
+
+ public String getId() {
+ return this.id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getProviderId() {
+ return this.providerId;
+ }
+
+ public void setProviderId(String providerId) {
+ this.providerId = providerId;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map<String, String> getConfig() {
+ return this.config;
+ }
+
+ public void setConfig(Map<String, String> config) {
+ this.config = config;
+ }
+
+ public String getGroupName() {
+ return this.groupName;
+ }
+
+ public void setGroupName(String groupName) {
+ this.groupName = groupName;
+ }
+
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isUpdateProfileFirstLogin() {
+ return this.updateProfileFirstLogin;
+ }
+
+ public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) {
+ this.updateProfileFirstLogin = updateProfileFirstLogin;
+ }
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
index b0a0a21..e5a1173 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RealmRepresentation.java
@@ -25,8 +25,6 @@ public class RealmRepresentation {
protected Boolean rememberMe;
protected Boolean verifyEmail;
protected Boolean resetPasswordAllowed;
- protected Boolean social;
- protected Boolean updateProfileOnInitialSocialLogin;
protected Boolean userCacheEnabled;
protected Boolean realmCacheEnabled;
@@ -55,7 +53,6 @@ public class RealmRepresentation {
protected List<ApplicationRepresentation> applications;
protected List<OAuthClientRepresentation> oauthClients;
protected Map<String, String> browserSecurityHeaders;
- protected Map<String, String> socialProviders;
protected Map<String, String> smtpServer;
protected List<UserFederationProviderRepresentation> userFederationProviders;
protected String loginTheme;
@@ -65,6 +62,8 @@ public class RealmRepresentation {
protected Boolean eventsEnabled;
protected Long eventsExpiration;
protected List<String> eventsListeners;
+ private List<IdentityProviderRepresentation> identityProviders;
+ private boolean identityFederationEnabled;
public String getId() {
return id;
@@ -294,22 +293,6 @@ public class RealmRepresentation {
this.resetPasswordAllowed = resetPassword;
}
- public Boolean isSocial() {
- return social;
- }
-
- public void setSocial(Boolean social) {
- this.social = social;
- }
-
- public Boolean isUpdateProfileOnInitialSocialLogin() {
- return updateProfileOnInitialSocialLogin;
- }
-
- public void setUpdateProfileOnInitialSocialLogin(Boolean updateProfileOnInitialSocialLogin) {
- this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
- }
-
public Map<String, String> getBrowserSecurityHeaders() {
return browserSecurityHeaders;
}
@@ -318,14 +301,6 @@ public class RealmRepresentation {
this.browserSecurityHeaders = browserSecurityHeaders;
}
- public Map<String, String> getSocialProviders() {
- return socialProviders;
- }
-
- public void setSocialProviders(Map<String, String> socialProviders) {
- this.socialProviders = socialProviders;
- }
-
public Map<String, String> getSmtpServer() {
return smtpServer;
}
@@ -485,4 +460,24 @@ public class RealmRepresentation {
public void setUserFederationProviders(List<UserFederationProviderRepresentation> userFederationProviders) {
this.userFederationProviders = userFederationProviders;
}
+
+ public List<IdentityProviderRepresentation> getIdentityProviders() {
+ if (this.identityProviders == null) {
+ this.identityProviders = new ArrayList<IdentityProviderRepresentation>();
+ }
+
+ return identityProviders;
+ }
+
+ public void setIdentityProviders(List<IdentityProviderRepresentation> identityProviders) {
+ this.identityProviders = identityProviders;
+ }
+
+ public void addIdentityProvider(IdentityProviderRepresentation identityProviderRepresentation) {
+ getIdentityProviders().add(identityProviderRepresentation);
+ }
+
+ public boolean isIdentityFederationEnabled() {
+ return !getIdentityProviders().isEmpty();
+ }
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
index 45cac16..8fa1d6a 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -24,7 +24,7 @@ public class UserRepresentation {
protected Map<String, String> attributes;
protected List<CredentialRepresentation> credentials;
protected List<String> requiredActions;
- protected List<SocialLinkRepresentation> socialLinks;
+ protected List<FederatedIdentityRepresentation> federatedIdentities;
protected List<String> realmRoles;
protected Map<String, List<String>> applicationRoles;
@@ -139,12 +139,12 @@ public class UserRepresentation {
this.requiredActions = requiredActions;
}
- public List<SocialLinkRepresentation> getSocialLinks() {
- return socialLinks;
+ public List<FederatedIdentityRepresentation> getFederatedIdentities() {
+ return federatedIdentities;
}
- public void setSocialLinks(List<SocialLinkRepresentation> socialLinks) {
- this.socialLinks = socialLinks;
+ public void setFederatedIdentities(List<FederatedIdentityRepresentation> federatedIdentities) {
+ this.federatedIdentities = federatedIdentities;
}
public List<String> getRealmRoles() {
dependencies/server-all/pom.xml 12(+11 -1)
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index 0a65c8f..eb1fe46 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -82,7 +82,17 @@
<version>${project.version}</version>
</dependency>
- <!-- social -->
+ <!-- identity providers -->
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-broker-oidc</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-broker-saml</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-social-github</artifactId>
diff --git a/events/api/src/main/java/org/keycloak/events/Errors.java b/events/api/src/main/java/org/keycloak/events/Errors.java
index 3e8c07c..2e75880 100755
--- a/events/api/src/main/java/org/keycloak/events/Errors.java
+++ b/events/api/src/main/java/org/keycloak/events/Errors.java
@@ -34,7 +34,7 @@ public interface Errors {
String NOT_ALLOWED = "not_allowed";
- String SOCIAL_PROVIDER_NOT_FOUND = "social_provider_not_found";
+ String IDENTITY_PROVIDER_NOT_FOUND = "identity_provider_not_found";
String SOCIAL_ID_IN_USE = "social_id_in_use";
String SSL_REQUIRED = "ssl_required";
diff --git a/events/api/src/main/java/org/keycloak/events/EventType.java b/events/api/src/main/java/org/keycloak/events/EventType.java
index cd624b4..681c87d 100755
--- a/events/api/src/main/java/org/keycloak/events/EventType.java
+++ b/events/api/src/main/java/org/keycloak/events/EventType.java
@@ -19,7 +19,7 @@ public enum EventType {
REFRESH_TOKEN_ERROR,
SOCIAL_LINK,
SOCIAL_LINK_ERROR,
- REMOVE_SOCIAL_LINK,
+ REMOVE_FEDERATED_IDENTITY,
REMOVE_SOCIAL_LINK_ERROR,
UPDATE_EMAIL,
diff --git a/examples/basic-auth/basicauthrealm.json b/examples/basic-auth/basicauthrealm.json
index d738fd2..f6bac20 100644
--- a/examples/basic-auth/basicauthrealm.json
+++ b/examples/basic-auth/basicauthrealm.json
@@ -9,8 +9,6 @@
"passwordCredentialGrantAllowed": true,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
examples/cors/cors-realm.json 2(+0 -2)
diff --git a/examples/cors/cors-realm.json b/examples/cors/cors-realm.json
index cafb7ff..ab08ee3 100755
--- a/examples/cors/cors-realm.json
+++ b/examples/cors/cors-realm.json
@@ -8,8 +8,6 @@
"ssoSessionMaxLifespan": 36000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/examples/demo-template/testrealm.json b/examples/demo-template/testrealm.json
index cc8e02f..7cf597a 100755
--- a/examples/demo-template/testrealm.json
+++ b/examples/demo-template/testrealm.json
@@ -9,8 +9,6 @@
"passwordCredentialGrantAllowed": true,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/examples/multi-tenant/tenant1-realm.json b/examples/multi-tenant/tenant1-realm.json
index dc41441..e7a9cc9 100644
--- a/examples/multi-tenant/tenant1-realm.json
+++ b/examples/multi-tenant/tenant1-realm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/examples/multi-tenant/tenant2-realm.json b/examples/multi-tenant/tenant2-realm.json
index e19509d..d628b73 100644
--- a/examples/multi-tenant/tenant2-realm.json
+++ b/examples/multi-tenant/tenant2-realm.json
@@ -7,7 +7,6 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
"updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXQIBAAKBgQDA0oJjgPQJhnVhOo51KauQGfLLreMFu64OJdKXRnfvAQJQTuKNwc5JrR63l/byyW1B6FgclABF818TtLvMCAkn4EuFwQZCZhg3x3+lFGiB/IzC6UAt4Bi0JQrTbdh83/U97GIPegvaDqiqEiQESEkbCZWxM6sh/34hQaAhCaFpMwIDAQABAoGADwFSvEOQuh0IjWRtKZjwjOo4BrmlbRDJ3rf6x2LoemTttSouXzGxx/H87fSZdxNNuU9HbBHoY4ko4POzmZEWhS0gV6UjM7VArc4YjID6Hh2tfU9vCbuuKZrRs7RjxL70b51WxycKc49PQ4JiR3g04punrpq2UzToPrm66zI+ICECQQD2Jauo6cXXoxHR0QychQf4dityZwFXUoR/8oI/YFiu9XwcWgSMwrFKUdWWNKYmrIRNqCBzrGyeiGdaAjsw41T3AkEAyIpn+XL7bek/uLno5/7ULauf2dFI6MEaHJixQJD7S6Tfo/CGuDK93H4K0GAdjgR0LA0tCnB09yyPCd5NmAYKpQJBAO7+BH4s/PsyScr+vs/6GpMTqXuap6KxbBUO0YfXdEPr9mVQwboqDxmp+0esNua1+n+sDlZBw/TpW+/42p/NGmECQF0sOQyjyH+TfGCmN7j6I7ioYZeA7h/9/9TDeK8n7SmDC8kOanlQUfgMs5eG4JRoK1WANaoA/8cLc9XA7EoynGUCQQDx/Gjg6qyWheVujxjKufH1XkqDNiQHClDRM1ntChCmGq/RmpVmce+mYeOYZ9eofv7UJUCBdamllRlB+056Ld2h",
diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
index c9ba3dd..7c63a62 100755
--- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ExportUtils.java
@@ -13,7 +13,7 @@ import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.ModelToRepresentation;
@@ -25,7 +25,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.RolesRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation;
-import org.keycloak.representations.idm.SocialLinkRepresentation;
+import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import java.io.IOException;
@@ -253,14 +253,14 @@ public class ExportUtils {
UserRepresentation userRep = ModelToRepresentation.toRepresentation(user);
// Social links
- Set<SocialLinkModel> socialLinks = session.users().getSocialLinks(user, realm);
- List<SocialLinkRepresentation> socialLinkReps = new ArrayList<SocialLinkRepresentation>();
- for (SocialLinkModel socialLink : socialLinks) {
- SocialLinkRepresentation socialLinkRep = exportSocialLink(socialLink);
+ Set<FederatedIdentityModel> socialLinks = session.users().getFederatedIdentities(user, realm);
+ List<FederatedIdentityRepresentation> socialLinkReps = new ArrayList<FederatedIdentityRepresentation>();
+ for (FederatedIdentityModel socialLink : socialLinks) {
+ FederatedIdentityRepresentation socialLinkRep = exportSocialLink(socialLink);
socialLinkReps.add(socialLinkRep);
}
if (socialLinkReps.size() > 0) {
- userRep.setSocialLinks(socialLinkReps);
+ userRep.setFederatedIdentities(socialLinkReps);
}
// Role mappings
@@ -303,11 +303,11 @@ public class ExportUtils {
return userRep;
}
- public static SocialLinkRepresentation exportSocialLink(SocialLinkModel socialLink) {
- SocialLinkRepresentation socialLinkRep = new SocialLinkRepresentation();
- socialLinkRep.setSocialProvider(socialLink.getSocialProvider());
- socialLinkRep.setSocialUserId(socialLink.getSocialUserId());
- socialLinkRep.setSocialUsername(socialLink.getSocialUsername());
+ public static FederatedIdentityRepresentation exportSocialLink(FederatedIdentityModel socialLink) {
+ FederatedIdentityRepresentation socialLinkRep = new FederatedIdentityRepresentation();
+ socialLinkRep.setIdentityProvider(socialLink.getIdentityProvider());
+ socialLinkRep.setUserId(socialLink.getUserId());
+ socialLinkRep.setUserName(socialLink.getUserName());
return socialLinkRep;
}
diff --git a/forms/account-api/src/main/java/org/keycloak/account/AccountPages.java b/forms/account-api/src/main/java/org/keycloak/account/AccountPages.java
index dc7e3c0..d316574 100644
--- a/forms/account-api/src/main/java/org/keycloak/account/AccountPages.java
+++ b/forms/account-api/src/main/java/org/keycloak/account/AccountPages.java
@@ -5,6 +5,6 @@ package org.keycloak.account;
*/
public enum AccountPages {
- ACCOUNT, PASSWORD, TOTP, SOCIAL, LOG, SESSIONS;
+ ACCOUNT, PASSWORD, TOTP, FEDERATED_IDENTITY, LOG, SESSIONS;
}
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
index 6d77747..368b459 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
@@ -4,7 +4,7 @@ import org.jboss.logging.Logger;
import org.keycloak.account.AccountPages;
import org.keycloak.account.AccountProvider;
import org.keycloak.account.freemarker.model.AccountBean;
-import org.keycloak.account.freemarker.model.AccountSocialBean;
+import org.keycloak.account.freemarker.model.AccountFederatedIdentityBean;
import org.keycloak.account.freemarker.model.FeaturesBean;
import org.keycloak.account.freemarker.model.LogBean;
import org.keycloak.account.freemarker.model.MessageBean;
@@ -51,7 +51,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
private List<Event> events;
private String stateChecker;
private List<UserSessionModel> sessions;
- private boolean socialEnabled;
+ private boolean identityProviderEnabled;
private boolean eventsEnabled;
private boolean passwordUpdateSupported;
private boolean passwordSet;
@@ -124,7 +124,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
attributes.put("url", new UrlBean(realm, theme, baseUri, baseQueryUri, uriInfo.getRequestUri(), stateChecker));
- attributes.put("features", new FeaturesBean(socialEnabled, eventsEnabled, passwordUpdateSupported));
+ attributes.put("features", new FeaturesBean(identityProviderEnabled, eventsEnabled, passwordUpdateSupported));
switch (page) {
case ACCOUNT:
@@ -133,8 +133,8 @@ public class FreeMarkerAccountProvider implements AccountProvider {
case TOTP:
attributes.put("totp", new TotpBean(realm, user, baseUri));
break;
- case SOCIAL:
- attributes.put("social", new AccountSocialBean(session, realm, user, uriInfo.getBaseUri(), stateChecker));
+ case FEDERATED_IDENTITY:
+ attributes.put("federatedIdentity", new AccountFederatedIdentityBean(session, realm, user, uriInfo.getBaseUri(), stateChecker));
break;
case LOG:
attributes.put("log", new LogBean(events));
@@ -232,8 +232,8 @@ public class FreeMarkerAccountProvider implements AccountProvider {
}
@Override
- public AccountProvider setFeatures(boolean socialEnabled, boolean eventsEnabled, boolean passwordUpdateSupported) {
- this.socialEnabled = socialEnabled;
+ public AccountProvider setFeatures(boolean identityProviderEnabled, boolean eventsEnabled, boolean passwordUpdateSupported) {
+ this.identityProviderEnabled = identityProviderEnabled;
this.eventsEnabled = eventsEnabled;
this.passwordUpdateSupported = passwordUpdateSupported;
return this;
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java
index 06e99eb..b3b96d6 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/FeaturesBean.java
@@ -5,18 +5,18 @@ package org.keycloak.account.freemarker.model;
*/
public class FeaturesBean {
- private final boolean social;
+ private final boolean identityFederation;
private final boolean log;
private final boolean passwordUpdateSupported;
- public FeaturesBean(boolean social, boolean log, boolean passwordUpdateSupported) {
- this.social = social;
+ public FeaturesBean(boolean identityFederation, boolean log, boolean passwordUpdateSupported) {
+ this.identityFederation = identityFederation;
this.log = log;
this.passwordUpdateSupported = passwordUpdateSupported;
}
- public boolean isSocial() {
- return social;
+ public boolean isIdentityFederation() {
+ return identityFederation;
}
public boolean isLog() {
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/Templates.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/Templates.java
index 80fc179..1d33f8a 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/Templates.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/Templates.java
@@ -15,8 +15,8 @@ public class Templates {
return "password.ftl";
case TOTP:
return "totp.ftl";
- case SOCIAL:
- return "social.ftl";
+ case FEDERATED_IDENTITY:
+ return "federatedIdentity.ftl";
case LOG:
return "log.ftl";
case SESSIONS:
diff --git a/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties b/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties
index 6feedf5..e49a938 100755
--- a/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties
+++ b/forms/common-themes/src/main/resources/theme/account/base/messages/messages.properties
@@ -29,13 +29,13 @@ successTotpRemoved=Mobile authenticator removed.
accountUpdated=Your account has been updated
accountPasswordUpdated=Your password has been updated
-missingSocialProvider=Social provider not specified
-invalidSocialAction=Invalid or missing action
-socialProviderNotFound=Specified social provider not found
-socialLinkNotActive=This social link is not active anymore
-socialRemovingLastProvider=You can't remove last social provider as you don't have password
-socialRedirectError=Failed to redirect to social provider
-socialProviderRemoved=Social provider removed successfully
+missingIdentityProvider=Identity provider not specified
+invalidFederatedIdentityAction=Invalid or missing action
+identityProviderNotFound=Specified identity provider not found
+federatedIdentityLinkNotActive=This identity is not active anymore
+federatedIdentityRemovingLastProvider=You can't remove last federated identity as you don't have password
+identityProviderRedirectError=Failed to redirect to identity provider
+identityProviderRemoved=Identity provider removed successfully
accountDisabled=Account is disabled, contact admin\
accountTemporarilyDisabled=Account is temporarily disabled, contact admin or try again later
diff --git a/forms/common-themes/src/main/resources/theme/account/base/template.ftl b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
index 49040df..11dc877 100644
--- a/forms/common-themes/src/main/resources/theme/account/base/template.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
@@ -42,7 +42,7 @@
<li class="<#if active=='account'>active</#if>"><a href="${url.accountUrl}">Account</a></li>
<#if features.passwordUpdateSupported><li class="<#if active=='password'>active</#if>"><a href="${url.passwordUrl}">Password</a></li></#if>
<li class="<#if active=='totp'>active</#if>"><a href="${url.totpUrl}">Authenticator</a></li>
- <#if features.social><li class="<#if active=='social'>active</#if>"><a href="${url.socialUrl}">Social</a></li></#if>
+ <#if features.identityFederation><li class="<#if active=='social'>active</#if>"><a href="${url.socialUrl}">Federated Identity</a></li></#if>
<li class="<#if active=='sessions'>active</#if>"><a href="${url.sessionsUrl}">Sessions</a></li>
<#if features.log><li class="<#if active=='log'>active</#if>"><a href="${url.logUrl}">Log</a></li></#if>
</ul>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
index 3198abf..d3cfbaf 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/app.js
@@ -147,17 +147,41 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'RealmKeysDetailCtrl'
})
- .when('/realms/:realm/social-settings', {
- templateUrl : 'partials/realm-social.html',
+ .when('/realms/:realm/identity-provider-settings', {
+ templateUrl : 'partials/realm-identity-provider.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
+ },
+ instance : function(IdentityProviderLoader) {
+ return {};
+ },
+ providerFactory : function(IdentityProviderFactoryLoader) {
+ return IdentityProviderFactoryLoader();
+ }
+ },
+ controller : 'RealmIdentityProviderCtrl'
+ })
+ .when('/realms/:realm/identity-provider-settings/provider/:provider_id/:id', {
+ templateUrl : function(params){ return 'partials/realm-identity-provider-' + params.provider_id + '.html'; },
+ resolve : {
+ realm : function(RealmLoader) {
+ return RealmLoader();
+ },
+ serverInfo : function(ServerInfoLoader) {
+ return ServerInfoLoader();
+ },
+ instance : function(IdentityProviderLoader) {
+ return IdentityProviderLoader();
+ },
+ providerFactory : function(IdentityProviderFactoryLoader) {
+ return IdentityProviderFactoryLoader();
}
},
- controller : 'RealmSocialCtrl'
+ controller : 'RealmIdentityProviderCtrl'
})
.when('/realms/:realm/default-roles', {
templateUrl : 'partials/realm-default-roles.html',
@@ -282,8 +306,8 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'UserSessionsCtrl'
})
- .when('/realms/:realm/users/:user/social-links', {
- templateUrl : 'partials/user-social-links.html',
+ .when('/realms/:realm/users/:user/federated-identity', {
+ templateUrl : 'partials/user-federated-identity.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
@@ -291,11 +315,11 @@ module.config([ '$routeProvider', function($routeProvider) {
user : function(UserLoader) {
return UserLoader();
},
- socialLinks : function(UserSocialLinksLoader) {
- return UserSocialLinksLoader();
+ federatedIdentities : function(UserFederatedIdentityLoader) {
+ return UserFederatedIdentityLoader();
}
},
- controller : 'UserSocialCtrl'
+ controller : 'UserFederatedIdentityCtrl'
})
.when('/realms/:realm/users', {
templateUrl : 'partials/user-list.html',
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
index 5a1c85d..8e9573d 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
@@ -258,7 +258,6 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, ser
$scope.realm = angular.copy(realm);
}
- $scope.social = $scope.realm.social;
$scope.registrationAllowed = $scope.realm.registrationAllowed;
var oldCopy = angular.copy($scope.realm);
@@ -287,7 +286,6 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, ser
}
$location.url("/realms/" + realmCopy.realm);
Notifications.success("The realm has been created.");
- $scope.social = $scope.realm.social;
$scope.registrationAllowed = $scope.realm.registrationAllowed;
});
});
@@ -307,7 +305,6 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, ser
});
$location.url("/realms/" + realmCopy.realm);
Notifications.success("Your changes have been saved to the realm.");
- $scope.social = $scope.realm.social;
$scope.registrationAllowed = $scope.realm.registrationAllowed;
});
}
@@ -337,7 +334,6 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, ser
function genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications, url) {
$scope.realm = angular.copy(realm);
$scope.serverInfo = serverInfo;
- $scope.social = $scope.realm.social;
$scope.registrationAllowed = $scope.realm.registrationAllowed;
var oldCopy = angular.copy($scope.realm);
@@ -367,7 +363,6 @@ function genericRealmUpdate($scope, Current, Realm, realm, serverInfo, $http, $l
});
$location.url(url);
Notifications.success("Your changes have been saved to the realm.");
- $scope.social = $scope.realm.social;
$scope.registrationAllowed = $scope.realm.registrationAllowed;
});
};
@@ -617,65 +612,156 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, appli
});
-module.controller('RealmSocialCtrl', function($scope, realm, Realm, serverInfo, $location, Notifications) {
- console.log('RealmSocialCtrl');
+module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload, realm, instance, providerFactory, IdentityProvider, serverInfo, $location, Notifications) {
+ console.log('RealmIdentityProviderCtrl');
$scope.realm = angular.copy(realm);
- $scope.serverInfo = serverInfo;
- $scope.allProviders = serverInfo.socialProviders;
- $scope.configuredProviders = [];
+ $scope.hidePassword = true;
- $scope.$watch('realm.socialProviders', function(socialProviders) {
- $scope.configuredProviders = [];
- for (var providerConfig in socialProviders) {
- var i = providerConfig.split('.');
- if (i.length == 2 && i[1] == 'key') {
- $scope.configuredProviders.push(i[0]);
- }
- }
- }, true);
+ $scope.getBoolean = function(value) {
+ if (value == 'true') {
+ return true;
+ } else if (value == 'false') {
+ return false;
+ } else {
+ return value;
+ }
+ }
- var oldCopy = angular.copy($scope.realm);
- $scope.changed = false;
- $scope.callbackUrl = $location.absUrl().replace(/\/admin.*/, "/social/callback");
+ if (instance && instance.id) {
+ $scope.identityProvider = angular.copy(instance);
- $scope.addProvider = function(pId) {
- if (!$scope.realm.socialProviders) {
- $scope.realm.socialProviders = {};
- }
+ // fixme: this is a hack to make onofswith work and recognize string representation of boolean values
+ $scope.identityProvider.config.validateSignature = $scope.getBoolean($scope.identityProvider.config.validateSignature);
+ $scope.identityProvider.config.forceAuthn = $scope.getBoolean($scope.identityProvider.config.forceAuthn);
+ $scope.newIdentityProvider = false;
+ } else {
+ $scope.identityProvider = {};
+ $scope.identityProvider.id = providerFactory.id;
+ $scope.identityProvider.providerId = providerFactory.id;
+ $scope.identityProvider.name = providerFactory.name;
+ $scope.identityProvider.enabled = true;
+ $scope.identityProvider.updateProfileFirstLogin = true;
+ $scope.newIdentityProvider = true;
+ }
+
+ $scope.serverInfo = serverInfo;
+
+ $scope.allProviders = angular.copy(serverInfo.identityProviders);
- $scope.realm.socialProviders[pId + ".key"] = "";
- $scope.realm.socialProviders[pId + ".secret"] = "";
+ $scope.configuredProviders = angular.copy(realm.identityProviders);
+
+ $scope.files = [];
+ $scope.importFile = false;
+
+ $scope.onFileSelect = function($files) {
+ $scope.importFile = true;
+ $scope.files = $files;
};
- $scope.removeProvider = function(pId) {
- delete $scope.realm.socialProviders[pId+".key"];
- delete $scope.realm.socialProviders[pId+".secret"];
+ $scope.clearFileSelect = function() {
+ $scope.importFile = false;
+ $scope.files = null;
+ }
+
+ $scope.uploadFile = function() {
+ //$files: an array of files selected, each file has name, size, and type.
+ for (var i = 0; i < $scope.files.length; i++) {
+ var $file = $scope.files[i];
+ $scope.upload = $upload.upload({
+ url: authUrl + '/admin/realms/' + realm.realm + '/identity-provider/',
+ // method: POST or PUT,
+ // headers: {'headerKey': 'headerValue'}, withCredential: true,
+ data: $scope.identityProvider,
+ file: $file
+ /* set file formData name for 'Content-Desposition' header. Default: 'file' */
+ //fileFormDataName: myFile,
+ /* customize how data is added to formData. See #40#issuecomment-28612000 for example */
+ //formDataAppender: function(formData, key, val){}
+ }).progress(function(evt) {
+ console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
+ }).success(function(data, status, headers) {
+ $location.url("/realms/" + realm.realm + "/identity-provider-settings");
+ Notifications.success("The " + $scope.identityProvider.name + " provider has been created.");
+ }).error(function() {
+ Notifications.error("The file can not be uploaded. Please verify the file.");
+ });
+ }
};
- $scope.$watch('realm', function() {
- if (!angular.equals($scope.realm, oldCopy)) {
- $scope.changed = true;
+ $scope.$watch('configuredProviders', function(configuredProviders) {
+ if (configuredProviders) {
+ $scope.configuredProviders = angular.copy(configuredProviders);
+
+ for (var j = 0; j < configuredProviders.length; j++) {
+ var configProvidedId = configuredProviders[j].providerId;
+
+ for (var i in $scope.allProviders) {
+ var provider = $scope.allProviders[i];
+
+ if (provider.groupName == 'Social' && (provider.id == configProvidedId)) {
+ $scope.allProviders.splice(i, 1);
+ break;
+ }
+ }
+ }
}
}, true);
- $scope.save = function() {
- var realmCopy = angular.copy($scope.realm);
- realmCopy.social = true;
- $scope.changed = false;
- Realm.update(realmCopy, function () {
- $location.url("/realms/" + realm.realm + "/social-settings");
- Notifications.success("The changes have been saved to the realm.");
- oldCopy = realmCopy;
+ $scope.callbackUrl = $location.absUrl().replace(/\/admin.*/, "/broker/") + realm.realm + "/" ;
+
+ $scope.addProvider = function(provider) {
+ $location.url("/realms/" + realm.realm + "/identity-provider-settings/provider/" + provider.id + "/" + provider.id);
+ };
+
+ $scope.remove = function() {
+ IdentityProvider.delete({
+ realm: $scope.realm.realm,
+ id: $scope.identityProvider.id
+ }, $scope.identityProvider, function () {
+ $scope.changed = false;
+ $location.url("/realms/" + realm.realm + "/identity-provider-settings");
+ Notifications.success("The " + $scope.identityProvider.name + " provider has been deleted.");
});
};
+ $scope.save = function() {
+ if ($scope.newIdentityProvider) {
+ IdentityProvider.create({
+ realm: $scope.realm.realm
+ }, $scope.identityProvider, function () {
+ $location.url("/realms/" + realm.realm + "/identity-provider-settings");
+ Notifications.success("The " + $scope.identityProvider.name + " provider has been created.");
+ });
+ } else {
+ IdentityProvider.update({
+ realm: $scope.realm.realm
+ }, $scope.identityProvider, function () {
+ $location.url("/realms/" + realm.realm + "/identity-provider-settings");
+ Notifications.success("The " + $scope.identityProvider.name + " provider has been update.");
+ });
+ }
+ };
+
$scope.reset = function() {
- $scope.realm = angular.copy(oldCopy);
- $scope.changed = false;
+ $scope.identityProvider = {};
+ $scope.configuredProviders = angular.copy($scope.realm.identityProviders);
+ };
+
+ $scope.showPassword = function(flag) {
+ $scope.hidePassword = flag;
};
+ $scope.getBoolean = function(value) {
+ if (value == 'true') {
+ return true;
+ } else if (value == 'false') {
+ return false;
+ } else {
+ return value;
+ }
+ }
});
module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, $location, $route, Dialog, Notifications, TimeUnit) {
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
index 8ba2d5a..a7f8a5d 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
@@ -129,11 +129,11 @@ module.controller('UserSessionsCtrl', function($scope, realm, user, sessions, Us
}
});
-module.controller('UserSocialCtrl', function($scope, realm, user, socialLinks) {
+module.controller('UserFederatedIdentityCtrl', function($scope, realm, user, federatedIdentities) {
$scope.realm = realm;
$scope.user = user;
- $scope.socialLinks = socialLinks;
- console.log('showing social links of user');
+ $scope.federatedIdentities = federatedIdentities;
+ console.log('showing federated identities of user');
});
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/loaders.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/loaders.js
index 4551615..cb467f4 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/loaders.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/loaders.js
@@ -125,8 +125,8 @@ module.factory('UserSessionsLoader', function(Loader, UserSessions, $route, $q)
});
});
-module.factory('UserSocialLinksLoader', function(Loader, UserSocialLinks, $route, $q) {
- return Loader.query(UserSocialLinks, function() {
+module.factory('UserFederatedIdentityLoader', function(Loader, UserFederatedIdentity, $route, $q) {
+ return Loader.query(UserFederatedIdentity, function() {
return {
realm : $route.current.params.realm,
user : $route.current.params.user
@@ -277,3 +277,21 @@ module.factory('OAuthClientInstallationLoader', function(Loader, OAuthClientInst
}
});
});
+
+module.factory('IdentityProviderLoader', function(Loader, IdentityProvider, $route, $q) {
+ return Loader.get(IdentityProvider, function () {
+ return {
+ realm: $route.current.params.realm,
+ id: $route.current.params.id
+ }
+ });
+});
+
+module.factory('IdentityProviderFactoryLoader', function(Loader, IdentityProviderFactory, $route, $q) {
+ return Loader.get(IdentityProviderFactory, function () {
+ return {
+ realm: $route.current.params.realm,
+ provider_id: $route.current.params.provider_id
+ }
+ });
+});
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
index d47e3ab..47af934 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/services.js
@@ -248,8 +248,8 @@ module.factory('UserLogout', function($resource) {
user : '@user'
});
});
-module.factory('UserSocialLinks', function($resource) {
- return $resource(authUrl + '/admin/realms/:realm/users/:user/social-links', {
+module.factory('UserFederatedIdentity', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/users/:user/federated-identity', {
realm : '@realm',
user : '@user'
});
@@ -1052,4 +1052,27 @@ module.factory('PasswordPolicy', function() {
};
return p;
+});
+
+module.factory('IdentityProvider', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/identity-provider/:id', {
+ realm : '@realm'
+ }, {
+ create : {
+ method : 'POST'
+ },
+ delete : {
+ method : 'DELETE'
+ },
+ update: {
+ method : 'PUT'
+ }
+ });
+});
+
+module.factory('IdentityProviderFactory', function($resource) {
+ return $resource(authUrl + '/admin/realms/:realm/identity-provider/providers/:provider_id', {
+ realm : '@realm',
+ provider_id : '@provider_id'
+ });
});
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-facebook.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-facebook.html
new file mode 100755
index 0000000..d565cfb
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-facebook.html
@@ -0,0 +1 @@
+<div data-ng-include data-src="'partials/realm-identity-provider-social.html'"></div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-github.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-github.html
new file mode 100755
index 0000000..d565cfb
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-github.html
@@ -0,0 +1 @@
+<div data-ng-include data-src="'partials/realm-identity-provider-social.html'"></div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-google.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-google.html
new file mode 100755
index 0000000..d565cfb
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-google.html
@@ -0,0 +1 @@
+<div data-ng-include data-src="'partials/realm-identity-provider-social.html'"></div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html
new file mode 100755
index 0000000..544ea0a
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-oidc.html
@@ -0,0 +1,117 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+ <div id="content-area" class="col-sm-9" role="main">
+ <data-kc-navigation data-kc-current="social" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
+ <h2></h2>
+ <div id="content">
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
+ <li class="active">{{identityProvider.name}} Provider Settings</li>
+ </ol>
+ <h2 class="pull-left">{{identityProvider.name}} Provider Settings</h2>
+ <p class="subtitle"><span class="required">*</span> Required fields</p>
+ <form class="form-horizontal" name="realmForm" novalidate>
+ <fieldset>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="identifier" type="text" ng-model="identityProvider.id" data-ng-readonly="!newIdentityProvider" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The alias unique identifies an identity provider and it is also used to build the redirect uri." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="name">Name <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="name" type="text" ng-model="identityProvider.name" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The friendly name for this identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="authorizationUrl">Authorization Url <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="authorizationUrl" type="text" ng-model="identityProvider.config.authorizationUrl" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The Authorization Url." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="tokenUrl">Token Url <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="tokenUrl" type="text" ng-model="identityProvider.config.tokenUrl" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The Token Url." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="userInfoUrl">User Info Url <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="userInfoUrl" type="text" ng-model="identityProvider.config.userInfoUrl" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The User Info Url." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="clientId">Client ID <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="clientId" type="text" ng-model="identityProvider.config.clientId" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The client or application identifier registered withing the identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="clientSecret">Client Secret <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="clientSecret" type="password" ng-model="identityProvider.config.clientSecret" ng-show="hidePassword" required>
+ <input class="form-control" id="clientSecret" type="text" ng-model="identityProvider.config.clientSecret" ng-show="!hidePassword" required>
+ <a href="" ng-click="showPassword(false)" class="link" ng-show="hidePassword">Show Secret</a>
+ <a href="" ng-click="showPassword(true);" ng-show="!hidePassword">Hide Secret</a>
+ </div>
+ <span tooltip-placement="right" tooltip="The client or application secret registered withing the identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="issuer">Issuer </label>
+ <div class="col-sm-4">
+ <input class="form-control" id="issuer" type="text" ng-model="identityProvider.config.issuer">
+ </div>
+ <span tooltip-placement="right" tooltip="The issuer identifier for the issuer of the response. If not provided, no validation will be performed." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="defaultScope">Default Scopes </label>
+ <div class="col-sm-4">
+ <input class="form-control" id="defaultScope" type="text" ng-model="identityProvider.config.defaultScope">
+ </div>
+ <span tooltip-placement="right" tooltip="The scopes to be sent when asking for authorization. It can be a comma-separated list of scopes. Defaults to 'openid'." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="prompt">Prompt</label>
+ <div class="col-sm-4">
+ <div class="select-kc">
+ <select id="prompt" ng-model="identityProvider.config.prompt">
+ <option value="">none</option>
+ <option>consent</option>
+ <option>login</option>
+ <option>select_account</option>
+ </select>
+ </div>
+ </div>
+ <span tooltip-placement="right" tooltip="Is HTTPS required? 'None' means HTTPS is not required for any client IP address. 'External requests' means localhost and private IP addresses can access without HTTPS. 'All requests' means HTTPS is required for all IP addresses." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="enabled">Enabled</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.enabled" id="enabled" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Enable/disable this identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="updateProfileFirstLogin">Update Profile on First Login</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.updateProfileFirstLogin" name="identityProvider.updateProfileFirstLogin" id="updateProfileFirstLogin" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
+ </div>
+ </fieldset>
+
+ <div class="pull-right form-actions">
+ <button kc-save>Save</button>
+ <button kc-delete data-ng-click="remove()" data-ng-show="!newIdentityProvider">Delete</button>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
new file mode 100755
index 0000000..6d4ea91
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-saml.html
@@ -0,0 +1,100 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+ <div id="content-area" class="col-sm-9" role="main">
+ <data-kc-navigation data-kc-current="social" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
+ <h2></h2>
+ <div id="content">
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
+ <li class="active">{{identityProvider.name}} Provider Settings</li>
+ </ol>
+ <h2 class="pull-left">{{identityProvider.name}} Provider Settings</h2>
+ <p class="subtitle"><span class="required">*</span> Required fields</p>
+ <form class="form-horizontal" name="realmForm" novalidate>
+ <fieldset>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="identifier" type="text" ng-model="identityProvider.id" data-ng-readonly="!newIdentityProvider" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The alias unique identifies an identity provider and it is also used to build the redirect uri." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="name">Name <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="name" type="text" ng-model="identityProvider.name" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The friendly name for this identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group" data-ng-show="newIdentityProvider">
+ <label class="col-sm-2 control-label">Import IdP SAML Metadata </label>
+ <div class="col-sm-4">
+ <div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
+ <a href="#" class="btn btn-default"><span class="kc-icon-upload">Icon: Upload</span>Choose a File...</a>
+ <input id="import-file" type="file" class="transparent" ng-file-select="onFileSelect($files)">
+ </div>
+ <span class="kc-uploaded-file" data-ng-show="files.length > 0">
+ {{files[0].name}}
+ </span>
+ </div>
+ </div>
+ <div class="form-group clearfix" data-ng-show="!importFile">
+ <label class="col-sm-2 control-label" for="singleSignOnServiceUrl">Single Sign-On Service Url<span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="singleSignOnServiceUrl" type="text" ng-model="identityProvider.config.singleSignOnServiceUrl" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The Url that must be used to send authentication requests(SAML AuthnRequest)." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="nameIDPolicyFormat">NameID Policy Format</label>
+ <div class="col-sm-4">
+ <input class="form-control" id="nameIDPolicyFormat" type="text" ng-model="identityProvider.config.nameIDPolicyFormat">
+ </div>
+ <span tooltip-placement="right" tooltip="Specifies the URI reference corresponding to a name identifier format. Defaults to urn:oasis:names:tc:SAML:2.0:nameid-format:persistent." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix" data-ng-show="!importFile">
+ <label class="col-sm-2 control-label" for="signingPublicKey">Signing Public Key</label>
+ <div class="col-sm-4">
+ <textarea class="form-control" id="signingPublicKey" ng-model="identityProvider.config.signingPublicKey"/>
+ </div>
+ <span tooltip-placement="right" tooltip="The public key that must be used to check for signatures." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="forceAuthn">Force Authentication</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.config.forceAuthn" id="forceAuthn" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip=" Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="validateSignature">Validate Signature</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.config.validateSignature" id="validateSignature" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Enable/disable signature validation of SAML responses." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="enabled">Enabled</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.enabled" id="enabled" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Enable/disable this identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="updateProfileFirstLogin">Update Profile on First Login</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.updateProfileFirstLogin" name="identityProvider.updateProfileFirstLogin" id="updateProfileFirstLogin" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
+ </div>
+ </fieldset>
+
+ <div class="pull-right form-actions">
+ <button kc-save data-ng-show="!importFile">Save</button>
+ <button type="submit" data-ng-click="clearFileSelect()" data-ng-show="importFile" class="btn btn-lg btn-default">Cancel</button>
+ <button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-lg btn-primary">Import from SAML Metadata</button>
+ <button kc-delete data-ng-click="remove()" data-ng-show="!newIdentityProvider">Delete</button>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html
new file mode 100755
index 0000000..c2c7a63
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-social.html
@@ -0,0 +1,68 @@
+<div class="bs-sidebar col-sm-3 " data-ng-include data-src="'partials/realm-menu.html'"></div>
+ <div id="content-area" class="col-sm-9" role="main">
+ <data-kc-navigation data-kc-current="social" data-kc-realm="realm.realm" data-kc-social="realm.social"></data-kc-navigation>
+ <h2></h2>
+ <div id="content">
+ <ol class="breadcrumb">
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Social Providers</a></li>
+ <li class="active">{{identityProvider.name}} Settings</li>
+ </ol>
+ <h2 class="pull-left">{{identityProvider.name}} Provider Settings</h2>
+ <p class="subtitle"><span class="required">*</span> Required fields</p>
+ <form class="form-horizontal" name="realmForm" novalidate>
+ <fieldset>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="identifier" type="text" ng-model="identityProvider.id" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The alias unique identifies an identity provider and it is also used to build the redirect uri." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="name">Name <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="name" type="text" ng-model="identityProvider.name" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The friendly name for this identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="clientId">Client ID <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="clientId" type="text" ng-model="identityProvider.config.clientId" required>
+ </div>
+ <span tooltip-placement="right" tooltip="The client or application identifier registered withing the identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="clientSecret">Client Secret <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <input class="form-control" id="clientSecret" type="password" ng-model="identityProvider.config.clientSecret" ng-show="hidePassword" required>
+ <input class="form-control" id="clientSecret" type="text" ng-model="identityProvider.config.clientSecret" ng-show="!hidePassword" required>
+ <a href="" ng-click="showPassword(false)" class="link" ng-show="hidePassword">Show Secret</a>
+ <a href="" ng-click="showPassword(true);" ng-show="!hidePassword">Hide Secret</a>
+ </div>
+ <span tooltip-placement="right" tooltip="The client or application secret registered withing the identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="enabled">Enabled</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.enabled" id="enabled" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Enable/disable this identity provider." class="fa fa-info-circle"></span>
+ </div>
+ <div class="form-group">
+ <label class="col-sm-2 control-label" for="updateProfileFirstLogin">Update Profile on First Login</label>
+ <div class="col-sm-4">
+ <input ng-model="identityProvider.updateProfileFirstLogin" name="identityProvider.updateProfileFirstLogin" id="updateProfileFirstLogin" onoffswitch />
+ </div>
+ <span tooltip-placement="right" tooltip="Indicates if user must update his profile right after the first login." class="fa fa-info-circle"></span>
+ </div>
+ </fieldset>
+
+ <div class="pull-right form-actions">
+ <button kc-save>Save</button>
+ <button kc-delete data-ng-click="remove()" data-ng-show="!newIdentityProvider">Delete</button>
+ </div>
+ </form>
+ </div>
+ </div>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-twitter.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-twitter.html
new file mode 100755
index 0000000..d565cfb
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-identity-provider-twitter.html
@@ -0,0 +1 @@
+<div data-ng-include data-src="'partials/realm-identity-provider-social.html'"></div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-login-settings.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-login-settings.html
index 27696ef..ff318b0 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-login-settings.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-login-settings.html
@@ -8,20 +8,6 @@
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<div class="form-group">
- <label class="col-sm-2 control-label" for="social">Social login</label>
- <div class="col-sm-4">
- <input ng-model="realm.social" name="social" id="social" onoffswitch />
- </div>
- <span tooltip-placement="right" tooltip="Enable/disable social login via Facebook, Google+, etc." class="fa fa-info-circle"></span>
- </div>
- <div class="form-group" data-ng-show="realm.social">
- <label class="col-sm-2 control-label" for="updateProfileOnInitialSocialLogin">Update profile on first social login</label>
- <div class="col-sm-4">
- <input ng-model="realm.updateProfileOnInitialSocialLogin" name="updateProfileOnInitialSocialLogin" id="updateProfileOnInitialSocialLogin" onoffswitch />
- </div>
- <span tooltip-placement="right" tooltip="Should user information from social provider be imported into user database?" class="fa fa-info-circle"></span>
- </div>
- <div class="form-group">
<label for="registrationAllowed" class="col-sm-2 control-label">User registration</label>
<div class="col-sm-4">
<input ng-model="realm.registrationAllowed" name="registrationAllowed" id="registrationAllowed" onoffswitch />
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/role-mappings.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/role-mappings.html
index 9cf5ada..6a07ca7 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/role-mappings.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/role-mappings.html
@@ -5,7 +5,7 @@
<li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
- <li data-ng-show="realm.social"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/social-links">Social Links</a></li>
+ <li data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/social-links">Federated Identities</a></li>
</ul>
<div id="content">
<ol class="breadcrumb">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-credentials.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-credentials.html
index 07ff9e2..0efd271 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-credentials.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-credentials.html
@@ -6,7 +6,7 @@
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
- <li data-ng-show="realm.social"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/social-links">Social Links</a></li>
+ <li data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/federated-identity">Federated Identities</a></li>
</ul>
<div id="content">
<ol class="breadcrumb">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-detail.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-detail.html
index 41afecc..c678571 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-detail.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-detail.html
@@ -6,7 +6,7 @@
<li data-ng-show="access.manageUsers"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
- <li data-ng-show="realm.social"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/social-links">Social Links</a></li>
+ <li data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/federated-identity">Federated Identities</a></li>
</ul>
<ul class="nav nav-tabs nav-tabs-pf" data-ng-show="create">
<li class="active"><a href="">User List</a></li>
@@ -27,7 +27,7 @@
<p class="subtitle"><span class="required">*</span> Required fields</p>
<form class="form-horizontal" name="userForm" novalidate kc-read-only="!access.manageUsers">
-
+
<fieldset class="border-top">
<div class="form-group">
<label class="col-sm-2 control-label"for="id">ID</label>
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-sessions.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-sessions.html
index 26fbab2..a2a8ee0 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-sessions.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/user-sessions.html
@@ -5,7 +5,7 @@
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/user-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.realm}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
<li class="active"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/sessions">Sessions</a></li>
- <li data-ng-show="realm.social"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/social-links">Social Links</a></li>
+ <li data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/users/{{user.username}}/federated-identity">Federated Identities</a></li>
</ul>
<div id="content">
<ol class="breadcrumb">
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation.html
index 19f2344..8d1ab9b 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/templates/kc-navigation.html
@@ -1,7 +1,7 @@
<ul class="nav nav-tabs nav-tabs-pf">
<li ng-class="{active: !path[2]}"><a href="#/realms/{{realm.realm}}">General</a></li>
<li ng-class="{active: path[2] == 'login-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/login-settings">Login</a></li>
- <li ng-class="{active: path[2] == 'social-settings'}" data-ng-show="realm.social && access.viewRealm"><a href="#/realms/{{realm.realm}}/social-settings">Social</a></li>
+ <li ng-class="{active: path[2] == 'identity-provider-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Provider</a></li>
<li ng-class="{active: path[2] == 'required-credentials'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/required-credentials">Credentials</a></li>
<li ng-class="{active: path[2] == 'keys-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/keys-settings">Keys</a></li>
<li ng-class="{active: path[2] == 'smtp-settings'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/smtp-settings">Email</a></li>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
index 6b12161..bd8f0b2 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
+++ b/forms/common-themes/src/main/resources/theme/login/base/messages/messages.properties
@@ -53,7 +53,8 @@ successTotpRemoved=Mobile authenticator removed.
usernameExists=Username already exists
emailExists=Email already exists
-socialEmailExists=User with email already exists. Please login to account management to link the account.
+federatedIdentityEmailExists=User with email already exists. Please login to account management to link the account.
+federatedIdentityUsernameExists=User with username already exists. Please login to account management to link the account.
loginTitle=Log in to
loginOauthTitle=Temporary access.
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index 744d588..704f8a7 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -20,7 +20,7 @@ import org.keycloak.login.freemarker.model.OAuthGrantBean;
import org.keycloak.login.freemarker.model.ProfileBean;
import org.keycloak.login.freemarker.model.RealmBean;
import org.keycloak.login.freemarker.model.RegisterBean;
-import org.keycloak.login.freemarker.model.SocialBean;
+import org.keycloak.login.freemarker.model.IdentityProviderBean;
import org.keycloak.login.freemarker.model.TotpBean;
import org.keycloak.login.freemarker.model.UrlBean;
import org.keycloak.models.ClientModel;
@@ -188,7 +188,7 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
if (realm != null) {
attributes.put("realm", new RealmBean(realm));
- attributes.put("social", new SocialBean(realm, baseUri));
+ attributes.put("social", new IdentityProviderBean(realm, baseUri));
attributes.put("url", new UrlBean(realm, theme, baseUri));
}
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
index 3dcebd8..f751cf9 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
@@ -40,8 +40,8 @@ public class RealmBean {
return realm.getName();
}
- public boolean isSocial() {
- return realm.isSocial();
+ public boolean isIdentityFederationEnabled() {
+ return realm.isIdentityFederationEnabled();
}
public boolean isRegistrationAllowed() {
@@ -64,5 +64,5 @@ public class RealmBean {
}
return false;
}
-
+
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
index ee27557..839ca23 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/UserResource.java
@@ -1,7 +1,7 @@
package org.keycloak.admin.client.resource;
import org.keycloak.representations.idm.CredentialRepresentation;
-import org.keycloak.representations.idm.SocialLinkRepresentation;
+import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
@@ -55,11 +55,11 @@ public interface UserResource {
@GET
@Path("social-links")
- public List<SocialLinkRepresentation> getSocialLinks();
+ public List<FederatedIdentityRepresentation> getSocialLinks();
@POST
@Path("social-links/{provider}")
- public Response addSocialLink(@PathParam("provider") String provider, SocialLinkRepresentation rep);
+ public Response addSocialLink(@PathParam("provider") String provider, FederatedIdentityRepresentation rep);
@Path("social-links/{provider}")
@DELETE
diff --git a/model/api/src/main/java/org/keycloak/models/entities/FederatedIdentityEntity.java b/model/api/src/main/java/org/keycloak/models/entities/FederatedIdentityEntity.java
new file mode 100644
index 0000000..a8fa647
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/FederatedIdentityEntity.java
@@ -0,0 +1,62 @@
+package org.keycloak.models.entities;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class FederatedIdentityEntity {
+
+ private String userId;
+ private String userName;
+ private String identityProvider;
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getIdentityProvider() {
+ return identityProvider;
+ }
+
+ public void setIdentityProvider(String identityProvider) {
+ this.identityProvider = identityProvider;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ FederatedIdentityEntity that = (FederatedIdentityEntity) o;
+
+ if (identityProvider != null && (that.identityProvider == null || !identityProvider.equals(that.identityProvider))) return false;
+ if (userId != null && (that.userId == null || !userId.equals(that.userId))) return false;
+ if (identityProvider == null && that.identityProvider != null)return false;
+ if (userId == null && that.userId != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int code = 1;
+ if (userId != null) {
+ code = code * userId.hashCode() * 13;
+ }
+ if (identityProvider != null) {
+ code = code * identityProvider.hashCode() * 17;
+ }
+ return code;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java b/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
new file mode 100644
index 0000000..06d2cca
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
@@ -0,0 +1,64 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models.entities;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+public class IdentityProviderEntity {
+
+ private String id;
+ private String name;
+ private String iconUrl;
+ private Map<String, String> config = new HashMap<String, String>();
+
+ public String getId() {
+ return this.id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getIconUrl() {
+ return this.iconUrl;
+ }
+
+ public void setIconUrl(String iconUrl) {
+ this.iconUrl = iconUrl;
+ }
+
+ public Map<String, String> getConfig() {
+ return this.config;
+ }
+
+ public void setConfig(Map<String, String> config) {
+ this.config = config;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
index ef38009..0796808 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RealmEntity.java
@@ -19,7 +19,6 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private boolean passwordCredentialGrantAllowed;
private boolean resetPasswordAllowed;
private boolean social;
- private boolean updateProfileOnInitialSocialLogin;
private String passwordPolicy;
//--- brute force settings
private boolean bruteForceProtected;
@@ -128,22 +127,6 @@ public class RealmEntity extends AbstractIdentifiableEntity {
this.resetPasswordAllowed = resetPasswordAllowed;
}
- public boolean isSocial() {
- return social;
- }
-
- public void setSocial(boolean social) {
- this.social = social;
- }
-
- public boolean isUpdateProfileOnInitialSocialLogin() {
- return updateProfileOnInitialSocialLogin;
- }
-
- public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
- this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
- }
-
public String getPasswordPolicy() {
return passwordPolicy;
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java b/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
index 50be198..c84a805 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
@@ -26,7 +26,7 @@ public class UserEntity extends AbstractIdentifiableEntity {
private Map<String, String> attributes;
private List<UserModel.RequiredAction> requiredActions;
private List<CredentialEntity> credentials = new ArrayList<CredentialEntity>();
- private List<SocialLinkEntity> socialLinks;
+ private List<FederatedIdentityEntity> socialLinks;
private String federationLink;
public String getUsername() {
@@ -125,11 +125,11 @@ public class UserEntity extends AbstractIdentifiableEntity {
this.credentials = credentials;
}
- public List<SocialLinkEntity> getSocialLinks() {
+ public List<FederatedIdentityEntity> getSocialLinks() {
return socialLinks;
}
- public void setSocialLinks(List<SocialLinkEntity> socialLinks) {
+ public void setSocialLinks(List<FederatedIdentityEntity> socialLinks) {
this.socialLinks = socialLinks;
}
diff --git a/model/api/src/main/java/org/keycloak/models/FederatedIdentityModel.java b/model/api/src/main/java/org/keycloak/models/FederatedIdentityModel.java
new file mode 100755
index 0000000..77db720
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/FederatedIdentityModel.java
@@ -0,0 +1,31 @@
+package org.keycloak.models;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class FederatedIdentityModel {
+
+ private String userId;
+ private String identityProvider;
+ private String userName;
+
+ public FederatedIdentityModel() {};
+
+ public FederatedIdentityModel(String identityProvider, String userId, String userName) {
+ this.userId = userId;
+ this.identityProvider = identityProvider;
+ this.userName = userName;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public String getIdentityProvider() {
+ return identityProvider;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java b/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
new file mode 100644
index 0000000..457ba35
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
@@ -0,0 +1,118 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.models;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * <p>A model type representing the configuration for identity providers. It provides some common properties and also a {@link org.keycloak.models.IdentityProviderModel#config}
+ * for configuration options and properties specifics to a identity provider.</p>
+ *
+ * @author Pedro Igor
+ */
+public class IdentityProviderModel {
+
+ /**
+ * <p>An user-defined identifier to unique identify an identity provider instance.</p>
+ */
+ private String id;
+
+ /**
+ * <p>An identifier used to reference a specific identity provider implementation. The value of this field is the same
+ * across instances of the same provider implementation.</p>
+ */
+ private String providerId;
+
+ /**
+ * <p>An user-defined friendly name for an identity provider instance.</p>
+ */
+ private String name;
+
+ private boolean enabled;
+
+ private boolean updateProfileFirstLogin = true;
+
+ /**
+ * <p>A map containing the configuration and properties for a specific identity provider instance and implementation. The items
+ * in the map are understood by the identity provider implementation.</p>
+ */
+ private Map<String, String> config = new HashMap<String, String>();
+
+ public IdentityProviderModel() {
+ this(null, null, null, null);
+ }
+
+ public IdentityProviderModel(String providerId, String id, String name, Map<String, String> config) {
+ this.providerId = providerId;
+ this.id = id;
+ this.name = name;
+
+ if (config != null) {
+ this.config.putAll(config);
+ }
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getProviderId() {
+ return this.providerId;
+ }
+
+ public void setProviderId(String providerId) {
+ this.providerId = providerId;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isUpdateProfileFirstLogin() {
+ return this.updateProfileFirstLogin;
+ }
+
+ public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) {
+ this.updateProfileFirstLogin = updateProfileFirstLogin;
+ }
+
+ public Map<String, String> getConfig() {
+ return this.config;
+ }
+
+ public void setConfig(Map<String, String> config) {
+ this.config = config;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/RealmModel.java b/model/api/src/main/java/org/keycloak/models/RealmModel.java
index ffcd7a5..454179e 100755
--- a/model/api/src/main/java/org/keycloak/models/RealmModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RealmModel.java
@@ -124,9 +124,9 @@ public interface RealmModel extends RoleContainerModel {
RoleModel getRoleById(String id);
List<String> getDefaultRoles();
-
+
void addDefaultRole(String name);
-
+
void updateDefaultRoles(String[] defaultRoles);
ClientModel findClient(String clientId);
@@ -146,14 +146,6 @@ public interface RealmModel extends RoleContainerModel {
void updateRequiredCredentials(Set<String> creds);
- boolean isSocial();
-
- void setSocial(boolean social);
-
- boolean isUpdateProfileOnInitialSocialLogin();
-
- void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin);
-
OAuthClientModel addOAuthClient(String name);
OAuthClientModel addOAuthClient(String id, String name);
@@ -171,9 +163,10 @@ public interface RealmModel extends RoleContainerModel {
void setSmtpConfig(Map<String, String> smtpConfig);
- Map<String, String> getSocialConfig();
-
- void setSocialConfig(Map<String, String> socialConfig);
+ List<IdentityProviderModel> getIdentityProviders();
+ void addIdentityProvider(IdentityProviderModel identityProvider);
+ void removeIdentityProviderById(String providerId);
+ void updateIdentityProvider(IdentityProviderModel identityProvider);
List<UserFederationProviderModel> getUserFederationProviders();
@@ -228,4 +221,5 @@ public interface RealmModel extends RoleContainerModel {
ClientModel findClientById(String id);
+ boolean isIdentityFederationEnabled();
}
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
index 2a37f6c..3bb00f3 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -116,16 +116,16 @@ public class UserFederationManager implements UserProvider {
}
@Override
- public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
+ public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
validateUser(realm, user);
- session.userStorage().addSocialLink(realm, user, socialLink);
+ session.userStorage().addFederatedIdentity(realm, user, socialLink);
}
@Override
- public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider) {
+ public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider) {
validateUser(realm, user);
if (user == null) throw new IllegalStateException("Federated user no longer valid");
- return session.userStorage().removeSocialLink(realm, user, socialProvider);
+ return session.userStorage().removeFederatedIdentity(realm, user, socialProvider);
}
@Override
@@ -168,8 +168,8 @@ public class UserFederationManager implements UserProvider {
}
@Override
- public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
- UserModel user = session.userStorage().getUserBySocialLink(socialLink, realm);
+ public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
+ UserModel user = session.userStorage().getUserByFederatedIdentity(socialLink, realm);
if (user != null) {
user = validateAndProxyUser(realm, user);
}
@@ -278,17 +278,17 @@ public class UserFederationManager implements UserProvider {
}
@Override
- public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
+ public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
validateUser(realm, user);
if (user == null) throw new IllegalStateException("Federated user no longer valid");
- return session.userStorage().getSocialLinks(user, realm);
+ return session.userStorage().getFederatedIdentities(user, realm);
}
@Override
- public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
+ public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
validateUser(realm, user);
if (user == null) throw new IllegalStateException("Federated user no longer valid");
- return session.userStorage().getSocialLink(user, socialProvider, realm);
+ return session.userStorage().getFederatedIdentity(user, socialProvider, realm);
}
@Override
diff --git a/model/api/src/main/java/org/keycloak/models/UserProvider.java b/model/api/src/main/java/org/keycloak/models/UserProvider.java
index dd7b08b..f0b7272 100755
--- a/model/api/src/main/java/org/keycloak/models/UserProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/UserProvider.java
@@ -17,13 +17,13 @@ public interface UserProvider extends Provider {
UserModel addUser(RealmModel realm, String username);
boolean removeUser(RealmModel realm, UserModel user);
- public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink);
- public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider);
+ public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink);
+ public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider);
UserModel getUserById(String id, RealmModel realm);
UserModel getUserByUsername(String username, RealmModel realm);
UserModel getUserByEmail(String email, RealmModel realm);
- UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm);
+ UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm);
List<UserModel> getUsers(RealmModel realm);
int getUsersCount(RealmModel realm);
List<UserModel> getUsers(RealmModel realm, int firstResult, int maxResults);
@@ -31,8 +31,8 @@ public interface UserProvider extends Provider {
List<UserModel> searchForUser(String search, RealmModel realm, int firstResult, int maxResults);
List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm);
List<UserModel> searchForUserByAttributes(Map<String, String> attributes, RealmModel realm, int firstResult, int maxResults);
- Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm);
- SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm);
+ Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm);
+ FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm);
void preRemove(RealmModel realm);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 0be1dc7..a96e217 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -4,12 +4,12 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClaimMask;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
-import org.keycloak.models.Constants;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
@@ -17,11 +17,12 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.ClaimRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.FederatedIdentityRepresentation;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.representations.idm.SocialLinkRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
@@ -80,9 +81,7 @@ public class ModelToRepresentation {
rep.setId(realm.getId());
rep.setRealm(realm.getName());
rep.setEnabled(realm.isEnabled());
- rep.setSocial(realm.isSocial());
rep.setNotBefore(realm.getNotBefore());
- rep.setUpdateProfileOnInitialSocialLogin(realm.isUpdateProfileOnInitialSocialLogin());
rep.setSslRequired(realm.getSslRequired().name().toLowerCase());
rep.setPublicKey(realm.getPublicKeyPem());
if (internal) {
@@ -112,7 +111,6 @@ public class ModelToRepresentation {
rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction());
rep.setSmtpServer(realm.getSmtpConfig());
- rep.setSocialProviders(realm.getSocialConfig());
rep.setBrowserSecurityHeaders(realm.getBrowserSecurityHeaders());
rep.setAccountTheme(realm.getAccountTheme());
rep.setLoginTheme(realm.getLoginTheme());
@@ -146,6 +144,20 @@ public class ModelToRepresentation {
}
rep.setUserFederationProviders(fedProviderReps);
}
+
+ for (IdentityProviderModel provider : realm.getIdentityProviders()) {
+ IdentityProviderRepresentation providerRep = new IdentityProviderRepresentation();
+
+ providerRep.setProviderId(provider.getProviderId());
+ providerRep.setId(provider.getId());
+ providerRep.setName(provider.getName());
+ providerRep.setEnabled(provider.isEnabled());
+ providerRep.setUpdateProfileFirstLogin(provider.isUpdateProfileFirstLogin());
+ providerRep.setConfig(provider.getConfig());
+
+ rep.addIdentityProvider(providerRep);
+ }
+
return rep;
}
@@ -185,11 +197,11 @@ public class ModelToRepresentation {
return rep;
}
- public static SocialLinkRepresentation toRepresentation(SocialLinkModel socialLink) {
- SocialLinkRepresentation rep = new SocialLinkRepresentation();
- rep.setSocialUsername(socialLink.getSocialUsername());
- rep.setSocialProvider(socialLink.getSocialProvider());
- rep.setSocialUserId(socialLink.getSocialUserId());
+ public static FederatedIdentityRepresentation toRepresentation(FederatedIdentityModel socialLink) {
+ FederatedIdentityRepresentation rep = new FederatedIdentityRepresentation();
+ rep.setUserName(socialLink.getUserName());
+ rep.setIdentityProvider(socialLink.getIdentityProvider());
+ rep.setUserId(socialLink.getUserId());
return rep;
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 5ba8d06..2ab0afe 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -7,12 +7,12 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.BrowserSecurityHeaders;
import org.keycloak.models.ClaimMask;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserFederationProviderModel;
@@ -20,11 +20,11 @@ import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.ClaimRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.ScopeMappingRepresentation;
-import org.keycloak.representations.idm.SocialLinkRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
@@ -44,7 +44,6 @@ public class RepresentationToModel {
public static void importRealm(KeycloakSession session, RealmRepresentation rep, RealmModel newRealm) {
newRealm.setName(rep.getRealm());
if (rep.isEnabled() != null) newRealm.setEnabled(rep.isEnabled());
- if (rep.isSocial() != null) newRealm.setSocial(rep.isSocial());
if (rep.isBruteForceProtected() != null) newRealm.setBruteForceProtected(rep.isBruteForceProtected());
if (rep.getMaxFailureWaitSeconds() != null) newRealm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
if (rep.getMinimumQuickLoginWaitSeconds() != null) newRealm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
@@ -79,8 +78,6 @@ public class RepresentationToModel {
if (rep.isRememberMe() != null) newRealm.setRememberMe(rep.isRememberMe());
if (rep.isVerifyEmail() != null) newRealm.setVerifyEmail(rep.isVerifyEmail());
if (rep.isResetPasswordAllowed() != null) newRealm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
- if (rep.isUpdateProfileOnInitialSocialLogin() != null)
- newRealm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin());
if (rep.getPrivateKey() == null || rep.getPublicKey() == null) {
KeycloakModelUtils.generateRealmKeys(newRealm);
} else {
@@ -220,9 +217,6 @@ public class RepresentationToModel {
newRealm.setBrowserSecurityHeaders(BrowserSecurityHeaders.defaultHeaders);
}
- if (rep.getSocialProviders() != null) {
- newRealm.setSocialConfig(new HashMap(rep.getSocialProviders()));
- }
if (rep.getUserFederationProviders() != null) {
List<UserFederationProviderModel> providerModels = convertFederationProviders(rep.getUserFederationProviders());
newRealm.setUserFederationProviders(providerModels);
@@ -242,7 +236,6 @@ public class RepresentationToModel {
realm.setName(rep.getRealm());
}
if (rep.isEnabled() != null) realm.setEnabled(rep.isEnabled());
- if (rep.isSocial() != null) realm.setSocial(rep.isSocial());
if (rep.isBruteForceProtected() != null) realm.setBruteForceProtected(rep.isBruteForceProtected());
if (rep.getMaxFailureWaitSeconds() != null) realm.setMaxFailureWaitSeconds(rep.getMaxFailureWaitSeconds());
if (rep.getMinimumQuickLoginWaitSeconds() != null) realm.setMinimumQuickLoginWaitSeconds(rep.getMinimumQuickLoginWaitSeconds());
@@ -255,8 +248,6 @@ public class RepresentationToModel {
if (rep.isRememberMe() != null) realm.setRememberMe(rep.isRememberMe());
if (rep.isVerifyEmail() != null) realm.setVerifyEmail(rep.isVerifyEmail());
if (rep.isResetPasswordAllowed() != null) realm.setResetPasswordAllowed(rep.isResetPasswordAllowed());
- if (rep.isUpdateProfileOnInitialSocialLogin() != null)
- realm.setUpdateProfileOnInitialSocialLogin(rep.isUpdateProfileOnInitialSocialLogin());
if (rep.getSslRequired() != null) realm.setSslRequired(SslRequired.valueOf(rep.getSslRequired().toUpperCase()));
if (rep.getAccessCodeLifespan() != null) realm.setAccessCodeLifespan(rep.getAccessCodeLifespan());
if (rep.getAccessCodeLifespanUserAction() != null)
@@ -286,10 +277,6 @@ public class RepresentationToModel {
realm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
}
- if (rep.getSocialProviders() != null) {
- realm.setSocialConfig(new HashMap(rep.getSocialProviders()));
- }
-
if (rep.getBrowserSecurityHeaders() != null) {
realm.setBrowserSecurityHeaders(rep.getBrowserSecurityHeaders());
}
@@ -670,10 +657,10 @@ public class RepresentationToModel {
updateCredential(user, cred);
}
}
- if (userRep.getSocialLinks() != null) {
- for (SocialLinkRepresentation socialLink : userRep.getSocialLinks()) {
- SocialLinkModel mappingModel = new SocialLinkModel(socialLink.getSocialProvider(), socialLink.getSocialUserId(), socialLink.getSocialUsername());
- session.users().addSocialLink(newRealm, user, mappingModel);
+ if (userRep.getFederatedIdentities() != null) {
+ for (FederatedIdentityRepresentation identity : userRep.getFederatedIdentities()) {
+ FederatedIdentityModel mappingModel = new FederatedIdentityModel(identity.getIdentityProvider(), identity.getUserId(), identity.getUserName());
+ session.users().addFederatedIdentity(newRealm, user, mappingModel);
}
}
if (userRep.getRealmRoles() != null) {
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
index 678bf70..2509081 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/DefaultCacheUserProvider.java
@@ -4,7 +4,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
@@ -191,8 +191,8 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
}
@Override
- public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
- return getDelegate().getUserBySocialLink(socialLink, realm);
+ public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
+ return getDelegate().getUserByFederatedIdentity(socialLink, realm);
}
@Override
@@ -231,13 +231,13 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
}
@Override
- public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
- return getDelegate().getSocialLinks(user, realm);
+ public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
+ return getDelegate().getFederatedIdentities(user, realm);
}
@Override
- public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
- return getDelegate().getSocialLink(user, socialProvider, realm);
+ public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
+ return getDelegate().getFederatedIdentity(user, socialProvider, realm);
}
@Override
@@ -258,13 +258,13 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
}
@Override
- public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
- getDelegate().addSocialLink(realm, user, socialLink);
+ public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
+ getDelegate().addFederatedIdentity(realm, user, socialLink);
}
@Override
- public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider) {
- return getDelegate().removeSocialLink(realm, user, socialProvider);
+ public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider) {
+ return getDelegate().removeFederatedIdentity(realm, user, socialProvider);
}
@Override
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
index 76c9204..0a13d92 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
@@ -2,6 +2,7 @@ package org.keycloak.models.cache.entities;
import org.keycloak.enums.SslRequired;
import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
@@ -34,8 +35,7 @@ public class CachedRealm {
private boolean verifyEmail;
private boolean passwordCredentialGrantAllowed;
private boolean resetPasswordAllowed;
- private boolean social;
- private boolean updateProfileOnInitialSocialLogin;
+ private boolean identityFederationEnabled;
//--- brute force settings
private boolean bruteForceProtected;
private int maxFailureWaitSeconds;
@@ -67,10 +67,10 @@ public class CachedRealm {
private List<RequiredCredentialModel> requiredCredentials = new ArrayList<RequiredCredentialModel>();
private List<UserFederationProviderModel> userFederationProviders = new ArrayList<UserFederationProviderModel>();
+ private List<IdentityProviderModel> identityProviders = new ArrayList<IdentityProviderModel>();
private Map<String, String> browserSecurityHeaders = new HashMap<String, String>();
private Map<String, String> smtpConfig = new HashMap<String, String>();
- private Map<String, String> socialConfig = new HashMap<String, String>();
private boolean eventsEnabled;
private long eventsExpiration;
@@ -93,8 +93,7 @@ public class CachedRealm {
verifyEmail = model.isVerifyEmail();
passwordCredentialGrantAllowed = model.isPasswordCredentialGrantAllowed();
resetPasswordAllowed = model.isResetPasswordAllowed();
- social = model.isSocial();
- updateProfileOnInitialSocialLogin = model.isUpdateProfileOnInitialSocialLogin();
+ identityFederationEnabled = model.isIdentityFederationEnabled();
//--- brute force settings
bruteForceProtected = model.isBruteForceProtected();
maxFailureWaitSeconds = model.getMaxFailureWaitSeconds();
@@ -125,9 +124,9 @@ public class CachedRealm {
requiredCredentials = model.getRequiredCredentials();
userFederationProviders = model.getUserFederationProviders();
+ identityProviders = model.getIdentityProviders();
smtpConfig.putAll(model.getSmtpConfig());
- socialConfig.putAll(model.getSocialConfig());
browserSecurityHeaders.putAll(model.getBrowserSecurityHeaders());
eventsEnabled = model.isEventsEnabled();
@@ -281,22 +280,14 @@ public class CachedRealm {
return passwordPolicy;
}
- public boolean isSocial() {
- return social;
- }
-
- public boolean isUpdateProfileOnInitialSocialLogin() {
- return updateProfileOnInitialSocialLogin;
+ public boolean isIdentityFederationEnabled() {
+ return identityFederationEnabled;
}
public Map<String, String> getSmtpConfig() {
return smtpConfig;
}
- public Map<String, String> getSocialConfig() {
- return socialConfig;
- }
-
public Map<String, String> getBrowserSecurityHeaders() {
return browserSecurityHeaders;
}
@@ -340,4 +331,8 @@ public class CachedRealm {
public String getCertificatePem() {
return certificatePem;
}
+
+ public List<IdentityProviderModel> getIdentityProviders() {
+ return identityProviders;
+ }
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
index 936604c..7063332 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/NoCacheUserProvider.java
@@ -3,7 +3,7 @@ package org.keycloak.models.cache;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
@@ -66,8 +66,8 @@ public class NoCacheUserProvider implements CacheUserProvider {
}
@Override
- public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
- return getDelegate().getUserBySocialLink(socialLink, realm);
+ public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
+ return getDelegate().getUserByFederatedIdentity(socialLink, realm);
}
@Override
@@ -106,13 +106,13 @@ public class NoCacheUserProvider implements CacheUserProvider {
}
@Override
- public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
- return getDelegate().getSocialLinks(user, realm);
+ public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
+ return getDelegate().getFederatedIdentities(user, realm);
}
@Override
- public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
- return getDelegate().getSocialLink(user, socialProvider, realm);
+ public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
+ return getDelegate().getFederatedIdentity(user, socialProvider, realm);
}
@Override
@@ -131,13 +131,13 @@ public class NoCacheUserProvider implements CacheUserProvider {
}
@Override
- public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
- getDelegate().addSocialLink(realm, user, socialLink);
+ public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
+ getDelegate().addFederatedIdentity(realm, user, socialLink);
}
@Override
- public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider) {
- return getDelegate().removeSocialLink(realm, user, socialProvider);
+ public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider) {
+ return getDelegate().removeFederatedIdentity(realm, user, socialProvider);
}
@Override
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index 8cc7297..98e120c 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -4,6 +4,7 @@ import org.keycloak.Config;
import org.keycloak.enums.SslRequired;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
@@ -533,30 +534,6 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public boolean isSocial() {
- if (updated != null) return updated.isSocial();
- return cached.isSocial();
- }
-
- @Override
- public void setSocial(boolean social) {
- getDelegateForUpdate();
- updated.setSocial(social);
- }
-
- @Override
- public boolean isUpdateProfileOnInitialSocialLogin() {
- if (updated != null) return updated.isUpdateProfileOnInitialSocialLogin();
- return cached.isUpdateProfileOnInitialSocialLogin();
- }
-
- @Override
- public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
- getDelegateForUpdate();
- updated.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
- }
-
- @Override
public OAuthClientModel addOAuthClient(String name) {
getDelegateForUpdate();
OAuthClientModel client = updated.addOAuthClient(name);
@@ -633,15 +610,27 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public Map<String, String> getSocialConfig() {
- if (updated != null) return updated.getSocialConfig();
- return cached.getSocialConfig();
+ public List<IdentityProviderModel> getIdentityProviders() {
+ if (updated != null) return updated.getIdentityProviders();
+ return cached.getIdentityProviders();
}
@Override
- public void setSocialConfig(Map<String, String> socialConfig) {
+ public void addIdentityProvider(IdentityProviderModel identityProvider) {
getDelegateForUpdate();
- updated.setSocialConfig(socialConfig);
+ updated.addIdentityProvider(identityProvider);
+ }
+
+ @Override
+ public void updateIdentityProvider(IdentityProviderModel identityProvider) {
+ getDelegateForUpdate();
+ updated.updateIdentityProvider(identityProvider);
+ }
+
+ @Override
+ public void removeIdentityProviderById(String providerId) {
+ getDelegateForUpdate();
+ updated.removeIdentityProviderById(providerId);
}
@Override
@@ -842,6 +831,13 @@ public class RealmAdapter implements RealmModel {
}
@Override
+ public boolean isIdentityFederationEnabled() {
+ if (updated != null) return updated.isIdentityFederationEnabled();
+ return cached.isIdentityFederationEnabled();
+ }
+
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof RealmModel)) return false;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
new file mode 100755
index 0000000..96d9c50
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/IdentityProviderEntity.java
@@ -0,0 +1,114 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.CollectionTable;
+import javax.persistence.Column;
+import javax.persistence.ElementCollection;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.MapKeyColumn;
+import javax.persistence.Table;
+import java.util.Map;
+
+/**
+ * @author Pedro Igor
+ */
+@Entity
+@Table(name="IDENTITY_PROVIDER")
+public class IdentityProviderEntity {
+
+ @Id
+ @Column(name="INTERNAL_ID", length = 36)
+ protected String internalId;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "REALM_ID")
+ protected RealmEntity realm;
+
+ @Column(name="PROVIDER_ID")
+ private String providerId;
+
+ @Column(name="PROVIDER_NONIMAL_ID")
+ private String id;
+
+ @Column(name="PROVIDER_NAME")
+ private String name;
+
+ @Column(name="ENABLED")
+ private boolean enabled;
+
+ @Column(name="UPDATE_PROFILE_FIRST_LOGIN")
+ private boolean updateProfileFirstLogin;
+
+ @ElementCollection
+ @MapKeyColumn(name="name")
+ @Column(name="value", columnDefinition = "TEXT")
+ @CollectionTable(name="IDENTITY_PROVIDER_CONFIG", joinColumns={ @JoinColumn(name="IDENTITY_PROVIDER_ID") })
+ private Map<String, String> config;
+
+ public String getInternalId() {
+ return this.internalId;
+ }
+
+ public void setInternalId(String internalId) {
+ this.internalId = internalId;
+ }
+
+ public String getProviderId() {
+ return this.providerId;
+ }
+
+ public void setProviderId(String providerId) {
+ this.providerId = providerId;
+ }
+
+ public RealmEntity getRealm() {
+ return this.realm;
+ }
+
+ public void setRealm(RealmEntity realm) {
+ this.realm = realm;
+ }
+
+ public String getId() {
+ return this.id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public boolean isUpdateProfileFirstLogin() {
+ return this.updateProfileFirstLogin;
+ }
+
+ public void setUpdateProfileFirstLogin(boolean updateProfileFirstLogin) {
+ this.updateProfileFirstLogin = updateProfileFirstLogin;
+ }
+
+ public Map<String, String> getConfig() {
+ return this.config;
+ }
+
+ public void setConfig(Map<String, String> config) {
+ this.config = config;
+ }
+}
\ No newline at end of file
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
index c78e916..0f2cea8 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RealmEntity.java
@@ -1,6 +1,5 @@
package org.keycloak.models.jpa.entities;
-
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
@@ -54,12 +53,8 @@ public class RealmEntity {
protected boolean verifyEmail;
@Column(name="RESET_PASSWORD_ALLOWED")
protected boolean resetPasswordAllowed;
- @Column(name="SOCIAL")
- protected boolean social;
@Column(name="REMEMBER_ME")
protected boolean rememberMe;
- @Column(name="UPDATE_PROFILE_ON_SOC_LOGIN")
- protected boolean updateProfileOnInitialSocialLogin;
@Column(name="PASSWORD_POLICY")
protected String passwordPolicy;
@@ -100,7 +95,6 @@ public class RealmEntity {
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<RequiredCredentialEntity> requiredCredentials = new ArrayList<RequiredCredentialEntity>();
-
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
@JoinTable(name="FED_PROVIDERS")
List<UserFederationProviderEntity> userFederationProviders = new ArrayList<UserFederationProviderEntity>();
@@ -118,12 +112,6 @@ public class RealmEntity {
@CollectionTable(name="REALM_SMTP_CONFIG", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Map<String, String> smtpConfig = new HashMap<String, String>();
- @ElementCollection
- @MapKeyColumn(name="NAME")
- @Column(name="VALUE")
- @CollectionTable(name="REALM_SOCIAL_CONFIG", joinColumns={ @JoinColumn(name="REALM_ID") })
- protected Map<String, String> socialConfig = new HashMap<String, String>();
-
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
@JoinTable(name="REALM_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="REALM_ID")}, inverseJoinColumns = { @JoinColumn(name="ROLE_ID")})
protected Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
@@ -142,6 +130,9 @@ public class RealmEntity {
@JoinColumn(name="MASTER_ADMIN_APP")
protected ApplicationEntity masterAdminApp;
+ @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
+ protected List<IdentityProviderEntity> identityProviders = new ArrayList<IdentityProviderEntity>();
+
public String getId() {
return id;
}
@@ -214,22 +205,6 @@ public class RealmEntity {
this.resetPasswordAllowed = resetPasswordAllowed;
}
- public boolean isSocial() {
- return social;
- }
-
- public void setSocial(boolean social) {
- this.social = social;
- }
-
- public boolean isUpdateProfileOnInitialSocialLogin() {
- return updateProfileOnInitialSocialLogin;
- }
-
- public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
- this.updateProfileOnInitialSocialLogin = updateProfileOnInitialSocialLogin;
- }
-
public int getSsoSessionIdleTimeout() {
return ssoSessionIdleTimeout;
}
@@ -333,14 +308,6 @@ public class RealmEntity {
this.smtpConfig = smtpConfig;
}
- public Map<String, String> getSocialConfig() {
- return socialConfig;
- }
-
- public void setSocialConfig(Map<String, String> socialConfig) {
- this.socialConfig = socialConfig;
- }
-
public Collection<RoleEntity> getDefaultRoles() {
return defaultRoles;
}
@@ -452,5 +419,18 @@ public class RealmEntity {
public void setCertificatePem(String certificatePem) {
this.certificatePem = certificatePem;
}
+
+ public List<IdentityProviderEntity> getIdentityProviders() {
+ return this.identityProviders;
+ }
+
+ public void setIdentityProviders(List<IdentityProviderEntity> identityProviders) {
+ this.identityProviders = identityProviders;
+ }
+
+ public void addIdentityProvider(IdentityProviderEntity entity) {
+ entity.setRealm(this);
+ getIdentityProviders().add(entity);
+ }
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index 675d655..60a79e6 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -1,15 +1,15 @@
package org.keycloak.models.jpa;
import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
-import org.keycloak.models.jpa.entities.SocialLinkEntity;
+import org.keycloak.models.jpa.entities.FederatedIdentityEntity;
import org.keycloak.models.jpa.entities.UserEntity;
import org.keycloak.models.utils.CredentialValidation;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -85,17 +85,17 @@ public class JpaUserProvider implements UserProvider {
private void removeUser(UserEntity user) {
em.createNamedQuery("deleteUserRoleMappingsByUser").setParameter("user", user).executeUpdate();
- em.createNamedQuery("deleteSocialLinkByUser").setParameter("user", user).executeUpdate();
+ em.createNamedQuery("deleteFederatedIdentityByUser").setParameter("user", user).executeUpdate();
em.remove(user);
}
@Override
- public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
- SocialLinkEntity entity = new SocialLinkEntity();
+ public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel identity) {
+ FederatedIdentityEntity entity = new FederatedIdentityEntity();
entity.setRealmId(realm.getId());
- entity.setSocialProvider(socialLink.getSocialProvider());
- entity.setSocialUserId(socialLink.getSocialUserId());
- entity.setSocialUsername(socialLink.getSocialUsername());
+ entity.setIdentityProvider(identity.getIdentityProvider());
+ entity.setUserId(identity.getUserId());
+ entity.setUserName(identity.getUserName());
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
entity.setUser(userEntity);
em.persist(entity);
@@ -103,8 +103,8 @@ public class JpaUserProvider implements UserProvider {
}
@Override
- public boolean removeSocialLink(RealmModel realm, UserModel user, String socialProvider) {
- SocialLinkEntity entity = findSocialLink(user, socialProvider);
+ public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String identityProvider) {
+ FederatedIdentityEntity entity = findFederatedIdentity(user, identityProvider);
if (entity != null) {
em.remove(entity);
em.flush();
@@ -122,7 +122,7 @@ public class JpaUserProvider implements UserProvider {
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteUserRequiredActionsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
- num = em.createNamedQuery("deleteSocialLinkByRealm")
+ num = em.createNamedQuery("deleteFederatedIdentityByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteCredentialsByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
@@ -142,7 +142,7 @@ public class JpaUserProvider implements UserProvider {
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
.executeUpdate();
- num = em.createNamedQuery("deleteSocialLinkByRealmAndLink")
+ num = em.createNamedQuery("deleteFederatedIdentityByRealmAndLink")
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
.executeUpdate();
@@ -199,17 +199,17 @@ public class JpaUserProvider implements UserProvider {
}
@Override
- public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
- TypedQuery<UserEntity> query = em.createNamedQuery("findUserByLinkAndRealm", UserEntity.class);
+ public UserModel getUserByFederatedIdentity(FederatedIdentityModel identity, RealmModel realm) {
+ TypedQuery<UserEntity> query = em.createNamedQuery("findUserByFederatedIdentityAndRealm", UserEntity.class);
query.setParameter("realmId", realm.getId());
- query.setParameter("socialProvider", socialLink.getSocialProvider());
- query.setParameter("socialUserId", socialLink.getSocialUserId());
+ query.setParameter("identityProvider", identity.getIdentityProvider());
+ query.setParameter("userId", identity.getUserId());
List<UserEntity> results = query.getResultList();
if (results.isEmpty()) {
return null;
} else if (results.size() > 1) {
- throw new IllegalStateException("More results found for socialProvider=" + socialLink.getSocialProvider() +
- ", socialUserId=" + socialLink.getSocialUserId() + ", results=" + results);
+ throw new IllegalStateException("More results found for identityProvider=" + identity.getIdentityProvider() +
+ ", userId=" + identity.getUserId() + ", results=" + results);
} else {
UserEntity user = results.get(0);
return new UserAdapter(realm, em, user);
@@ -326,33 +326,33 @@ public class JpaUserProvider implements UserProvider {
return users;
}
- private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
- TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUserAndProvider", SocialLinkEntity.class);
+ private FederatedIdentityEntity findFederatedIdentity(UserModel user, String identityProvider) {
+ TypedQuery<FederatedIdentityEntity> query = em.createNamedQuery("findFederatedIdentityByUserAndProvider", FederatedIdentityEntity.class);
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
query.setParameter("user", userEntity);
- query.setParameter("socialProvider", socialProvider);
- List<SocialLinkEntity> results = query.getResultList();
+ query.setParameter("identityProvider", identityProvider);
+ List<FederatedIdentityEntity> results = query.getResultList();
return results.size() > 0 ? results.get(0) : null;
}
@Override
- public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
- TypedQuery<SocialLinkEntity> query = em.createNamedQuery("findSocialLinkByUser", SocialLinkEntity.class);
+ public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
+ TypedQuery<FederatedIdentityEntity> query = em.createNamedQuery("findFederatedIdentityByUser", FederatedIdentityEntity.class);
UserEntity userEntity = em.getReference(UserEntity.class, user.getId());
query.setParameter("user", userEntity);
- List<SocialLinkEntity> results = query.getResultList();
- Set<SocialLinkModel> set = new HashSet<SocialLinkModel>();
- for (SocialLinkEntity entity : results) {
- set.add(new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()));
+ List<FederatedIdentityEntity> results = query.getResultList();
+ Set<FederatedIdentityModel> set = new HashSet<FederatedIdentityModel>();
+ for (FederatedIdentityEntity entity : results) {
+ set.add(new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName()));
}
return set;
}
@Override
- public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
- SocialLinkEntity entity = findSocialLink(user, socialProvider);
- return (entity != null) ? new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()) : null;
+ public FederatedIdentityModel getFederatedIdentity(UserModel user, String identityProvider, RealmModel realm) {
+ FederatedIdentityEntity entity = findFederatedIdentity(user, identityProvider);
+ return (entity != null) ? new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName()) : null;
}
@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index b58bd71..b8d864c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -3,6 +3,7 @@ package org.keycloak.models.jpa;
import org.keycloak.enums.SslRequired;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
@@ -11,6 +12,7 @@ import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.jpa.entities.ApplicationEntity;
+import org.keycloak.models.jpa.entities.IdentityProviderEntity;
import org.keycloak.models.jpa.entities.OAuthClientEntity;
import org.keycloak.models.jpa.entities.RealmAttributeEntity;
import org.keycloak.models.jpa.entities.RealmEntity;
@@ -683,28 +685,6 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public boolean isSocial() {
- return realm.isSocial();
- }
-
- @Override
- public void setSocial(boolean social) {
- realm.setSocial(social);
- em.flush();
- }
-
- @Override
- public boolean isUpdateProfileOnInitialSocialLogin() {
- return realm.isUpdateProfileOnInitialSocialLogin();
- }
-
- @Override
- public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
- realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
- em.flush();
- }
-
- @Override
public OAuthClientModel addOAuthClient(String name) {
return this.addOAuthClient(KeycloakModelUtils.generateId(), name);
}
@@ -791,17 +771,6 @@ public class RealmAdapter implements RealmModel {
}
@Override
- public Map<String, String> getSocialConfig() {
- return realm.getSocialConfig();
- }
-
- @Override
- public void setSocialConfig(Map<String, String> socialConfig) {
- realm.setSocialConfig(socialConfig);
- em.flush();
- }
-
- @Override
public List<UserFederationProviderModel> getUserFederationProviders() {
List<UserFederationProviderEntity> entities = realm.getUserFederationProviders();
List<UserFederationProviderEntity> copy = new ArrayList<UserFederationProviderEntity>();
@@ -1137,4 +1106,67 @@ public class RealmAdapter implements RealmModel {
em.flush();
}
-}
+ @Override
+ public List<IdentityProviderModel> getIdentityProviders() {
+ List<IdentityProviderModel> identityProviders = new ArrayList<IdentityProviderModel>();
+
+ for (IdentityProviderEntity entity: realm.getIdentityProviders()) {
+ IdentityProviderModel identityProviderModel = new IdentityProviderModel(entity.getProviderId(), entity.getId(), entity.getName(),
+ entity.getConfig());
+
+ identityProviderModel.setEnabled(entity.isEnabled());
+ identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
+
+ identityProviders.add(identityProviderModel);
+ }
+
+ return identityProviders;
+ }
+
+ @Override
+ public void addIdentityProvider(IdentityProviderModel identityProvider) {
+ IdentityProviderEntity entity = new IdentityProviderEntity();
+
+ entity.setInternalId(KeycloakModelUtils.generateId());
+ entity.setId(identityProvider.getId());
+ entity.setProviderId(identityProvider.getProviderId());
+ entity.setName(identityProvider.getName());
+ entity.setEnabled(identityProvider.isEnabled());
+ entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+ entity.setConfig(identityProvider.getConfig());
+
+ realm.addIdentityProvider(entity);
+
+ em.persist(entity);
+ em.flush();
+ }
+
+ @Override
+ public void removeIdentityProviderById(String providerId) {
+ for (IdentityProviderEntity entity : realm.getIdentityProviders()) {
+ if (entity.getId().equals(providerId)) {
+ em.remove(entity);
+ em.flush();
+ }
+ }
+ }
+
+ @Override
+ public void updateIdentityProvider(IdentityProviderModel identityProvider) {
+ for (IdentityProviderEntity entity : this.realm.getIdentityProviders()) {
+ if (entity.getId().equals(identityProvider.getId())) {
+ entity.setName(identityProvider.getName());
+ entity.setEnabled(identityProvider.isEnabled());
+ entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
+ entity.setConfig(identityProvider.getConfig());
+ }
+ }
+
+ em.flush();
+ }
+
+ @Override
+ public boolean isIdentityFederationEnabled() {
+ return !this.realm.getIdentityProviders().isEmpty();
+ }
+}
\ No newline at end of file
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index df11ffd..c67952a 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -9,12 +9,12 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
-import org.keycloak.models.entities.SocialLinkEntity;
+import org.keycloak.models.entities.FederatedIdentityEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
import org.keycloak.models.utils.CredentialValidation;
@@ -93,10 +93,10 @@ public class MongoUserProvider implements UserProvider {
}
@Override
- public UserModel getUserBySocialLink(SocialLinkModel socialLink, RealmModel realm) {
+ public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
DBObject query = new QueryBuilder()
- .and("socialLinks.socialProvider").is(socialLink.getSocialProvider())
- .and("socialLinks.socialUserId").is(socialLink.getSocialUserId())
+ .and("federatedIdentities.identityProvider").is(socialLink.getIdentityProvider())
+ .and("federatedIdentities.userId").is(socialLink.getUserId())
.and("realmId").is(realm.getId())
.get();
MongoUserEntity userEntity = getMongoStore().loadSingleEntity(MongoUserEntity.class, query, invocationContext);
@@ -213,35 +213,35 @@ public class MongoUserProvider implements UserProvider {
}
@Override
- public Set<SocialLinkModel> getSocialLinks(UserModel userModel, RealmModel realm) {
+ public Set<FederatedIdentityModel> getFederatedIdentities(UserModel userModel, RealmModel realm) {
UserModel user = getUserById(userModel.getId(), realm);
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
- List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
+ List<FederatedIdentityEntity> linkEntities = userEntity.getSocialLinks();
if (linkEntities == null) {
return Collections.EMPTY_SET;
}
- Set<SocialLinkModel> result = new HashSet<SocialLinkModel>();
- for (SocialLinkEntity socialLinkEntity : linkEntities) {
- SocialLinkModel model = new SocialLinkModel(socialLinkEntity.getSocialProvider(),
- socialLinkEntity.getSocialUserId(), socialLinkEntity.getSocialUsername());
+ Set<FederatedIdentityModel> result = new HashSet<FederatedIdentityModel>();
+ for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
+ FederatedIdentityModel model = new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(),
+ federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName());
result.add(model);
}
return result;
}
- private SocialLinkEntity findSocialLink(UserModel userModel, String socialProvider, RealmModel realm) {
+ private FederatedIdentityEntity findSocialLink(UserModel userModel, String socialProvider, RealmModel realm) {
UserModel user = getUserById(userModel.getId(), realm);
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
- List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
+ List<FederatedIdentityEntity> linkEntities = userEntity.getSocialLinks();
if (linkEntities == null) {
return null;
}
- for (SocialLinkEntity socialLinkEntity : linkEntities) {
- if (socialLinkEntity.getSocialProvider().equals(socialProvider)) {
- return socialLinkEntity;
+ for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
+ if (federatedIdentityEntity.getIdentityProvider().equals(socialProvider)) {
+ return federatedIdentityEntity;
}
}
return null;
@@ -249,9 +249,9 @@ public class MongoUserProvider implements UserProvider {
@Override
- public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
- SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider, realm);
- return socialLinkEntity != null ? new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUserId(), socialLinkEntity.getSocialUsername()) : null;
+ public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
+ FederatedIdentityEntity federatedIdentityEntity = findSocialLink(user, socialProvider, realm);
+ return federatedIdentityEntity != null ? new FederatedIdentityModel(federatedIdentityEntity.getIdentityProvider(), federatedIdentityEntity.getUserId(), federatedIdentityEntity.getUserName()) : null;
}
@Override
@@ -296,37 +296,37 @@ public class MongoUserProvider implements UserProvider {
@Override
- public void addSocialLink(RealmModel realm, UserModel user, SocialLinkModel socialLink) {
+ public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
user = getUserById(user.getId(), realm);
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
- SocialLinkEntity socialLinkEntity = new SocialLinkEntity();
- socialLinkEntity.setSocialProvider(socialLink.getSocialProvider());
- socialLinkEntity.setSocialUserId(socialLink.getSocialUserId());
- socialLinkEntity.setSocialUsername(socialLink.getSocialUsername());
+ FederatedIdentityEntity federatedIdentityEntity = new FederatedIdentityEntity();
+ federatedIdentityEntity.setIdentityProvider(socialLink.getIdentityProvider());
+ federatedIdentityEntity.setUserId(socialLink.getUserId());
+ federatedIdentityEntity.setUserName(socialLink.getUserName());
- getMongoStore().pushItemToList(userEntity, "socialLinks", socialLinkEntity, true, invocationContext);
+ getMongoStore().pushItemToList(userEntity, "federatedIdentities", federatedIdentityEntity, true, invocationContext);
}
@Override
- public boolean removeSocialLink(RealmModel realm, UserModel userModel, String socialProvider) {
+ public boolean removeFederatedIdentity(RealmModel realm, UserModel userModel, String socialProvider) {
UserModel user = getUserById(userModel.getId(), realm);
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
- SocialLinkEntity socialLinkEntity = findSocialLink(userEntity, socialProvider);
- if (socialLinkEntity == null) {
+ FederatedIdentityEntity federatedIdentityEntity = findSocialLink(userEntity, socialProvider);
+ if (federatedIdentityEntity == null) {
return false;
}
- return getMongoStore().pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext);
+ return getMongoStore().pullItemFromList(userEntity, "federatedIdentities", federatedIdentityEntity, invocationContext);
}
- private SocialLinkEntity findSocialLink(MongoUserEntity userEntity, String socialProvider) {
- List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
+ private FederatedIdentityEntity findSocialLink(MongoUserEntity userEntity, String socialProvider) {
+ List<FederatedIdentityEntity> linkEntities = userEntity.getSocialLinks();
if (linkEntities == null) {
return null;
}
- for (SocialLinkEntity socialLinkEntity : linkEntities) {
- if (socialLinkEntity.getSocialProvider().equals(socialProvider)) {
- return socialLinkEntity;
+ for (FederatedIdentityEntity federatedIdentityEntity : linkEntities) {
+ if (federatedIdentityEntity.getIdentityProvider().equals(socialProvider)) {
+ return federatedIdentityEntity;
}
}
return null;
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 49fe46b..b23d3a0 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -2,11 +2,11 @@ package org.keycloak.models.mongo.keycloak.adapters;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
-import org.jboss.logging.Logger;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.enums.SslRequired;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
@@ -235,28 +235,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
@Override
- public boolean isSocial() {
- return realm.isSocial();
- }
-
- @Override
- public void setSocial(boolean social) {
- realm.setSocial(social);
- updateRealm();
- }
-
- @Override
- public boolean isUpdateProfileOnInitialSocialLogin() {
- return realm.isUpdateProfileOnInitialSocialLogin();
- }
-
- @Override
- public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
- realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
- updateRealm();
- }
-
- @Override
public PasswordPolicy getPasswordPolicy() {
if (passwordPolicy == null) {
passwordPolicy = new PasswordPolicy(realm.getPasswordPolicy());
@@ -804,14 +782,23 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
@Override
- public Map<String, String> getSocialConfig() {
- return realm.getSocialConfig();
+ public List<IdentityProviderModel> getIdentityProviders() {
+ return null;
}
@Override
- public void setSocialConfig(Map<String, String> socialConfig) {
- realm.setSocialConfig(socialConfig);
- updateRealm();
+ public void addIdentityProvider(IdentityProviderModel identityProvider) {
+
+ }
+
+ @Override
+ public void removeIdentityProviderById(String providerId) {
+
+ }
+
+ @Override
+ public void updateIdentityProvider(IdentityProviderModel identityProvider) {
+
}
@Override
@@ -975,6 +962,12 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
}
@Override
+ public boolean isIdentityFederationEnabled() {
+ //TODO: support identity federation storage for mongo
+ return false;
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof RealmModel)) return false;
pom.xml 3(+2 -1)
diff --git a/pom.xml b/pom.xml
index 93cfce2..afc7119 100755
--- a/pom.xml
+++ b/pom.xml
@@ -113,6 +113,7 @@
<module>federation</module>
<module>services</module>
<module>saml</module>
+ <module>broker</module>
<module>social</module>
<module>forms</module>
<module>examples</module>
@@ -702,7 +703,7 @@
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
- <version>1.0.1.Final</version>
+ <version>1.0.1.Final</version>
<configuration>
<skip>true</skip>
</configuration>
diff --git a/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/testrealm.json b/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/testrealm.json
index ab37d0a..e087a1b 100755
--- a/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/testrealm.json
+++ b/project-integrations/aerogear-ups/auth-server/src/main/webapp/WEB-INF/testrealm.json
@@ -6,11 +6,9 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"adminTheme": "aerogear",
"accountTheme": "aerogear",
"loginTheme": "aerogear",
- "updateProfileOnInitialSocialLogin": false,
"requiredCredentials": [ "password" ],
"users" : [
{
services/pom.xml 5(+5 -0)
diff --git a/services/pom.xml b/services/pom.xml
index 82e4b6a..d061ea8 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -81,6 +81,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-broker-core</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
diff --git a/services/src/main/java/org/keycloak/services/messages/Messages.java b/services/src/main/java/org/keycloak/services/messages/Messages.java
index bcea5ed..c2eaaaa 100755
--- a/services/src/main/java/org/keycloak/services/messages/Messages.java
+++ b/services/src/main/java/org/keycloak/services/messages/Messages.java
@@ -71,19 +71,19 @@ public class Messages {
public static final String ACTION_WARN_EMAIL = "actionEmailWarning";
- public static final String MISSING_SOCIAL_PROVIDER = "missingSocialProvider";
+ public static final String MISSING_IDENTITY_PROVIDER = "missingIdentityProvider";
- public static final String INVALID_SOCIAL_ACTION = "invalidSocialAction";
+ public static final String INVALID_FEDERATED_IDENTITY_ACTION = "invalidFederatedIdentityAction";
- public static final String SOCIAL_PROVIDER_NOT_FOUND = "socialProviderNotFound";
+ public static final String IDENTITY_PROVIDER_NOT_FOUND = "identityProviderNotFound";
- public static final String SOCIAL_LINK_NOT_ACTIVE = "socialLinkNotActive";
+ public static final String FEDERATED_IDENTITY_NOT_ACTIVE = "federatedIdentityLinkNotActive";
- public static final String SOCIAL_REMOVING_LAST_PROVIDER = "socialRemovingLastProvider";
+ public static final String FEDERATED_IDENTITY_REMOVING_LAST_PROVIDER = "federatedIdentityRemovingLastProvider";
- public static final String SOCIAL_REDIRECT_ERROR = "socialRedirectError";
+ public static final String IDENTITY_PROVIDER_REDIRECT_ERROR = "identityProviderRedirectError";
- public static final String SOCIAL_PROVIDER_REMOVED = "socialProviderRemoved";
+ public static final String IDENTITY_PROVIDER_REMOVED = "identityProviderRemoved";
public static final String ERROR = "error";
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index f08df6f..f3f6e9a 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -37,14 +37,16 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelReadOnlyException;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.protocol.oidc.OpenIDConnect;
@@ -64,14 +66,10 @@ import org.keycloak.services.resources.flows.Urls;
import org.keycloak.services.util.CookieHelper;
import org.keycloak.services.util.ResolveRelative;
import org.keycloak.services.validation.Validation;
-import org.keycloak.social.SocialLoader;
-import org.keycloak.social.SocialProvider;
-import org.keycloak.social.SocialProviderException;
import org.keycloak.util.UriUtils;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
-import javax.ws.rs.HttpMethod;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
@@ -111,7 +109,7 @@ public class AccountService {
}
}
- private static final EventType[] LOG_EVENTS = {EventType.LOGIN, EventType.LOGOUT, EventType.REGISTER, EventType.REMOVE_SOCIAL_LINK, EventType.REMOVE_TOTP, EventType.SEND_RESET_PASSWORD,
+ private static final EventType[] LOG_EVENTS = {EventType.LOGIN, EventType.LOGOUT, EventType.REGISTER, EventType.REMOVE_FEDERATED_IDENTITY, EventType.REMOVE_TOTP, EventType.SEND_RESET_PASSWORD,
EventType.SEND_VERIFY_EMAIL, EventType.SOCIAL_LINK, EventType.UPDATE_EMAIL, EventType.UPDATE_PASSWORD, EventType.UPDATE_PROFILE, EventType.UPDATE_TOTP, EventType.VERIFY_EMAIL};
private static final Set<String> LOG_DETAILS = new HashSet<String>();
@@ -227,7 +225,7 @@ public class AccountService {
boolean eventsEnabled = eventStore != null && realm.isEventsEnabled();
// todo find out from federation if password is updatable
- account.setFeatures(realm.isSocial(), eventsEnabled, true);
+ account.setFeatures(realm.isIdentityFederationEnabled(), eventsEnabled, true);
}
public static UriBuilder accountServiceBaseUrl(UriInfo uriInfo) {
@@ -325,15 +323,10 @@ public class AccountService {
return forwardToPage("password", AccountPages.PASSWORD);
}
-
- public static UriBuilder socialUrl(UriBuilder base) {
- return RealmsResource.accountUrl(base).path(AccountService.class, "socialPage");
- }
-
- @Path("social")
+ @Path("identity")
@GET
- public Response socialPage() {
- return forwardToPage("social", AccountPages.SOCIAL);
+ public Response federatedIdentityPage() {
+ return forwardToPage("identity", AccountPages.FEDERATED_IDENTITY);
}
@Path("log")
@@ -639,13 +632,13 @@ public class AccountService {
return account.setPasswordSet(true).setSuccess("accountPasswordUpdated").createResponse(AccountPages.PASSWORD);
}
- @Path("social-update")
+ @Path("federated-identity-update")
@GET
- public Response processSocialUpdate(@QueryParam("action") String action,
- @QueryParam("provider_id") String providerId,
- @QueryParam("stateChecker") String stateChecker) {
+ public Response processFederatedIdentityUpdate(@QueryParam("action") String action,
+ @QueryParam("provider_id") String providerId,
+ @QueryParam("stateChecker") String stateChecker) {
if (auth == null) {
- return login("social");
+ return login("broker");
}
require(AccountRoles.MANAGE_ACCOUNT);
@@ -654,23 +647,30 @@ public class AccountService {
if (Validation.isEmpty(providerId)) {
setReferrerOnPage();
- return account.setError(Messages.MISSING_SOCIAL_PROVIDER).createResponse(AccountPages.SOCIAL);
+ return account.setError(Messages.MISSING_IDENTITY_PROVIDER).createResponse(AccountPages.FEDERATED_IDENTITY);
}
AccountSocialAction accountSocialAction = AccountSocialAction.getAction(action);
if (accountSocialAction == null) {
setReferrerOnPage();
- return account.setError(Messages.INVALID_SOCIAL_ACTION).createResponse(AccountPages.SOCIAL);
+ return account.setError(Messages.INVALID_FEDERATED_IDENTITY_ACTION).createResponse(AccountPages.FEDERATED_IDENTITY);
}
- SocialProvider provider = SocialLoader.load(providerId);
- if (provider == null) {
+ boolean hasProvider = false;
+
+ for (IdentityProviderModel model : realm.getIdentityProviders()) {
+ if (model.getId().equals(providerId)) {
+ hasProvider = true;
+ }
+ }
+
+ if (!hasProvider) {
setReferrerOnPage();
- return account.setError(Messages.SOCIAL_PROVIDER_NOT_FOUND).createResponse(AccountPages.SOCIAL);
+ return account.setError(Messages.IDENTITY_PROVIDER_NOT_FOUND).createResponse(AccountPages.FEDERATED_IDENTITY);
}
if (!user.isEnabled()) {
setReferrerOnPage();
- return account.setError(Messages.ACCOUNT_DISABLED).createResponse(AccountPages.SOCIAL);
+ return account.setError(Messages.ACCOUNT_DISABLED).createResponse(AccountPages.FEDERATED_IDENTITY);
}
switch (accountSocialAction) {
@@ -682,36 +682,44 @@ public class AccountService {
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
clientSession.setRedirectUri(redirectUri);
clientSession.setNote(OpenIDConnect.STATE_PARAM, UUID.randomUUID().toString());
+ clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret());
ClientSessionCode clientSessionCode = new ClientSessionCode(realm, clientSession);
- return Flows.social(realm, uriInfo, clientConnection, provider)
- .redirectToSocialProvider(clientSessionCode);
- } catch (SocialProviderException spe) {
+
+ URI url = UriBuilder.fromUri(this.uriInfo.getBaseUri())
+ .path(AuthenticationBrokerResource.class)
+ .path(AuthenticationBrokerResource.class, "performLogin")
+ .queryParam("provider_id", providerId)
+ .queryParam("code", clientSessionCode.getCode())
+ .build(this.realm.getName());
+
+ return Response.temporaryRedirect(url).build();
+ } catch (Exception spe) {
setReferrerOnPage();
- return account.setError(Messages.SOCIAL_REDIRECT_ERROR).createResponse(AccountPages.SOCIAL);
+ return account.setError(Messages.IDENTITY_PROVIDER_REDIRECT_ERROR).createResponse(AccountPages.FEDERATED_IDENTITY);
}
case REMOVE:
- SocialLinkModel link = session.users().getSocialLink(user, providerId, realm);
+ FederatedIdentityModel link = session.users().getFederatedIdentity(user, providerId, realm);
if (link != null) {
// Removing last social provider is not possible if you don't have other possibility to authenticate
- if (session.users().getSocialLinks(user, realm).size() > 1 || user.getFederationLink() != null || isPasswordSet(user)) {
- session.users().removeSocialLink(realm, user, providerId);
+ if (session.users().getFederatedIdentities(user, realm).size() > 1 || user.getFederationLink() != null || isPasswordSet(user)) {
+ session.users().removeFederatedIdentity(realm, user, providerId);
logger.debugv("Social provider {0} removed successfully from user {1}", providerId, user.getUsername());
- event.event(EventType.REMOVE_SOCIAL_LINK).client(auth.getClient()).user(auth.getUser())
- .detail(Details.USERNAME, link.getSocialUserId() + "@" + link.getSocialProvider())
+ event.event(EventType.REMOVE_FEDERATED_IDENTITY).client(auth.getClient()).user(auth.getUser())
+ .detail(Details.USERNAME, link.getUserId() + "@" + link.getIdentityProvider())
.success();
setReferrerOnPage();
- return account.setSuccess(Messages.SOCIAL_PROVIDER_REMOVED).createResponse(AccountPages.SOCIAL);
+ return account.setSuccess(Messages.IDENTITY_PROVIDER_REMOVED).createResponse(AccountPages.FEDERATED_IDENTITY);
} else {
setReferrerOnPage();
- return account.setError(Messages.SOCIAL_REMOVING_LAST_PROVIDER).createResponse(AccountPages.SOCIAL);
+ return account.setError(Messages.FEDERATED_IDENTITY_REMOVING_LAST_PROVIDER).createResponse(AccountPages.FEDERATED_IDENTITY);
}
} else {
setReferrerOnPage();
- return account.setError(Messages.SOCIAL_LINK_NOT_ACTIVE).createResponse(AccountPages.SOCIAL);
+ return account.setError(Messages.FEDERATED_IDENTITY_NOT_ACTIVE).createResponse(AccountPages.FEDERATED_IDENTITY);
}
default:
throw new IllegalArgumentException();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
new file mode 100755
index 0000000..fc0e6ca
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/admin/IdentityProviderResource.java
@@ -0,0 +1,145 @@
+package org.keycloak.services.resources.admin;
+
+import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.plugins.providers.multipart.InputPart;
+import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
+import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.social.SocialIdentityProvider;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
+
+/**
+ * @author Pedro Igor
+ */
+public class IdentityProviderResource {
+
+ private final RealmModel realm;
+ private final KeycloakSession session;
+
+ public IdentityProviderResource(RealmModel realm, KeycloakSession session) {
+ this.realm = realm;
+ this.session = session;
+ }
+
+ @GET
+ @NoCache
+ @Produces("application/json")
+ public List<IdentityProviderModel> getIdentityProviders() {
+ return realm.getIdentityProviders();
+ }
+
+ @Path("/providers/{provider_id}")
+ @GET
+ @NoCache
+ @Produces("application/json")
+ public Response getIdentityProviders(@PathParam("provider_id") String providerId) {
+ IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
+
+ if (providerFactory != null) {
+ return Response.ok(providerFactory).build();
+ }
+
+ return Response.status(BAD_REQUEST).build();
+ }
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response create(@Context UriInfo uriInfo, IdentityProviderModel providerModel) {
+ realm.addIdentityProvider(providerModel);
+
+ return Response.created(uriInfo.getAbsolutePathBuilder().path(providerModel.getProviderId()).build()).build();
+ }
+
+ @POST
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ public Response createWithFile(@Context UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
+ Map<String, List<InputPart>> formDataMap = input.getFormDataMap();
+
+ String id = formDataMap.get("id").get(0).getBodyAsString();
+ String name = formDataMap.get("name").get(0).getBodyAsString();
+ String providerId = formDataMap.get("providerId").get(0).getBodyAsString();
+ String enabled = formDataMap.get("enabled").get(0).getBodyAsString();
+ String updateProfileFirstLogin = formDataMap.get("updateProfileFirstLogin").get(0).getBodyAsString();
+ InputPart file = formDataMap.get("file").get(0);
+ InputStream inputStream = file.getBody(InputStream.class, null);
+ IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
+ Map config = providerFactory.parseConfig(inputStream);
+ IdentityProviderModel providerModel = new IdentityProviderModel();
+
+ providerModel.setId(id);
+ providerModel.setName(name);
+ providerModel.setProviderId(providerId);
+ providerModel.setEnabled(Boolean.valueOf(enabled));
+ providerModel.setUpdateProfileFirstLogin(Boolean.valueOf(updateProfileFirstLogin));
+ providerModel.setConfig(config);
+
+ return create(uriInfo, providerModel);
+ }
+
+ @Path("{id}")
+ @GET
+ @NoCache
+ @Produces("application/json")
+ public Response getIdentityProvider(@PathParam("id") String providerId) {
+ for (IdentityProviderModel identityProviderModel : this.realm.getIdentityProviders()) {
+ if (identityProviderModel.getId().equals(providerId)) {
+ return Response.ok(identityProviderModel).build();
+ }
+ }
+
+ return Response.noContent().build();
+ }
+
+ @Path("{id}")
+ @DELETE
+ @NoCache
+ public Response delete(@PathParam("id") String providerId) {
+ this.realm.removeIdentityProviderById(providerId);
+ return Response.noContent().build();
+ }
+
+ @PUT
+ @Consumes("application/json")
+ public Response update(IdentityProviderModel model) {
+ this.realm.updateIdentityProvider(model);
+ return Response.noContent().build();
+ }
+
+ private IdentityProviderFactory getProviderFactorytById(String providerId) {
+ List<ProviderFactory> allProviders = new ArrayList<ProviderFactory>();
+
+ allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class));
+ allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class));
+
+ for (ProviderFactory providerFactory : allProviders) {
+ if (providerFactory.getId().equals(providerId)) {
+ return (IdentityProviderFactory) providerFactory;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index b2766bb..51a39f7 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -445,4 +445,9 @@ public class RealmAdminResource {
boolean result = new LDAPConnectionTestManager().testLDAP(action, connectionUrl, bindDn, bindCredential);
return result ? Response.noContent().build() : Flows.errors().error("LDAP test error", Response.Status.BAD_REQUEST);
}
+
+ @Path("identity-provider")
+ public IdentityProviderResource getIdentityProviderResource() {
+ return new IdentityProviderResource(realm, session);
+ }
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
index 837b56b..2ac929d 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
@@ -1,6 +1,8 @@
package org.keycloak.services.resources.admin;
import org.keycloak.Version;
+import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.broker.provider.IdentityProviderFactory;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.exportimport.ApplicationImporter;
import org.keycloak.exportimport.ApplicationImporterFactory;
@@ -8,16 +10,15 @@ import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.protocol.LoginProtocol;
-import org.keycloak.protocol.LoginProtocolFactory;
-import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
-import org.keycloak.social.SocialProvider;
-import org.keycloak.util.ProviderLoader;
+import org.keycloak.representations.idm.IdentityProviderRepresentation;
+import org.keycloak.social.SocialIdentityProvider;
import javax.ws.rs.GET;
import javax.ws.rs.core.Context;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
@@ -45,6 +46,7 @@ public class ServerInfoAdminResource {
info.version = Version.VERSION;
info.serverTime = new Date().toString();
setSocialProviders(info);
+ setIdentityProviders(info);
setThemes(info);
setEventListeners(info);
setProtocols(info);
@@ -74,11 +76,38 @@ public class ServerInfoAdminResource {
}
private void setSocialProviders(ServerInfoRepresentation info) {
- info.socialProviders = new LinkedList<String>();
- for (SocialProvider p : ProviderLoader.load(SocialProvider.class)) {
- info.socialProviders.add(p.getId());
+ info.socialProviders = new LinkedList<IdentityProviderRepresentation>();
+ List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class);
+ setIdentityProviders(providerFactories, info.socialProviders, "Social");
+ }
+
+ private void setIdentityProviders(ServerInfoRepresentation info) {
+ info.identityProviders = new LinkedList<IdentityProviderRepresentation>();
+ List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class);
+ setIdentityProviders(providerFactories, info.identityProviders, "User-defined");
+
+ providerFactories = session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class);
+ setIdentityProviders(providerFactories, info.identityProviders, "Social");
+ }
+
+ public void setIdentityProviders(List<ProviderFactory> factories, List<IdentityProviderRepresentation> providers, String groupName) {
+ for (ProviderFactory providerFactory : factories) {
+ IdentityProviderFactory factory = (IdentityProviderFactory) providerFactory;
+ IdentityProviderRepresentation rep = new IdentityProviderRepresentation();
+
+ rep.setId(factory.getId());
+ rep.setName(factory.getName());
+ rep.setGroupName(groupName);
+
+ providers.add(rep);
}
- Collections.sort(info.socialProviders);
+
+ Collections.sort(providers, new Comparator<IdentityProviderRepresentation>() {
+ @Override
+ public int compare(IdentityProviderRepresentation o1, IdentityProviderRepresentation o2) {
+ return o1.getName().compareTo(o2.getName());
+ }
+ });
}
private void setEventListeners(ServerInfoRepresentation info) {
@@ -118,7 +147,8 @@ public class ServerInfoAdminResource {
private Map<String, List<String>> themes;
- private List<String> socialProviders;
+ private List<IdentityProviderRepresentation> socialProviders;
+ public List<IdentityProviderRepresentation> identityProviders;
private List<String> protocols;
private List<Map<String, String>> applicationImporters;
@@ -141,10 +171,14 @@ public class ServerInfoAdminResource {
return themes;
}
- public List<String> getSocialProviders() {
+ public List<IdentityProviderRepresentation> getSocialProviders() {
return socialProviders;
}
+ public List<IdentityProviderRepresentation> getIdentityProviders() {
+ return this.identityProviders;
+ }
+
public List<String> getEventListeners() {
return eventListeners;
}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 810f3d7..28b443c 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -11,12 +11,13 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
+import org.keycloak.models.FederatedIdentityModel;
+import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ModelReadOnlyException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
@@ -27,9 +28,9 @@ import org.keycloak.protocol.oidc.OpenIDConnectService;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.idm.ApplicationMappingsRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.MappingsRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
-import org.keycloak.representations.idm.SocialLinkRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.idm.UserSessionRepresentation;
import org.keycloak.services.managers.ClientSessionCode;
@@ -59,7 +60,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
@@ -248,21 +248,30 @@ public class UsersResource {
* @param username
* @return
*/
- @Path("{username}/social-links")
+ @Path("{username}/federated-identity")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
- public List<SocialLinkRepresentation> getSocialLinks(final @PathParam("username") String username) {
+ public List<FederatedIdentityRepresentation> getFederatedIdentity(final @PathParam("username") String username) {
auth.requireView();
UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
- Set<SocialLinkModel> socialLinks = session.users().getSocialLinks(user, realm);
- List<SocialLinkRepresentation> result = new ArrayList<SocialLinkRepresentation>();
- for (SocialLinkModel socialLink : socialLinks) {
- SocialLinkRepresentation rep = ModelToRepresentation.toRepresentation(socialLink);
- result.add(rep);
+
+ Set<FederatedIdentityModel> identities = session.users().getFederatedIdentities(user, realm);
+ List<FederatedIdentityRepresentation> result = new ArrayList<FederatedIdentityRepresentation>();
+
+ for (FederatedIdentityModel identity : identities) {
+ for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
+ if (identityProviderModel.getProviderId().equals(identity.getIdentityProvider())) {
+ FederatedIdentityRepresentation rep = ModelToRepresentation.toRepresentation(identity);
+
+ rep.setIdentityProvider(identityProviderModel.getName());
+
+ result.add(rep);
+ }
+ }
}
return result;
}
@@ -270,18 +279,18 @@ public class UsersResource {
@Path("{username}/social-links/{provider}")
@POST
@NoCache
- public Response addSocialLink(final @PathParam("username") String username, final @PathParam("provider") String provider, SocialLinkRepresentation rep) {
+ public Response addSocialLink(final @PathParam("username") String username, final @PathParam("provider") String provider, FederatedIdentityRepresentation rep) {
auth.requireManage();
UserModel user = session.users().getUserByUsername(username, realm);
if (user == null) {
throw new NotFoundException("User not found");
}
- if (session.users().getSocialLink(user, provider, realm) != null) {
+ if (session.users().getFederatedIdentity(user, provider, realm) != null) {
return Flows.errors().exists("User is already linked with provider");
}
- SocialLinkModel socialLink = new SocialLinkModel(provider, rep.getSocialUserId(), rep.getSocialUsername());
- session.users().addSocialLink(realm, user, socialLink);
+ FederatedIdentityModel socialLink = new FederatedIdentityModel(provider, rep.getUserId(), rep.getUserName());
+ session.users().addFederatedIdentity(realm, user, socialLink);
return Response.noContent().build();
}
@@ -295,7 +304,7 @@ public class UsersResource {
if (user == null) {
throw new NotFoundException("User not found");
}
- if (!session.users().removeSocialLink(realm, user, provider)) {
+ if (!session.users().removeFederatedIdentity(realm, user, provider)) {
throw new NotFoundException("Link not found");
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java b/services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java
new file mode 100644
index 0000000..550deda
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/resources/AuthenticationBrokerResource.java
@@ -0,0 +1,356 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.services.resources;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.broker.provider.AuthenticationRequest;
+import org.keycloak.broker.provider.AuthenticationResponse;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+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.protocol.oidc.TokenManager;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.managers.EventsManager;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.resources.flows.Flows;
+import org.keycloak.social.SocialIdentityProvider;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.keycloak.models.AccountRoles.MANAGE_ACCOUNT;
+import static org.keycloak.models.ClientSessionModel.Action.AUTHENTICATE;
+import static org.keycloak.models.Constants.ACCOUNT_MANAGEMENT_APP;
+import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PROFILE;
+
+/**
+ * @author Pedro Igor
+ */
+@Path("/broker")
+public class AuthenticationBrokerResource {
+
+ @Context
+ private UriInfo uriInfo;
+
+ @Context
+ private KeycloakSession session;
+
+ @Context
+ private ClientConnection clientConnection;
+
+ @Context
+ private HttpRequest request;
+
+ @GET
+ @Path("{realm}/login")
+ public Response performLogin(@PathParam("realm") String realmName,
+ @QueryParam("provider_id") String providerId,
+ @QueryParam("code") String code) {
+ RealmManager realmManager = new RealmManager(session);
+ RealmModel realm = realmManager.getRealmByName(realmName);
+ ClientSessionCode clientCode = isValidAuthorizationCode(code, realm);
+
+ if (clientCode == null) {
+ return redirectToErrorPage(realm, "Invalid code, please login again through your application.");
+ }
+
+ EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder()
+ .event(EventType.LOGIN)
+ .detail(Details.AUTH_METHOD, "unknown_id@" + providerId);
+
+ try {
+ ClientSessionModel clientSession = clientCode.getClientSession();
+ IdentityProvider identityProvider = getIdentityProvider(realm, providerId);
+
+ if (identityProvider == null) {
+ event.error(Errors.IDENTITY_PROVIDER_NOT_FOUND);
+ return Flows.forms(session, realm, null, uriInfo).setError("Identity Provider not found").createErrorPage();
+ }
+
+ AuthenticationResponse authenticationResponse = identityProvider.handleRequest(createAuthenticationRequest(providerId, code, realm,
+ clientSession));
+ Response response = authenticationResponse.getResponse();
+
+ if (response != null) {
+ event.success();
+ return response;
+ }
+ } catch (Exception e) {
+ String message = "Could not send authentication request to identity provider";
+ event.error(message);
+ return redirectToErrorPage(realm, message);
+ }
+
+ String message = "Could not proceed with authentication request to identity provider.";
+
+ event.error(message);
+
+ return redirectToErrorPage(realm, message);
+ }
+
+ @GET
+ @Path("{realm}/{provider_id}")
+ public Response handleResponseGet(@PathParam("realm") final String realmName,
+ @PathParam("provider_id") String providerId) {
+ return handleResponse(realmName, providerId);
+ }
+
+ @POST
+ @Path("{realm}/{provider_id}")
+ public Response handleResponsePost(@PathParam("realm") final String realmName,
+ @PathParam("provider_id") String providerId) {
+ return handleResponse(realmName, providerId);
+ }
+
+ private Response handleResponse(String realmName, String providerId) {
+ RealmManager realmManager = new RealmManager(session);
+ RealmModel realm = realmManager.getRealmByName(realmName);
+
+ try {
+ IdentityProvider provider = getIdentityProvider(realm, providerId);
+
+ if (provider == null) {
+ return Flows.forms(session, realm, null, uriInfo).setError("Social provider not found").createErrorPage();
+ }
+
+ String relayState = provider.getRelayState(createAuthenticationRequest(providerId, null, realm, null));
+
+ if (relayState == null) {
+ return redirectToErrorPage(realm, "No authorization code provided.");
+ }
+
+ ClientSessionCode clientCode = isValidAuthorizationCode(relayState, realm);
+
+ if (clientCode == null) {
+ return redirectToErrorPage(realm, "Invalid authorization code, please login again through your application.");
+ }
+
+ ClientSessionModel clientSession = clientCode.getClientSession();
+
+ AuthenticationResponse authenticationResponse = provider.handleResponse(createAuthenticationRequest(providerId, null, realm, clientSession));
+ Response response = authenticationResponse.getResponse();
+
+ if (response != null) {
+ return response;
+ }
+
+ FederatedIdentity socialUser = authenticationResponse.getUser();
+
+ return performLocalAuthentication(realm, providerId, socialUser, clientCode);
+ } catch (Exception e) {
+ if (session.getTransaction().isActive()) {
+ session.getTransaction().rollback();
+ }
+
+ IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(realm, providerId);
+
+ return Flows.forms(session, realm, null, uriInfo).setError("Authentication failed. Could not authenticate against Identity Provider [" + identityProviderConfig.getName() + "].").createErrorPage();
+ } finally {
+ if (session.getTransaction().isActive()) {
+ session.getTransaction().commit();
+ }
+ }
+ }
+
+ private Response performLocalAuthentication(RealmModel realm, String providerId, FederatedIdentity socialUser, ClientSessionCode clientCode) {
+ ClientSessionModel clientSession = clientCode.getClientSession();
+ FederatedIdentityModel socialLink = new FederatedIdentityModel(providerId, socialUser.getId(),
+ socialUser.getUsername());
+ UserModel federatedUser = session.users().getUserByFederatedIdentity(socialLink, realm);
+ IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(realm, providerId);
+
+ String authMethod = socialLink.getUserId() + "@" + identityProviderConfig.getId();
+ EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder()
+ .event(EventType.LOGIN)
+ .client(clientSession.getClient())
+ .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
+ .detail(Details.AUTH_METHOD, authMethod);
+
+ event.detail(Details.USERNAME, authMethod);
+
+ // Check if federatedUser is already authenticated (this means linking social into existing federatedUser account)
+ if (clientSession.getUserSession() != null) {
+ UserModel authenticatedUser = clientSession.getUserSession().getUser();
+
+ if (federatedUser != null) {
+ String message = "The identity returned by the Identity Provider [" + identityProviderConfig.getName() + "] is already linked to other user";
+ event.error(message);
+ return redirectToErrorPage(realm, message);
+ }
+
+ if (!authenticatedUser.isEnabled()) {
+ event.error(Errors.USER_DISABLED);
+ return redirectToErrorPage(realm, "User is disabled");
+ }
+
+ if (!authenticatedUser.hasRole(realm.getApplicationByName(ACCOUNT_MANAGEMENT_APP).getRole(MANAGE_ACCOUNT))) {
+ event.error(Errors.NOT_ALLOWED);
+ return redirectToErrorPage(realm, "Insufficient permissions to link identity");
+ }
+
+ session.users().addFederatedIdentity(realm, authenticatedUser, socialLink);
+
+ event.success();
+
+ return Response.status(302).location(UriBuilder.fromUri(clientSession.getRedirectUri()).build()).build();
+ }
+
+ UserModel user = session.users().getUserByEmail(socialUser.getEmail(), realm);
+ String errorMessage = "federatedIdentityEmailExists";
+
+ if (user == null) {
+ user = session.users().getUserByUsername(socialUser.getUsername(), realm);
+ errorMessage = "federatedIdentityUsernameExists";
+ }
+
+ if (user == null) {
+ federatedUser = session.users().addUser(realm, socialUser.getUsername());
+ federatedUser.setEnabled(true);
+ federatedUser.setFirstName(socialUser.getFirstName());
+ federatedUser.setLastName(socialUser.getLastName());
+ federatedUser.setEmail(socialUser.getEmail());
+
+ session.users().addFederatedIdentity(realm, federatedUser, socialLink);
+
+ event.clone().user(federatedUser).event(EventType.REGISTER)
+ .detail(Details.REGISTER_METHOD, authMethod)
+ .detail(Details.EMAIL, federatedUser.getEmail())
+ .removeDetail("auth_method")
+ .success();
+
+ if (identityProviderConfig.isUpdateProfileFirstLogin()) {
+ federatedUser.addRequiredAction(UPDATE_PROFILE);
+ }
+ } else {
+ if (federatedUser == null) {
+ return Flows.forms(session, realm, clientSession.getClient(), uriInfo)
+ .setClientSessionCode(clientCode.getCode())
+ .setError(errorMessage)
+ .createLogin();
+ }
+ }
+
+ event.user(federatedUser);
+
+ String username = socialLink.getUserId() + "@" + identityProviderConfig.getName();
+
+ UserSessionModel userSession = session.sessions()
+ .createUserSession(realm, federatedUser, username, clientConnection.getRemoteAddr(), "broker", false);
+
+ event.session(userSession);
+
+ TokenManager.attachClientSession(userSession, clientSession);
+
+ AuthenticationManager authManager = new AuthenticationManager();
+
+ return authManager.nextActionAfterAuthentication(session, userSession, clientSession, clientConnection, request,
+ uriInfo, event);
+ }
+
+ private ClientSessionCode isValidAuthorizationCode(String code, RealmModel realm) {
+ ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, realm);
+
+ if (clientCode != null && clientCode.isValid(AUTHENTICATE)) {
+ return clientCode;
+ }
+
+ return null;
+ }
+
+ private AuthenticationRequest createAuthenticationRequest(String providerId, String code, RealmModel realm, ClientSessionModel clientSession) {
+ return new AuthenticationRequest(realm, clientSession, this.request, this.uriInfo, code, getRedirectUri(providerId, realm));
+ }
+
+ private String getRedirectUri(String providerId, RealmModel realm) {
+ return UriBuilder.fromUri(this.uriInfo.getBaseUri())
+ .path(AuthenticationBrokerResource.class)
+ .path(AuthenticationBrokerResource.class, "handleResponseGet")
+ .build(realm.getName(), providerId)
+ .toString();
+ }
+
+ private Response redirectToErrorPage(RealmModel realm, String message) {
+ return Flows.forwardToSecurityFailurePage(this.session, realm, uriInfo, message);
+ }
+
+ private IdentityProvider getIdentityProvider(RealmModel realm, String providerId) {
+ for (IdentityProviderModel model : realm.getIdentityProviders()) {
+ if (model.getId().equals(providerId)) {
+ IdentityProviderFactory providerFactory = getIdentityProviderFactory(model);
+
+ if (providerFactory == null) {
+ throw new RuntimeException("Could not find provider factory for identity provider [" + providerId + "].");
+ }
+
+ return providerFactory.create(model);
+ }
+ }
+
+ return null;
+ }
+
+ private IdentityProviderFactory getIdentityProviderFactory(IdentityProviderModel model) {
+ Map<String, IdentityProviderFactory> availableProviders = new HashMap<String, IdentityProviderFactory>();
+ List<ProviderFactory> allProviders = new ArrayList<ProviderFactory>();
+
+ allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class));
+ allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class));
+
+ for (ProviderFactory providerFactory : allProviders) {
+ availableProviders.put(providerFactory.getId(), (IdentityProviderFactory) providerFactory);
+ }
+
+ return availableProviders.get(model.getProviderId());
+ }
+
+ private IdentityProviderModel getIdentityProviderConfig(RealmModel realm, String providerId) {
+ for (IdentityProviderModel model : realm.getIdentityProviders()) {
+ if (model.getId().equals(providerId)) {
+ return model;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Flows.java b/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
index 01bdb61..8c9e4d4 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Flows.java
@@ -21,12 +21,10 @@
*/
package org.keycloak.services.resources.flows;
-import org.keycloak.ClientConnection;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.social.SocialProvider;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
@@ -43,10 +41,6 @@ public class Flows {
return session.getProvider(LoginFormsProvider.class).setRealm(realm).setUriInfo(uriInfo).setClient(client);
}
- public static SocialRedirectFlows social(RealmModel realm, UriInfo uriInfo, ClientConnection clientConnection, SocialProvider provider) {
- return new SocialRedirectFlows(realm, uriInfo, clientConnection, provider);
- }
-
public static ErrorFlows errors() {
return new ErrorFlows();
}
diff --git a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
index 2e4148e..09c4df3 100755
--- a/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
+++ b/services/src/main/java/org/keycloak/services/resources/flows/Urls.java
@@ -26,7 +26,6 @@ import org.keycloak.protocol.oidc.OpenIDConnectService;
import org.keycloak.services.resources.AccountService;
import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.services.resources.RealmsResource;
-import org.keycloak.services.resources.SocialResource;
import org.keycloak.services.resources.ThemeResource;
import javax.ws.rs.core.UriBuilder;
@@ -58,11 +57,11 @@ public class Urls {
}
public static URI accountSocialPage(URI baseUri, String realmId) {
- return accountBase(baseUri).path(AccountService.class, "socialPage").build(realmId);
+ return accountBase(baseUri).path(AccountService.class, "federatedIdentityPage").build(realmId);
}
- public static URI accountSocialUpdate(URI baseUri, String realmName) {
- return accountBase(baseUri).path(AccountService.class, "processSocialUpdate").build(realmName);
+ public static URI accountFederatedIdentityUpdate(URI baseUri, String realmName) {
+ return accountBase(baseUri).path(AccountService.class, "processFederatedIdentityUpdate").build(realmName);
}
public static URI accountTotpPage(URI baseUri, String realmId) {
@@ -161,19 +160,6 @@ public class Urls {
return requiredActionsBase(baseUri).path(LoginActionsService.class, "processConsent").build(realmId);
}
- public static UriBuilder socialBase(URI baseUri) {
- return UriBuilder.fromUri(baseUri).path(SocialResource.class);
- }
-
- public static URI socialCallback(URI baseUri) {
- return socialBase(baseUri).path(SocialResource.class, "callback").build();
- }
-
- public static URI socialRedirectToProviderAuth(URI baseUri, String realmId) {
- return socialBase(baseUri).path(SocialResource.class, "redirectToProviderAuth")
- .build(realmId);
- }
-
public static URI themeRoot(URI baseUri) {
return themeBase(baseUri).build();
}
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 89874e6..f3044ea 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -72,7 +72,7 @@ public class KeycloakApplication extends Application {
singletons.add(new ServerVersionResource());
singletons.add(new RealmsResource());
- singletons.add(new SocialResource());
+ singletons.add(new AuthenticationBrokerResource());
singletons.add(new AdminRoot());
classes.add(SkeletonKeyContextResolver.class);
classes.add(QRCodeResource.class);
social/core/pom.xml 20(+1 -19)
diff --git a/social/core/pom.xml b/social/core/pom.xml
index 4cab299..a65659e 100755
--- a/social/core/pom.xml
+++ b/social/core/pom.xml
@@ -16,28 +16,10 @@
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-core</artifactId>
+ <artifactId>keycloak-broker-core</artifactId>
<version>${project.version}</version>
- <scope>provided</scope>
</dependency>
<dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-api</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.jackson</groupId>
- <artifactId>jackson-core-asl</artifactId>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.jackson</groupId>
- <artifactId>jackson-mapper-asl</artifactId>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
diff --git a/social/core/src/main/java/org/keycloak/social/SocialIdentityProvider.java b/social/core/src/main/java/org/keycloak/social/SocialIdentityProvider.java
new file mode 100644
index 0000000..9cc31ae
--- /dev/null
+++ b/social/core/src/main/java/org/keycloak/social/SocialIdentityProvider.java
@@ -0,0 +1,27 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.social;
+
+import org.keycloak.broker.provider.IdentityProvider;
+import org.keycloak.models.IdentityProviderModel;
+
+/**
+ * @author Pedro Igor
+ */
+public interface SocialIdentityProvider<C extends IdentityProviderModel> extends IdentityProvider<C> {
+}
diff --git a/social/core/src/main/java/org/keycloak/social/SocialIdentityProviderFactory.java b/social/core/src/main/java/org/keycloak/social/SocialIdentityProviderFactory.java
new file mode 100644
index 0000000..9c7f59f
--- /dev/null
+++ b/social/core/src/main/java/org/keycloak/social/SocialIdentityProviderFactory.java
@@ -0,0 +1,26 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.social;
+
+import org.keycloak.broker.provider.IdentityProviderFactory;
+
+/**
+ * @author Pedro Igor
+ */
+public interface SocialIdentityProviderFactory<I extends SocialIdentityProvider> extends IdentityProviderFactory<I> {
+}
diff --git a/social/core/src/main/java/org/keycloak/social/SocialProviderSpi.java b/social/core/src/main/java/org/keycloak/social/SocialProviderSpi.java
new file mode 100644
index 0000000..949fea7
--- /dev/null
+++ b/social/core/src/main/java/org/keycloak/social/SocialProviderSpi.java
@@ -0,0 +1,45 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.social;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author Pedro Igor
+ */
+public class SocialProviderSpi implements Spi {
+
+ public static final String SOCIAL_SPI_NAME = "social";
+
+ @Override
+ public String getName() {
+ return SOCIAL_SPI_NAME;
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return SocialIdentityProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return SocialIdentityProviderFactory.class;
+ }
+}
diff --git a/social/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/social/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi
new file mode 100755
index 0000000..f160730
--- /dev/null
+++ b/social/core/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -0,0 +1 @@
+org.keycloak.social.SocialProviderSpi
\ No newline at end of file
social/facebook/pom.xml 4(+2 -2)
diff --git a/social/facebook/pom.xml b/social/facebook/pom.xml
index 630e4da..c4eb427 100755
--- a/social/facebook/pom.xml
+++ b/social/facebook/pom.xml
@@ -16,13 +16,13 @@
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-api</artifactId>
+ <artifactId>keycloak-social-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-core</artifactId>
+ <artifactId>keycloak-broker-oidc</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
diff --git a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
new file mode 100755
index 0000000..518b1e1
--- /dev/null
+++ b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProvider.java
@@ -0,0 +1,70 @@
+package org.keycloak.social.facebook;
+
+import org.codehaus.jackson.JsonNode;
+import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.oidc.util.SimpleHttp;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.social.SocialIdentityProvider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class FacebookIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {
+
+ private static final String ID = "facebook";
+ private static final String AUTH_URL = "https://graph.facebook.com/oauth/authorize";
+ private static final String TOKEN_URL = "https://graph.facebook.com/oauth/access_token";
+ private static final String PROFILE_URL = "https://graph.facebook.com/me";
+ private static final String DEFAULT_SCOPE = "email";
+
+ public FacebookIdentityProvider(OAuth2IdentityProviderConfig config) {
+ super(config);
+ config.setAuthorizationUrl(AUTH_URL);
+ config.setTokenUrl(TOKEN_URL);
+ config.setUserInfoUrl(PROFILE_URL);
+ config.setDefaultScope(DEFAULT_SCOPE);
+ }
+
+ @Override
+ protected FederatedIdentity getFederatedIdentity(String accessToken) {
+ try {
+ JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
+
+ String id = getJsonProperty(profile, "id");
+
+ FederatedIdentity user = new FederatedIdentity(id);
+
+ String email = getJsonProperty(profile, "email");
+
+ user.setEmail(email);
+
+ String username = getJsonProperty(profile, "username");
+
+ if (username == null) {
+ if (email != null) {
+ username = email;
+ } else {
+ username = id;
+ }
+ }
+
+ user.setUsername(username);
+
+ String firstName = getJsonProperty(profile, "first_name");
+ String lastName = getJsonProperty(profile, "last_name");
+
+ if (lastName == null) {
+ lastName = "";
+ } else {
+ lastName = " " + lastName;
+ }
+
+ user.setName(firstName + lastName);
+
+ return user;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProviderFactory.java b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProviderFactory.java
new file mode 100644
index 0000000..c5bdad3
--- /dev/null
+++ b/social/facebook/src/main/java/org/keycloak/social/facebook/FacebookIdentityProviderFactory.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.social.facebook;
+
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.social.SocialIdentityProviderFactory;
+
+/**
+ * @author Pedro Igor
+ */
+public class FacebookIdentityProviderFactory extends AbstractIdentityProviderFactory<FacebookIdentityProvider> implements SocialIdentityProviderFactory<FacebookIdentityProvider> {
+
+ @Override
+ public String getName() {
+ return "Facebook";
+ }
+
+ @Override
+ public FacebookIdentityProvider create(IdentityProviderModel model) {
+ return new FacebookIdentityProvider(new OAuth2IdentityProviderConfig(getId(), model.getId(), model.getName(), model.getConfig()));
+ }
+
+ @Override
+ public String getId() {
+ return "facebook";
+ }
+}
diff --git a/social/facebook/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory b/social/facebook/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
new file mode 100644
index 0000000..cf0f1d7
--- /dev/null
+++ b/social/facebook/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
@@ -0,0 +1 @@
+org.keycloak.social.facebook.FacebookIdentityProviderFactory
\ No newline at end of file
social/github/pom.xml 9(+7 -2)
diff --git a/social/github/pom.xml b/social/github/pom.xml
index 3e049cc..d439d3e 100755
--- a/social/github/pom.xml
+++ b/social/github/pom.xml
@@ -16,13 +16,13 @@
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-api</artifactId>
+ <artifactId>keycloak-social-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-core</artifactId>
+ <artifactId>keycloak-broker-oidc</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
@@ -31,5 +31,10 @@
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
new file mode 100755
index 0000000..b48b825
--- /dev/null
+++ b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProvider.java
@@ -0,0 +1,45 @@
+package org.keycloak.social.github;
+
+import org.codehaus.jackson.JsonNode;
+import org.keycloak.broker.oidc.AbstractOAuth2IdentityProvider;
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.oidc.util.SimpleHttp;
+import org.keycloak.broker.provider.FederatedIdentity;
+import org.keycloak.social.SocialIdentityProvider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class GitHubIdentityProvider extends AbstractOAuth2IdentityProvider implements SocialIdentityProvider {
+
+ private static final String ID = "github";
+ private static final String AUTH_URL = "https://github.com/login/oauth/authorize";
+ private static final String TOKEN_URL = "https://github.com/login/oauth/access_token";
+ private static final String PROFILE_URL = "https://api.github.com/user";
+ private static final String DEFAULT_SCOPE = "user:email";
+
+ public GitHubIdentityProvider(OAuth2IdentityProviderConfig config) {
+ super(config);
+ config.setAuthorizationUrl(AUTH_URL);
+ config.setTokenUrl(TOKEN_URL);
+ config.setUserInfoUrl(PROFILE_URL);
+ config.setDefaultScope(DEFAULT_SCOPE);
+ }
+
+ @Override
+ protected FederatedIdentity getFederatedIdentity(String accessToken) {
+ try {
+ JsonNode profile = SimpleHttp.doGet(PROFILE_URL).header("Authorization", "Bearer " + accessToken).asJson();
+
+ FederatedIdentity user = new FederatedIdentity(getJsonProperty(profile, "id"));
+
+ user.setUsername(getJsonProperty(profile, "login"));
+ user.setName(getJsonProperty(profile, "name"));
+ user.setEmail(getJsonProperty(profile, "email"));
+
+ return user;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProviderFactory.java b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProviderFactory.java
new file mode 100644
index 0000000..43cea68
--- /dev/null
+++ b/social/github/src/main/java/org/keycloak/social/github/GitHubIdentityProviderFactory.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.social.github;
+
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.social.SocialIdentityProviderFactory;
+
+/**
+ * @author Pedro Igor
+ */
+public class GitHubIdentityProviderFactory extends AbstractIdentityProviderFactory<GitHubIdentityProvider> implements SocialIdentityProviderFactory<GitHubIdentityProvider> {
+
+ @Override
+ public String getName() {
+ return "GitHub";
+ }
+
+ @Override
+ public GitHubIdentityProvider create(IdentityProviderModel model) {
+ return new GitHubIdentityProvider(new OAuth2IdentityProviderConfig(getId(), model.getId(), model.getName(), model.getConfig()));
+ }
+
+ @Override
+ public String getId() {
+ return "github";
+ }
+}
diff --git a/social/github/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory b/social/github/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
new file mode 100644
index 0000000..2afe00a
--- /dev/null
+++ b/social/github/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
@@ -0,0 +1 @@
+org.keycloak.social.github.GitHubIdentityProviderFactory
\ No newline at end of file
social/google/pom.xml 9(+7 -2)
diff --git a/social/google/pom.xml b/social/google/pom.xml
index af32b13..d36bbb9 100755
--- a/social/google/pom.xml
+++ b/social/google/pom.xml
@@ -16,13 +16,13 @@
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-api</artifactId>
+ <artifactId>keycloak-social-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-core</artifactId>
+ <artifactId>keycloak-broker-oidc</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
@@ -31,5 +31,10 @@
<artifactId>jackson-mapper-asl</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/social/google/src/main/java/org/keycloak/social/google/GoogleIdentityProviderFactory.java b/social/google/src/main/java/org/keycloak/social/google/GoogleIdentityProviderFactory.java
new file mode 100644
index 0000000..df5d45e
--- /dev/null
+++ b/social/google/src/main/java/org/keycloak/social/google/GoogleIdentityProviderFactory.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.social.google;
+
+import org.keycloak.broker.oidc.OIDCIdentityProviderConfig;
+import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.social.SocialIdentityProviderFactory;
+
+/**
+ * @author Pedro Igor
+ */
+public class GoogleIdentityProviderFactory extends AbstractIdentityProviderFactory<GoogleIdentityProvider> implements SocialIdentityProviderFactory<GoogleIdentityProvider> {
+
+ @Override
+ public String getName() {
+ return "Google";
+ }
+
+ @Override
+ public GoogleIdentityProvider create(IdentityProviderModel model) {
+ return new GoogleIdentityProvider(new OIDCIdentityProviderConfig(getId(), model.getId(), model.getName(), model.getConfig()));
+ }
+
+ @Override
+ public String getId() {
+ return "google";
+ }
+}
diff --git a/social/google/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory b/social/google/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
new file mode 100644
index 0000000..ab9aaab
--- /dev/null
+++ b/social/google/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
@@ -0,0 +1 @@
+org.keycloak.social.google.GoogleIdentityProviderFactory
\ No newline at end of file
social/twitter/pom.xml 14(+12 -2)
diff --git a/social/twitter/pom.xml b/social/twitter/pom.xml
index 03eac1e..15ff779 100755
--- a/social/twitter/pom.xml
+++ b/social/twitter/pom.xml
@@ -16,17 +16,27 @@
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-model-api</artifactId>
+ <artifactId>keycloak-social-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-social-core</artifactId>
+ <artifactId>keycloak-broker-oidc</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.twitter4j</groupId>
<artifactId>twitter4j-core</artifactId>
<scope>provided</scope>
diff --git a/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProviderFactory.java b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProviderFactory.java
new file mode 100644
index 0000000..382ecf9
--- /dev/null
+++ b/social/twitter/src/main/java/org/keycloak/social/twitter/TwitterIdentityProviderFactory.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.social.twitter;
+
+import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
+import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
+import org.keycloak.models.IdentityProviderModel;
+import org.keycloak.social.SocialIdentityProviderFactory;
+
+/**
+ * @author Pedro Igor
+ */
+public class TwitterIdentityProviderFactory extends AbstractIdentityProviderFactory<TwitterIdentityProvider> implements SocialIdentityProviderFactory<TwitterIdentityProvider> {
+
+ @Override
+ public String getName() {
+ return "Twitter";
+ }
+
+ @Override
+ public TwitterIdentityProvider create(IdentityProviderModel model) {
+ return new TwitterIdentityProvider(new OAuth2IdentityProviderConfig(getId(), model.getId(), model.getName(), model.getConfig()));
+ }
+
+ @Override
+ public String getId() {
+ return "twitter";
+ }
+}
diff --git a/social/twitter/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory b/social/twitter/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
new file mode 100644
index 0000000..33b860f
--- /dev/null
+++ b/social/twitter/src/main/resources/META-INF/services/org.keycloak.social.SocialIdentityProviderFactory
@@ -0,0 +1 @@
+org.keycloak.social.twitter.TwitterIdentityProviderFactory
\ No newline at end of file
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
index b118f5c..2d18dac 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
@@ -228,7 +228,6 @@ public class AdminAPITest {
Assert.assertEquals(rep.getRealm(), storedRealm.getRealm());
}
if (rep.isEnabled() != null) Assert.assertEquals(rep.isEnabled(), storedRealm.isEnabled());
- if (rep.isSocial() != null) Assert.assertEquals(rep.isSocial(), storedRealm.isSocial());
if (rep.isBruteForceProtected() != null) Assert.assertEquals(rep.isBruteForceProtected(), storedRealm.isBruteForceProtected());
if (rep.getMaxFailureWaitSeconds() != null) Assert.assertEquals(rep.getMaxFailureWaitSeconds(), storedRealm.getMaxFailureWaitSeconds());
if (rep.getMinimumQuickLoginWaitSeconds() != null) Assert.assertEquals(rep.getMinimumQuickLoginWaitSeconds(), storedRealm.getMinimumQuickLoginWaitSeconds());
@@ -241,8 +240,6 @@ public class AdminAPITest {
if (rep.isRememberMe() != null) Assert.assertEquals(rep.isRememberMe(), storedRealm.isRememberMe());
if (rep.isVerifyEmail() != null) Assert.assertEquals(rep.isVerifyEmail(), storedRealm.isVerifyEmail());
if (rep.isResetPasswordAllowed() != null) Assert.assertEquals(rep.isResetPasswordAllowed(), storedRealm.isResetPasswordAllowed());
- if (rep.isUpdateProfileOnInitialSocialLogin() != null)
- Assert.assertEquals(rep.isUpdateProfileOnInitialSocialLogin(), storedRealm.isUpdateProfileOnInitialSocialLogin());
if (rep.getSslRequired() != null) Assert.assertEquals(rep.getSslRequired(), storedRealm.getSslRequired());
if (rep.getAccessCodeLifespan() != null) Assert.assertEquals(rep.getAccessCodeLifespan(), storedRealm.getAccessCodeLifespan());
if (rep.getAccessCodeLifespanUserAction() != null)
@@ -275,9 +272,6 @@ public class AdminAPITest {
Assert.assertEquals(rep.getSmtpServer(), storedRealm.getSmtpServer());
}
- if (rep.getSocialProviders() != null) {
- Assert.assertEquals(rep.getSocialProviders(), storedRealm.getSocialProviders());
- }
if (rep.getBrowserSecurityHeaders() != null) {
Assert.assertEquals(rep.getBrowserSecurityHeaders(), storedRealm.getBrowserSecurityHeaders());
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
index 4ae65dd..b6710ee 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/UserTest.java
@@ -1,16 +1,19 @@
package org.keycloak.testsuite.admin;
+import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.admin.client.resource.UserResource;
-import org.keycloak.models.UserModel;
-import org.keycloak.representations.idm.SocialLinkRepresentation;
+import org.keycloak.representations.idm.FederatedIdentityRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import javax.ws.rs.ClientErrorException;
import javax.ws.rs.core.Response;
import java.util.List;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -116,28 +119,30 @@ public class UserTest extends AbstractClientTest {
UserResource user = realm.users().get("user1");
- SocialLinkRepresentation link = new SocialLinkRepresentation();
- link.setSocialUserId("social-user-id");
- link.setSocialUsername("social-username");
+ FederatedIdentityRepresentation link = new FederatedIdentityRepresentation();
+ link.setUserId("social-user-id");
+ link.setUserName("social-username");
Response response = user.addSocialLink("social-provider-id", link);
assertEquals(204, response.getStatus());
}
@Test
+ @Ignore("Refactor based on KEYCLOAK-883")
public void getSocialLinks() {
addSocialLink();
UserResource user = realm.users().get("user1");
assertEquals(1, user.getSocialLinks().size());
- SocialLinkRepresentation link = user.getSocialLinks().get(0);
- assertEquals("social-provider-id", link.getSocialProvider());
- assertEquals("social-user-id", link.getSocialUserId());
- assertEquals("social-username", link.getSocialUsername());
+ FederatedIdentityRepresentation link = user.getSocialLinks().get(0);
+ assertEquals("social-provider-id", link.getIdentityProvider());
+ assertEquals("social-user-id", link.getUserId());
+ assertEquals("social-username", link.getUserName());
}
@Test
+ @Ignore("Refactor based on KEYCLOAK-883")
public void removeSocialLink() {
addSocialLink();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
index 5261da6..8e3ba99 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
@@ -6,7 +6,6 @@ import org.junit.Test;
import org.junit.rules.ExternalResource;
import org.junit.rules.RuleChain;
import org.junit.rules.TestRule;
-import org.keycloak.Config;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.dir.DirExportProvider;
import org.keycloak.exportimport.dir.DirExportProviderFactory;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
index f357c8c..ec52678 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
@@ -5,13 +5,13 @@ import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
@@ -47,7 +47,6 @@ public class AdapterTest extends AbstractModelTest {
realmModel.setPrivateKeyPem("0234234");
realmModel.setPublicKeyPem("0234234");
realmModel.setAccessTokenLifespan(1000);
- realmModel.setUpdateProfileOnInitialSocialLogin(true);
realmModel.addDefaultRole("foo");
realmModel = realmManager.getRealm(realmModel.getId());
@@ -59,7 +58,6 @@ public class AdapterTest extends AbstractModelTest {
Assert.assertEquals(realmModel.getName(), "JUGGLER");
Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234");
Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234");
- Assert.assertEquals(realmModel.isUpdateProfileOnInitialSocialLogin(), true);
Assert.assertEquals(1, realmModel.getDefaultRoles().size());
Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0));
}
@@ -74,7 +72,6 @@ public class AdapterTest extends AbstractModelTest {
realmModel.setPrivateKeyPem("0234234");
realmModel.setPublicKeyPem("0234234");
realmModel.setAccessTokenLifespan(1000);
- realmModel.setUpdateProfileOnInitialSocialLogin(true);
realmModel.addDefaultRole("foo");
realmModel = realmManager.getRealm(realmModel.getId());
@@ -86,7 +83,6 @@ public class AdapterTest extends AbstractModelTest {
Assert.assertEquals(realmModel.getName(), "JUGGLER");
Assert.assertEquals(realmModel.getPrivateKeyPem(), "0234234");
Assert.assertEquals(realmModel.getPublicKeyPem(), "0234234");
- Assert.assertEquals(realmModel.isUpdateProfileOnInitialSocialLogin(), true);
Assert.assertEquals(1, realmModel.getDefaultRoles().size());
Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0));
@@ -170,8 +166,8 @@ public class AdapterTest extends AbstractModelTest {
RoleModel appRole = app.addRole("test");
user.grantRole(appRole);
- SocialLinkModel socialLink = new SocialLinkModel("google", "google1", user.getUsername());
- realmManager.getSession().users().addSocialLink(realmModel, user, socialLink);
+ FederatedIdentityModel socialLink = new FederatedIdentityModel("google", "google1", user.getUsername());
+ realmManager.getSession().users().addFederatedIdentity(realmModel, user, socialLink);
UserCredentialModel cred = new UserCredentialModel();
cred.setType(CredentialRepresentation.PASSWORD);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
index 97c433e..74f3ff9 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
@@ -7,11 +7,11 @@ import org.junit.runners.MethodSorters;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel;
@@ -62,7 +62,6 @@ public class ImportTest extends AbstractModelTest {
public static void assertDataImportedInRealm(KeycloakSession session, RealmModel realm) {
Assert.assertTrue(realm.isVerifyEmail());
- Assert.assertFalse(realm.isUpdateProfileOnInitialSocialLogin());
List<RequiredCredentialModel> creds = realm.getRequiredCredentials();
Assert.assertEquals(1, creds.size());
RequiredCredentialModel cred = creds.get(0);
@@ -74,7 +73,7 @@ public class ImportTest extends AbstractModelTest {
UserModel user = session.users().getUserByUsername("loginclient", realm);
Assert.assertNotNull(user);
- Assert.assertEquals(0, session.users().getSocialLinks(user, realm).size());
+ Assert.assertEquals(0, session.users().getFederatedIdentities(user, realm).size());
List<ApplicationModel> resources = realm.getApplications();
for (ApplicationModel app : resources) {
@@ -154,42 +153,42 @@ public class ImportTest extends AbstractModelTest {
// Test social linking
UserModel socialUser = session.users().getUserByUsername("mySocialUser", realm);
- Set<SocialLinkModel> socialLinks = session.users().getSocialLinks(socialUser, realm);
+ Set<FederatedIdentityModel> socialLinks = session.users().getFederatedIdentities(socialUser, realm);
Assert.assertEquals(3, socialLinks.size());
boolean facebookFound = false;
boolean googleFound = false;
boolean twitterFound = false;
- for (SocialLinkModel socialLinkModel : socialLinks) {
- if ("facebook".equals(socialLinkModel.getSocialProvider())) {
+ for (FederatedIdentityModel federatedIdentityModel : socialLinks) {
+ if ("facebook".equals(federatedIdentityModel.getIdentityProvider())) {
facebookFound = true;
- Assert.assertEquals(socialLinkModel.getSocialUserId(), "facebook1");
- Assert.assertEquals(socialLinkModel.getSocialUsername(), "fbuser1");
- } else if ("google".equals(socialLinkModel.getSocialProvider())) {
+ Assert.assertEquals(federatedIdentityModel.getUserId(), "facebook1");
+ Assert.assertEquals(federatedIdentityModel.getUserName(), "fbuser1");
+ } else if ("google".equals(federatedIdentityModel.getIdentityProvider())) {
googleFound = true;
- Assert.assertEquals(socialLinkModel.getSocialUserId(), "google1");
- Assert.assertEquals(socialLinkModel.getSocialUsername(), "mySocialUser@gmail.com");
- } else if ("twitter".equals(socialLinkModel.getSocialProvider())) {
+ Assert.assertEquals(federatedIdentityModel.getUserId(), "google1");
+ Assert.assertEquals(federatedIdentityModel.getUserName(), "mySocialUser@gmail.com");
+ } else if ("twitter".equals(federatedIdentityModel.getIdentityProvider())) {
twitterFound = true;
- Assert.assertEquals(socialLinkModel.getSocialUserId(), "twitter1");
- Assert.assertEquals(socialLinkModel.getSocialUsername(), "twuser1");
+ Assert.assertEquals(federatedIdentityModel.getUserId(), "twitter1");
+ Assert.assertEquals(federatedIdentityModel.getUserName(), "twuser1");
}
}
Assert.assertTrue(facebookFound && twitterFound && googleFound);
- UserModel foundSocialUser = session.users().getUserBySocialLink(new SocialLinkModel("facebook", "facebook1", "fbuser1"), realm);
+ UserModel foundSocialUser = session.users().getUserByFederatedIdentity(new FederatedIdentityModel("facebook", "facebook1", "fbuser1"), realm);
Assert.assertEquals(foundSocialUser.getUsername(), socialUser.getUsername());
- Assert.assertNull(session.users().getUserBySocialLink(new SocialLinkModel("facebook", "not-existing", "not-existing"), realm));
+ Assert.assertNull(session.users().getUserByFederatedIdentity(new FederatedIdentityModel("facebook", "not-existing", "not-existing"), realm));
- SocialLinkModel foundSocialLink = session.users().getSocialLink(socialUser, "facebook", realm);
- Assert.assertEquals("facebook1", foundSocialLink.getSocialUserId());
- Assert.assertEquals("fbuser1", foundSocialLink.getSocialUsername());
- Assert.assertEquals("facebook", foundSocialLink.getSocialProvider());
+ FederatedIdentityModel foundSocialLink = session.users().getFederatedIdentity(socialUser, "facebook", realm);
+ Assert.assertEquals("facebook1", foundSocialLink.getUserId());
+ Assert.assertEquals("fbuser1", foundSocialLink.getUserName());
+ Assert.assertEquals("facebook", foundSocialLink.getIdentityProvider());
// Test removing social link
- Assert.assertTrue(session.users().removeSocialLink(realm, socialUser, "facebook"));
- Assert.assertNull(session.users().getSocialLink(socialUser, "facebook", realm));
- Assert.assertFalse(session.users().removeSocialLink(realm, socialUser, "facebook"));
- session.users().addSocialLink(realm, socialUser, new SocialLinkModel("facebook", "facebook1", "fbuser1"));
+ Assert.assertTrue(session.users().removeFederatedIdentity(realm, socialUser, "facebook"));
+ Assert.assertNull(session.users().getFederatedIdentity(socialUser, "facebook", realm));
+ Assert.assertFalse(session.users().removeFederatedIdentity(realm, socialUser, "facebook"));
+ session.users().addFederatedIdentity(realm, socialUser, new FederatedIdentityModel("facebook", "facebook1", "fbuser1"));
// Test smtp config
Map<String, String> smtpConfig = realm.getSmtpConfig();
@@ -199,10 +198,11 @@ public class ImportTest extends AbstractModelTest {
Assert.assertEquals("3025", smtpConfig.get("port"));
// Test social config
- Map<String, String> socialConfig = realm.getSocialConfig();
- Assert.assertTrue(socialConfig.size() == 2);
- Assert.assertEquals("abc", socialConfig.get("google.key"));
- Assert.assertEquals("def", socialConfig.get("google.secret"));
+ //FIXME: KEYCLOAK-883
+// Map<String, String> socialConfig = realm.getSocialConfig();
+// Assert.assertTrue(socialConfig.size() == 2);
+// Assert.assertEquals("abc", socialConfig.get("google.key"));
+// Assert.assertEquals("def", socialConfig.get("google.secret"));
// Test federation providers
List<UserFederationProviderModel> fedProviders = realm.getUserFederationProviders();
@@ -225,7 +225,6 @@ public class ImportTest extends AbstractModelTest {
rep.setId("demo");
RealmModel realm =manager.importRealm(rep);
- Assert.assertFalse(realm.isUpdateProfileOnInitialSocialLogin());
Assert.assertEquals(600, realm.getAccessCodeLifespanUserAction());
verifyRequiredCredentials(realm.getRequiredCredentials(), "password");
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ModelTest.java
index eb64196..30807a4 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ModelTest.java
@@ -18,7 +18,6 @@ public class ModelTest extends AbstractModelTest {
RealmModel realm = realmManager.createRealm("original");
realm.setRegistrationAllowed(true);
realm.setResetPasswordAllowed(true);
- realm.setSocial(true);
realm.setSslRequired(SslRequired.EXTERNAL);
realm.setVerifyEmail(true);
realm.setAccessTokenLifespan(1000);
@@ -36,7 +35,8 @@ public class ModelTest extends AbstractModelTest {
HashMap<String, String> social = new HashMap<String,String>();
social.put("google.key", "1234");
social.put("google.secret", "5678");
- realm.setSocialConfig(social);
+ //FIXME: KEYCLOAK-883
+// realm.setSocialConfig(social);
RealmModel persisted = realmManager.getRealm(realm.getId());
assertEquals(realm, persisted);
@@ -46,11 +46,8 @@ public class ModelTest extends AbstractModelTest {
}
public static void assertEquals(RealmModel expected, RealmModel actual) {
- Assert.assertEquals(expected.isUpdateProfileOnInitialSocialLogin(),
- actual.isUpdateProfileOnInitialSocialLogin());
Assert.assertEquals(expected.isRegistrationAllowed(), actual.isRegistrationAllowed());
Assert.assertEquals(expected.isResetPasswordAllowed(), actual.isResetPasswordAllowed());
- Assert.assertEquals(expected.isSocial(), actual.isSocial());
Assert.assertEquals(expected.getSslRequired(), actual.getSslRequired());
Assert.assertEquals(expected.isVerifyEmail(), actual.isVerifyEmail());
Assert.assertEquals(expected.getAccessTokenLifespan(), actual.getAccessTokenLifespan());
@@ -63,7 +60,8 @@ public class ModelTest extends AbstractModelTest {
Assert.assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles());
Assert.assertEquals(expected.getSmtpConfig(), actual.getSmtpConfig());
- Assert.assertEquals(expected.getSocialConfig(), actual.getSocialConfig());
+ //FIXME: KEYCLOAK-883
+// Assert.assertEquals(expected.getSocialConfig(), actual.getSocialConfig());
}
private RealmModel importExport(RealmModel src, String copyName) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
index 658512e..830f05f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/social/SocialLoginTest.java
@@ -24,6 +24,7 @@ package org.keycloak.testsuite.social;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.OAuth2Constants;
@@ -54,19 +55,16 @@ import java.util.HashMap;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
+@Ignore("Refactor based on KEYCLOAK-883")
public class SocialLoginTest {
@ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
- appRealm.setSocial(true);
- appRealm.setUpdateProfileOnInitialSocialLogin(false);
-
HashMap<String, String> socialConfig = new HashMap<String, String>();
socialConfig.put("dummy.key", "1234");
socialConfig.put("dummy.secret", "1234");
- appRealm.setSocialConfig(socialConfig);
}
});
@@ -217,7 +215,6 @@ public class SocialLoginTest {
keycloakRule.configure(new KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
- appRealm.setUpdateProfileOnInitialSocialLogin(true);
}
});
@@ -271,7 +268,6 @@ public class SocialLoginTest {
keycloakRule.configure(new KeycloakSetup() {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
- appRealm.setUpdateProfileOnInitialSocialLogin(false);
}
});
}
diff --git a/testsuite/integration/src/test/resources/adapter-test/demorealm.json b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
index f4668a4..32a79c7 100755
--- a/testsuite/integration/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/integration/src/test/resources/adapter-test/demorealm-relative.json b/testsuite/integration/src/test/resources/adapter-test/demorealm-relative.json
index d920209..0c1c38a 100755
--- a/testsuite/integration/src/test/resources/adapter-test/demorealm-relative.json
+++ b/testsuite/integration/src/test/resources/adapter-test/demorealm-relative.json
@@ -6,8 +6,6 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/integration/src/test/resources/adapter-test/tenant1-realm.json b/testsuite/integration/src/test/resources/adapter-test/tenant1-realm.json
index 783776f..3f6649c 100644
--- a/testsuite/integration/src/test/resources/adapter-test/tenant1-realm.json
+++ b/testsuite/integration/src/test/resources/adapter-test/tenant1-realm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/integration/src/test/resources/adapter-test/tenant2-realm.json b/testsuite/integration/src/test/resources/adapter-test/tenant2-realm.json
index 1c17f11..1a5ccc4 100644
--- a/testsuite/integration/src/test/resources/adapter-test/tenant2-realm.json
+++ b/testsuite/integration/src/test/resources/adapter-test/tenant2-realm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/integration/src/test/resources/model/testrealm.json b/testsuite/integration/src/test/resources/model/testrealm.json
index fdb8f82..be9279a 100755
--- a/testsuite/integration/src/test/resources/model/testrealm.json
+++ b/testsuite/integration/src/test/resources/model/testrealm.json
@@ -12,10 +12,17 @@
"host": "localhost",
"port":"3025"
},
- "socialProviders": {
- "google.key": "abc",
- "google.secret": "def"
- },
+ "identityProviders" : [
+ {
+ "providerId" : "google",
+ "name" : "Google",
+ "enabled": true,
+ "config": {
+ "clientId": "clientId",
+ "clientSecret": "clientSecret"
+ }
+ }
+ ],
"userFederationProviders": [
{
"displayName": "MyLDAPProvider",
@@ -72,21 +79,21 @@
{
"username": "mySocialUser",
"enabled": true,
- "socialLinks": [
+ "federatedIdentities": [
{
- "socialProvider": "facebook",
- "socialUserId": "facebook1",
- "socialUsername": "fbuser1"
+ "identityProvider": "facebook",
+ "userId": "facebook1",
+ "userName": "fbuser1"
},
{
- "socialProvider": "twitter",
- "socialUserId": "twitter1",
- "socialUsername": "twuser1"
+ "identityProvider": "twitter",
+ "userId": "twitter1",
+ "userName": "twuser1"
},
{
- "socialProvider": "google",
- "socialUserId": "google1",
- "socialUsername": "mySocialUser@gmail.com"
+ "identityProvider": "google",
+ "userId": "google1",
+ "userName": "mySocialUser@gmail.com"
}
]
}
diff --git a/testsuite/integration/src/test/resources/model/testrealm2.json b/testsuite/integration/src/test/resources/model/testrealm2.json
index 5ce0084..85fce1e 100755
--- a/testsuite/integration/src/test/resources/model/testrealm2.json
+++ b/testsuite/integration/src/test/resources/model/testrealm2.json
@@ -6,8 +6,6 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/integration/src/test/resources/model/testrealm-demo.json b/testsuite/integration/src/test/resources/model/testrealm-demo.json
index fb307bc..f089c9d 100755
--- a/testsuite/integration/src/test/resources/model/testrealm-demo.json
+++ b/testsuite/integration/src/test/resources/model/testrealm-demo.json
@@ -5,7 +5,6 @@
"accessCodeLifespan": 10,
"accessCodeLifespanUserAction": 600,
"sslRequired": "external",
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/jetty/jetty81/src/test/resources/adapter-test/demorealm.json b/testsuite/jetty/jetty81/src/test/resources/adapter-test/demorealm.json
index 0de8bce..9db9064 100755
--- a/testsuite/jetty/jetty81/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/jetty/jetty81/src/test/resources/adapter-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/jetty/jetty81/src/test/resources/jetty-test/demorealm.json b/testsuite/jetty/jetty81/src/test/resources/jetty-test/demorealm.json
index a4a6ec9..ee13db5 100755
--- a/testsuite/jetty/jetty81/src/test/resources/jetty-test/demorealm.json
+++ b/testsuite/jetty/jetty81/src/test/resources/jetty-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/jetty/jetty91/src/test/resources/adapter-test/demorealm.json b/testsuite/jetty/jetty91/src/test/resources/adapter-test/demorealm.json
index 0de8bce..9db9064 100755
--- a/testsuite/jetty/jetty91/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/jetty/jetty91/src/test/resources/adapter-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/jetty/jetty91/src/test/resources/jetty-test/demorealm.json b/testsuite/jetty/jetty91/src/test/resources/jetty-test/demorealm.json
index a4a6ec9..ee13db5 100755
--- a/testsuite/jetty/jetty91/src/test/resources/jetty-test/demorealm.json
+++ b/testsuite/jetty/jetty91/src/test/resources/jetty-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/jetty/jetty92/src/test/resources/adapter-test/demorealm.json b/testsuite/jetty/jetty92/src/test/resources/adapter-test/demorealm.json
index 0de8bce..9db9064 100755
--- a/testsuite/jetty/jetty92/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/jetty/jetty92/src/test/resources/adapter-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java
index 48e5672..75b9af2 100755
--- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java
+++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/CreateUsersWorker.java
@@ -5,7 +5,7 @@ import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation;
@@ -85,7 +85,7 @@ public class CreateUsersWorker implements Worker {
user.updateCredential(password);
}
- // Creating some socialLinks
+ // Creating some federatedIdentities
for (int i=0 ; i<socialLinksPerUserCount ; i++) {
String socialProvider;
switch (i) {
@@ -96,8 +96,8 @@ public class CreateUsersWorker implements Worker {
+ " which is too big.");
}
- SocialLinkModel socialLink = new SocialLinkModel(socialProvider, username, username);
- session.users().addSocialLink(realm, user, socialLink);
+ FederatedIdentityModel socialLink = new FederatedIdentityModel(socialProvider, username, username);
+ session.users().addFederatedIdentity(realm, user, socialLink);
}
log.info("Finished creation of user " + username + " in realm: " + realm.getId());
diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java
index d9c36d5..3f4a23b 100755
--- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java
+++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/ReadUsersWorker.java
@@ -5,7 +5,7 @@ import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.SocialLinkModel;
+import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
@@ -105,15 +105,15 @@ public class ReadUsersWorker implements Worker {
session.users().validCredentials(realm, user, UserCredentialModel.password(username));
}
- // Read socialLinks of user
+ // Read federatedIdentities of user
if (readSocialLinks) {
- session.users().getSocialLinks(user, realm);
+ session.users().getFederatedIdentities(user, realm);
}
// Try to search by social links
if (searchBySocialLinks) {
- SocialLinkModel socialLink = new SocialLinkModel("facebook", username, username);
- session.users().getUserBySocialLink(socialLink, realm);
+ FederatedIdentityModel socialLink = new FederatedIdentityModel("facebook", username, username);
+ session.users().getUserByFederatedIdentity(socialLink, realm);
}
}
diff --git a/testsuite/performance/src/test/jmeter/system.properties b/testsuite/performance/src/test/jmeter/system.properties
index 42045ff..a445c98 100644
--- a/testsuite/performance/src/test/jmeter/system.properties
+++ b/testsuite/performance/src/test/jmeter/system.properties
@@ -67,7 +67,7 @@ keycloak.perf.createUsers.addPassword=true
keycloak.perf.createUsers.socialLinksPerUserCount=0
-## Properties for ReadUsers test. This test is used to read some users from DB and alternatively read some of his properties (passwords, roles, scopes, socialLinks)
+## Properties for ReadUsers test. This test is used to read some users from DB and alternatively read some of his properties (passwords, roles, scopes, federatedIdentities)
keycloak.perf.readUsers.realms.offset=1
# Number of read users in each iteration
keycloak.perf.readUsers.readUsersPerIteration=5
diff --git a/testsuite/proxy/src/test/resources/demorealm.json b/testsuite/proxy/src/test/resources/demorealm.json
index 40104c9..5c83e0f 100755
--- a/testsuite/proxy/src/test/resources/demorealm.json
+++ b/testsuite/proxy/src/test/resources/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json b/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json
index 0de8bce..9db9064 100755
--- a/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/tomcat6/src/test/resources/adapter-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/tomcat7/src/test/resources/adapter-test/demorealm.json b/testsuite/tomcat7/src/test/resources/adapter-test/demorealm.json
index 0de8bce..9db9064 100755
--- a/testsuite/tomcat7/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/tomcat7/src/test/resources/adapter-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/tomcat8/src/test/resources/adapter-test/demorealm.json b/testsuite/tomcat8/src/test/resources/adapter-test/demorealm.json
index 0de8bce..9db9064 100755
--- a/testsuite/tomcat8/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/tomcat8/src/test/resources/adapter-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
diff --git a/testsuite/tomcat8/src/test/resources/tomcat-test/demorealm.json b/testsuite/tomcat8/src/test/resources/tomcat-test/demorealm.json
index a4a6ec9..ee13db5 100755
--- a/testsuite/tomcat8/src/test/resources/tomcat-test/demorealm.json
+++ b/testsuite/tomcat8/src/test/resources/tomcat-test/demorealm.json
@@ -7,9 +7,7 @@
"accessCodeLifespanUserAction": 6000,
"sslRequired": "external",
"registrationAllowed": false,
- "social": false,
"passwordCredentialGrantAllowed": true,
- "updateProfileOnInitialSocialLogin": false,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],