commit
1700cb149b
80 changed files with 1332 additions and 1043 deletions
|
@ -19,6 +19,7 @@ package org.keycloak.broker.provider;
|
||||||
|
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
@ -48,4 +49,13 @@ public abstract class AbstractIdentityProvider<C extends IdentityProviderModel>
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object callback(RealmModel realm, AuthenticationCallback callback) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
* 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AuthenticationResponse fromResponse(Response response) {
|
|
||||||
return new AuthenticationResponse(response);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,16 +20,39 @@ package org.keycloak.broker.provider;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.provider.Provider;
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Pedro Igor
|
* @author Pedro Igor
|
||||||
*/
|
*/
|
||||||
public interface IdentityProvider<C extends IdentityProviderModel> extends Provider {
|
public interface IdentityProvider<C extends IdentityProviderModel> extends Provider {
|
||||||
|
|
||||||
|
public interface AuthenticationCallback {
|
||||||
|
/**
|
||||||
|
* This method should be called by provider after the JAXRS callback endpoint has finished authentication
|
||||||
|
* with the remote IDP
|
||||||
|
*
|
||||||
|
* @param userNotes notes to add to the UserSessionModel
|
||||||
|
* @param identityProviderConfig provider config
|
||||||
|
* @param federatedIdentity federated identity
|
||||||
|
* @param code relayState or state parameter used to identity the client session
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Response authenticated(Map<String, String> userNotes, IdentityProviderModel identityProviderConfig, FederatedIdentity federatedIdentity, String code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JAXRS callback endpoint for when the remote IDP wants to callback to keycloak.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Object callback(RealmModel realm, AuthenticationCallback callback);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Initiates the authentication process by sending an authentication request to an identity provider. This method is called
|
* <p>Initiates the authentication process by sending an authentication request to an identity provider. This method is called
|
||||||
* only once during the authentication.</p>
|
* only once during the authentication.</p>
|
||||||
|
@ -44,31 +67,7 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
||||||
* identity provider.
|
* identity provider.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
AuthenticationResponse handleRequest(AuthenticationRequest request);
|
Response 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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Returns a {@link javax.ws.rs.core.Response} containing the token previously stored during the authentication process for a
|
* <p>Returns a {@link javax.ws.rs.core.Response} containing the token previously stored during the authentication process for a
|
||||||
|
@ -79,6 +78,17 @@ public interface IdentityProvider<C extends IdentityProviderModel> extends Provi
|
||||||
*/
|
*/
|
||||||
Response retrieveToken(FederatedIdentityModel identity);
|
Response retrieveToken(FederatedIdentityModel identity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a Keycloak application initiates a logout through the browser. This is expected to do a logout
|
||||||
|
* with the IDP
|
||||||
|
*
|
||||||
|
* @param userSession
|
||||||
|
* @param uriInfo
|
||||||
|
* @param realm
|
||||||
|
* @return null if this is not supported by this provider
|
||||||
|
*/
|
||||||
|
Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Export a representation of the IdentityProvider in a specific format. For example, a SAML EntityDescriptor
|
* Export a representation of the IdentityProvider in a specific format. For example, a SAML EntityDescriptor
|
||||||
*
|
*
|
||||||
|
|
|
@ -30,6 +30,23 @@
|
||||||
<artifactId>jackson-mapper-asl</artifactId>
|
<artifactId>jackson-mapper-asl</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-events-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-services</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logging</groupId>
|
||||||
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
133
broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
Normal file → Executable file
133
broker/oidc/src/main/java/org/keycloak/broker/oidc/AbstractOAuth2IdentityProvider.java
Normal file → Executable file
|
@ -19,20 +19,34 @@ package org.keycloak.broker.oidc;
|
||||||
|
|
||||||
import org.codehaus.jackson.JsonNode;
|
import org.codehaus.jackson.JsonNode;
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
|
import org.jboss.resteasy.logging.Logger;
|
||||||
|
import org.keycloak.ClientConnection;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.broker.oidc.util.SimpleHttp;
|
import org.keycloak.broker.oidc.util.SimpleHttp;
|
||||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
|
||||||
import org.keycloak.broker.provider.FederatedIdentity;
|
import org.keycloak.broker.provider.FederatedIdentity;
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
|
import org.keycloak.events.Errors;
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.services.managers.EventsManager;
|
||||||
|
import org.keycloak.services.messages.Messages;
|
||||||
|
import org.keycloak.services.resources.flows.Flows;
|
||||||
|
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -40,6 +54,7 @@ import java.util.regex.Pattern;
|
||||||
* @author Pedro Igor
|
* @author Pedro Igor
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityProviderConfig> extends AbstractIdentityProvider<C> {
|
public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityProviderConfig> extends AbstractIdentityProvider<C> {
|
||||||
|
protected static final Logger logger = Logger.getLogger(AbstractOAuth2IdentityProvider.class);
|
||||||
|
|
||||||
public static final String OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code";
|
public static final String OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE = "authorization_code";
|
||||||
protected static ObjectMapper mapper = new ObjectMapper();
|
protected static ObjectMapper mapper = new ObjectMapper();
|
||||||
|
@ -54,6 +69,8 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||||
public static final String OAUTH2_PARAMETER_CLIENT_SECRET = "client_secret";
|
public static final String OAUTH2_PARAMETER_CLIENT_SECRET = "client_secret";
|
||||||
public static final String OAUTH2_PARAMETER_GRANT_TYPE = "grant_type";
|
public static final String OAUTH2_PARAMETER_GRANT_TYPE = "grant_type";
|
||||||
|
|
||||||
|
protected AuthenticationCallback callback;
|
||||||
|
|
||||||
public AbstractOAuth2IdentityProvider(C config) {
|
public AbstractOAuth2IdentityProvider(C config) {
|
||||||
super(config);
|
super(config);
|
||||||
|
|
||||||
|
@ -63,61 +80,22 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationResponse handleRequest(AuthenticationRequest request) {
|
public Object callback(RealmModel realm, AuthenticationCallback callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
return new Endpoint(realm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response handleRequest(AuthenticationRequest request) {
|
||||||
try {
|
try {
|
||||||
URI authorizationUrl = createAuthorizationUrl(request).build();
|
URI authorizationUrl = createAuthorizationUrl(request).build();
|
||||||
|
|
||||||
return AuthenticationResponse.temporaryRedirect(authorizationUrl);
|
return Response.temporaryRedirect(authorizationUrl).build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IdentityBrokerException("Could not create authentication request.", e);
|
throw new IdentityBrokerException("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 IdentityBrokerException("Access denied.");
|
|
||||||
} else {
|
|
||||||
throw new IdentityBrokerException(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();
|
|
||||||
|
|
||||||
FederatedIdentity federatedIdentity = getFederatedIdentity(response);
|
|
||||||
|
|
||||||
if (getConfig().isStoreToken()) {
|
|
||||||
federatedIdentity.setToken(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
return AuthenticationResponse.end(federatedIdentity);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IdentityBrokerException("No authorization code from identity provider.");
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IdentityBrokerException("Could not process response from identity provider.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response retrieveToken(FederatedIdentityModel identity) {
|
public Response retrieveToken(FederatedIdentityModel identity) {
|
||||||
return Response.ok(identity.getToken()).build();
|
return Response.ok(identity.getToken()).build();
|
||||||
|
@ -184,4 +162,63 @@ public abstract class AbstractOAuth2IdentityProvider<C extends OAuth2IdentityPro
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract String getDefaultScopes();
|
protected abstract String getDefaultScopes();
|
||||||
|
|
||||||
|
protected class Endpoint {
|
||||||
|
protected RealmModel realm;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected KeycloakSession session;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected ClientConnection clientConnection;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected HttpHeaders headers;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected UriInfo uriInfo;
|
||||||
|
|
||||||
|
public Endpoint(RealmModel realm) {
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public Response authResponse(@QueryParam(AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_STATE) String state,
|
||||||
|
@QueryParam(AbstractOAuth2IdentityProvider.OAUTH2_PARAMETER_CODE) String authorizationCode,
|
||||||
|
@QueryParam(OAuth2Constants.ERROR) String error) {
|
||||||
|
|
||||||
|
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder();
|
||||||
|
if (error != null) {
|
||||||
|
//logger.error("Failed " + getConfig().getAlias() + " broker login: " + error);
|
||||||
|
event.event(EventType.LOGIN);
|
||||||
|
event.error(error);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
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, uriInfo.getAbsolutePath().toString())
|
||||||
|
.param(OAUTH2_PARAMETER_GRANT_TYPE, OAUTH2_GRANT_TYPE_AUTHORIZATION_CODE).asString();
|
||||||
|
|
||||||
|
FederatedIdentity federatedIdentity = getFederatedIdentity(response);
|
||||||
|
|
||||||
|
if (getConfig().isStoreToken()) {
|
||||||
|
federatedIdentity.setToken(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
return callback.authenticated(new HashMap<String, String>(), getConfig(), federatedIdentity, state);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("Failed to make identity provider oauth callback", e);
|
||||||
|
}
|
||||||
|
event.event(EventType.LOGIN);
|
||||||
|
event.error(Errors.IDENTITY_PROVIDER_LOGIN_FAILURE);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,23 @@
|
||||||
<groupId>org.picketlink</groupId>
|
<groupId>org.picketlink</groupId>
|
||||||
<artifactId>picketlink-federation</artifactId>
|
<artifactId>picketlink-federation</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-services</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-events-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logging</groupId>
|
||||||
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
372
broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
Executable file
372
broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
Executable file
|
@ -0,0 +1,372 @@
|
||||||
|
package org.keycloak.broker.saml;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
|
import org.keycloak.ClientConnection;
|
||||||
|
import org.keycloak.VerificationException;
|
||||||
|
import org.keycloak.broker.provider.FederatedIdentity;
|
||||||
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
|
import org.keycloak.broker.provider.IdentityProvider;
|
||||||
|
import org.keycloak.events.Details;
|
||||||
|
import org.keycloak.events.Errors;
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
|
import org.keycloak.protocol.saml.SAMLRequestParser;
|
||||||
|
import org.keycloak.protocol.saml.SamlProtocol;
|
||||||
|
import org.keycloak.protocol.saml.SamlProtocolUtils;
|
||||||
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
|
import org.keycloak.services.managers.EventsManager;
|
||||||
|
import org.keycloak.services.messages.Messages;
|
||||||
|
import org.keycloak.services.resources.flows.Flows;
|
||||||
|
import org.picketlink.common.constants.GeneralConstants;
|
||||||
|
import org.picketlink.common.constants.JBossSAMLConstants;
|
||||||
|
import org.picketlink.common.constants.JBossSAMLURIConstants;
|
||||||
|
import org.picketlink.common.exceptions.ProcessingException;
|
||||||
|
import org.picketlink.common.util.DocumentUtil;
|
||||||
|
import org.picketlink.common.util.StaxParserUtil;
|
||||||
|
import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response;
|
||||||
|
import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
|
||||||
|
import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder;
|
||||||
|
import org.picketlink.identity.federation.core.util.JAXPValidationUtil;
|
||||||
|
import org.picketlink.identity.federation.core.util.XMLEncryptionUtil;
|
||||||
|
import org.picketlink.identity.federation.core.util.XMLSignatureUtil;
|
||||||
|
import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
|
||||||
|
import org.picketlink.identity.federation.saml.v2.assertion.AuthnStatementType;
|
||||||
|
import org.picketlink.identity.federation.saml.v2.assertion.EncryptedAssertionType;
|
||||||
|
import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
|
||||||
|
import org.picketlink.identity.federation.saml.v2.assertion.SubjectType;
|
||||||
|
import org.picketlink.identity.federation.saml.v2.protocol.ResponseType;
|
||||||
|
import org.picketlink.identity.federation.saml.v2.protocol.StatusResponseType;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.FormParam;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
import java.security.PrivateKey;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class SAMLEndpoint {
|
||||||
|
protected static final Logger logger = Logger.getLogger(SAMLEndpoint.class);
|
||||||
|
protected RealmModel realm;
|
||||||
|
protected EventBuilder event;
|
||||||
|
protected SAMLIdentityProviderConfig config;
|
||||||
|
protected IdentityProvider.AuthenticationCallback callback;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
private UriInfo uriInfo;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
private KeycloakSession session;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
private ClientConnection clientConnection;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
private HttpRequest request;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
private HttpHeaders headers;
|
||||||
|
|
||||||
|
|
||||||
|
public SAMLEndpoint(RealmModel realm, SAMLIdentityProviderConfig config, IdentityProvider.AuthenticationCallback callback) {
|
||||||
|
this.realm = realm;
|
||||||
|
this.config = config;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
public Response redirectBinding(@QueryParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
|
||||||
|
@QueryParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
|
||||||
|
@QueryParam(GeneralConstants.RELAY_STATE) String relayState) {
|
||||||
|
return new RedirectBinding().execute(samlRequest, samlResponse, relayState);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
|
||||||
|
public Response postBinding(@FormParam(GeneralConstants.SAML_REQUEST_KEY) String samlRequest,
|
||||||
|
@FormParam(GeneralConstants.SAML_RESPONSE_KEY) String samlResponse,
|
||||||
|
@FormParam(GeneralConstants.RELAY_STATE) String relayState) {
|
||||||
|
return new PostBinding().execute(samlRequest, samlResponse, relayState);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract class Binding {
|
||||||
|
private boolean checkSsl() {
|
||||||
|
if (uriInfo.getBaseUri().getScheme().equals("https")) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return !realm.getSslRequired().isRequired(clientConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Response basicChecks(String samlRequest, String samlResponse) {
|
||||||
|
if (!checkSsl()) {
|
||||||
|
event.event(EventType.LOGIN);
|
||||||
|
event.error(Errors.SSL_REQUIRED);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.HTTPS_REQUIRED);
|
||||||
|
}
|
||||||
|
if (!realm.isEnabled()) {
|
||||||
|
event.event(EventType.LOGIN_ERROR);
|
||||||
|
event.error(Errors.REALM_DISABLED);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.REALM_NOT_ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (samlRequest == null && samlResponse == null) {
|
||||||
|
event.event(EventType.LOGIN);
|
||||||
|
event.error(Errors.INVALID_REQUEST);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST );
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract String getBindingType();
|
||||||
|
protected abstract void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException;
|
||||||
|
protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest);
|
||||||
|
protected abstract SAMLDocumentHolder extractResponseDocument(String response);
|
||||||
|
protected PublicKey getIDPKey() {
|
||||||
|
X509Certificate certificate = null;
|
||||||
|
try {
|
||||||
|
certificate = XMLSignatureUtil.getX509CertificateFromKeyInfoString(config.getSigningCertificate().replaceAll("\\s", ""));
|
||||||
|
} catch (ProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return certificate.getPublicKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response execute(String samlRequest, String samlResponse, String relayState) {
|
||||||
|
event = new EventsManager(realm, session, clientConnection).createEventBuilder();
|
||||||
|
Response response = basicChecks(samlRequest, samlResponse);
|
||||||
|
if (response != null) return response;
|
||||||
|
if (samlRequest != null) throw new RuntimeException("NOT IMPLEMETED");//return handleSamlRequest(samlRequest, relayState);
|
||||||
|
else return handleSamlResponse(samlResponse, relayState);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Response handleLoginResponse(String samlResponse, SAMLDocumentHolder holder, ResponseType responseType, String relayState) {
|
||||||
|
if (config.isValidateSignature()) {
|
||||||
|
try {
|
||||||
|
verifySignature(holder);
|
||||||
|
} catch (VerificationException e) {
|
||||||
|
logger.error("validation failed", e);
|
||||||
|
event.event(EventType.LOGIN);
|
||||||
|
event.error(Errors.INVALID_SIGNATURE);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUESTER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
AssertionType assertion = getAssertion(responseType);
|
||||||
|
SubjectType subject = assertion.getSubject();
|
||||||
|
SubjectType.STSubType subType = subject.getSubType();
|
||||||
|
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
||||||
|
Map<String, String> notes = new HashMap<>();
|
||||||
|
notes.put("SAML_FEDERATED_SUBJECT", subjectNameID.getValue());
|
||||||
|
if (subjectNameID.getFormat() != null) notes.put("SAML_FEDERATED_SUBJECT_NAMEFORMAT", subjectNameID.getFormat().toString());
|
||||||
|
FederatedIdentity identity = new FederatedIdentity(subjectNameID.getValue());
|
||||||
|
|
||||||
|
identity.setUsername(subjectNameID.getValue());
|
||||||
|
|
||||||
|
if (subjectNameID.getFormat().toString().equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
|
||||||
|
identity.setEmail(subjectNameID.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.isStoreToken()) {
|
||||||
|
identity.setToken(samlResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthnStatementType authn = null;
|
||||||
|
for (Object statement : assertion.getStatements()) {
|
||||||
|
if (statement instanceof AuthnStatementType) {
|
||||||
|
authn = (AuthnStatementType)statement;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (authn != null && authn.getSessionIndex() != null) {
|
||||||
|
notes.put("SAML_FEDERATED_SESSION_INDEX", authn.getSessionIndex());
|
||||||
|
}
|
||||||
|
return callback.authenticated(notes, config, identity, relayState);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IdentityBrokerException("Could not process response from SAML identity provider.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private AssertionType getAssertion(ResponseType responseType) throws ProcessingException {
|
||||||
|
List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
|
||||||
|
|
||||||
|
if (assertions.isEmpty()) {
|
||||||
|
throw new IdentityBrokerException("No assertion from response.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseType.RTChoiceType rtChoiceType = assertions.get(0);
|
||||||
|
EncryptedAssertionType encryptedAssertion = rtChoiceType.getEncryptedAssertion();
|
||||||
|
|
||||||
|
if (encryptedAssertion != null) {
|
||||||
|
decryptAssertion(responseType, realm.getPrivateKey());
|
||||||
|
|
||||||
|
}
|
||||||
|
return responseType.getAssertions().get(0).getAssertion();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response handleSamlResponse(String samlResponse, String relayState) {
|
||||||
|
SAMLDocumentHolder holder = extractResponseDocument(samlResponse);
|
||||||
|
StatusResponseType statusResponse = (StatusResponseType)holder.getSamlObject();
|
||||||
|
// validate destination
|
||||||
|
if (!uriInfo.getAbsolutePath().toString().equals(statusResponse.getDestination())) {
|
||||||
|
event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
||||||
|
event.error(Errors.INVALID_SAML_RESPONSE);
|
||||||
|
event.detail(Details.REASON, "invalid_destination");
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REQUEST);
|
||||||
|
}
|
||||||
|
if (statusResponse instanceof ResponseType) {
|
||||||
|
return handleLoginResponse(samlResponse, holder, (ResponseType)statusResponse, relayState);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// todo need to check that it is actually a LogoutResponse
|
||||||
|
return handleLogoutResponse(holder, statusResponse, relayState);
|
||||||
|
}
|
||||||
|
//throw new RuntimeException("Unknown response type");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Response handleLogoutResponse(SAMLDocumentHolder holder, StatusResponseType responseType, String relayState) {
|
||||||
|
if (config.isValidateSignature()) {
|
||||||
|
try {
|
||||||
|
verifySignature(holder);
|
||||||
|
} catch (VerificationException e) {
|
||||||
|
logger.error("logout response validation failed", e);
|
||||||
|
event.event(EventType.LOGOUT);
|
||||||
|
event.error(Errors.INVALID_SIGNATURE);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (relayState == null) {
|
||||||
|
logger.error("no valid user session");
|
||||||
|
event.event(EventType.LOGOUT);
|
||||||
|
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||||
|
}
|
||||||
|
UserSessionModel userSession = session.sessions().getUserSession(realm, relayState);
|
||||||
|
if (userSession == null) {
|
||||||
|
logger.error("no valid user session");
|
||||||
|
event.event(EventType.LOGOUT);
|
||||||
|
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.IDENTITY_PROVIDER_UNEXPECTED_ERROR);
|
||||||
|
}
|
||||||
|
if (userSession.getState() != UserSessionModel.State.LOGGING_OUT) {
|
||||||
|
logger.error("usersession in different state");
|
||||||
|
event.event(EventType.LOGOUT);
|
||||||
|
event.error(Errors.USER_SESSION_NOT_FOUND);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.SESSION_NOT_ACTIVE);
|
||||||
|
}
|
||||||
|
return AuthenticationManager.finishBrowserLogout(session, realm, userSession, uriInfo, clientConnection, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected 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 IdentityBrokerException("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 ResponseType.RTChoiceType(assertion));
|
||||||
|
|
||||||
|
return responseType;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IdentityBrokerException("Could not decrypt assertion.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class PostBinding extends Binding {
|
||||||
|
@Override
|
||||||
|
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
|
||||||
|
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
|
||||||
|
return SAMLRequestParser.parseRequestPostBinding(samlRequest);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected SAMLDocumentHolder extractResponseDocument(String response) {
|
||||||
|
return SAMLRequestParser.parseResponsePostBinding(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getBindingType() {
|
||||||
|
return SamlProtocol.SAML_POST_BINDING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class RedirectBinding extends Binding {
|
||||||
|
@Override
|
||||||
|
protected void verifySignature(SAMLDocumentHolder documentHolder) throws VerificationException {
|
||||||
|
PublicKey publicKey = getIDPKey();
|
||||||
|
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
|
||||||
|
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SAMLDocumentHolder extractResponseDocument(String response) {
|
||||||
|
return SAMLRequestParser.parseRequestRedirectBinding(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getBindingType() {
|
||||||
|
return SamlProtocol.SAML_REDIRECT_BINDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,75 +17,40 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.broker.saml;
|
package org.keycloak.broker.saml;
|
||||||
|
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
|
||||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
|
||||||
import org.keycloak.broker.provider.FederatedIdentity;
|
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
|
import org.keycloak.protocol.saml.SAML2AuthnRequestBuilder;
|
||||||
|
import org.keycloak.protocol.saml.SAML2LogoutRequestBuilder;
|
||||||
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
|
import org.keycloak.protocol.saml.SAML2NameIDPolicyBuilder;
|
||||||
import org.picketlink.common.constants.JBossSAMLConstants;
|
|
||||||
import org.picketlink.common.constants.JBossSAMLURIConstants;
|
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.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.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.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriBuilder;
|
import javax.ws.rs.core.UriBuilder;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import javax.xml.namespace.QName;
|
|
||||||
import java.net.URLDecoder;
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.PrivateKey;
|
import java.security.PrivateKey;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.cert.X509Certificate;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Pedro Igor
|
* @author Pedro Igor
|
||||||
*/
|
*/
|
||||||
public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityProviderConfig> {
|
public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityProviderConfig> {
|
||||||
|
|
||||||
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) {
|
public SAMLIdentityProvider(SAMLIdentityProviderConfig config) {
|
||||||
super(config);
|
super(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationResponse handleRequest(AuthenticationRequest request) {
|
public Object callback(RealmModel realm, AuthenticationCallback callback) {
|
||||||
|
return new SAMLEndpoint(realm, getConfig(), callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response handleRequest(AuthenticationRequest request) {
|
||||||
try {
|
try {
|
||||||
UriInfo uriInfo = request.getUriInfo();
|
UriInfo uriInfo = request.getUriInfo();
|
||||||
RealmModel realm = request.getRealm();
|
RealmModel realm = request.getRealm();
|
||||||
|
@ -99,12 +64,14 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
|
|
||||||
String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
|
String protocolBinding = JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get();
|
||||||
|
|
||||||
|
String assertionConsumerServiceUrl = request.getRedirectUri();
|
||||||
|
|
||||||
if (getConfig().isPostBindingResponse()) {
|
if (getConfig().isPostBindingResponse()) {
|
||||||
protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
|
protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
|
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
|
||||||
.assertionConsumerUrl(request.getRedirectUri())
|
.assertionConsumerUrl(assertionConsumerServiceUrl)
|
||||||
.destination(destinationUrl)
|
.destination(destinationUrl)
|
||||||
.issuer(issuerURL)
|
.issuer(issuerURL)
|
||||||
.forceAuthn(getConfig().isForceAuthn())
|
.forceAuthn(getConfig().isForceAuthn())
|
||||||
|
@ -117,11 +84,11 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
PublicKey publicKey = realm.getPublicKey();
|
PublicKey publicKey = realm.getPublicKey();
|
||||||
|
|
||||||
if (privateKey == null) {
|
if (privateKey == null) {
|
||||||
throw new IdentityBrokerException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + realm.getName() + "] does not have a private key.");
|
throw new IdentityBrokerException("Identity Provider [" + getConfig().getAlias() + "] wants a signed authentication request. But the Realm [" + realm.getName() + "] does not have a private key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publicKey == null) {
|
if (publicKey == null) {
|
||||||
throw new IdentityBrokerException("Identity Provider [" + getConfig().getName() + "] wants a signed authentication request. But the Realm [" + realm.getName() + "] does not have a public key.");
|
throw new IdentityBrokerException("Identity Provider [" + getConfig().getAlias() + "] wants a signed authentication request. But the Realm [" + realm.getName() + "] does not have a public key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyPair keypair = new KeyPair(publicKey, privateKey);
|
KeyPair keypair = new KeyPair(publicKey, privateKey);
|
||||||
|
@ -131,9 +98,9 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getConfig().isPostBindingAuthnRequest()) {
|
if (getConfig().isPostBindingAuthnRequest()) {
|
||||||
return AuthenticationResponse.fromResponse(authnRequestBuilder.postBinding().request());
|
return authnRequestBuilder.postBinding().request();
|
||||||
} else {
|
} else {
|
||||||
return AuthenticationResponse.fromResponse(authnRequestBuilder.redirectBinding().request());
|
return authnRequestBuilder.redirectBinding().request();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IdentityBrokerException("Could not create authentication request.", e);
|
throw new IdentityBrokerException("Could not create authentication request.", e);
|
||||||
|
@ -144,152 +111,30 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
|
return UriBuilder.fromUri(uriInfo.getBaseUri()).path("realms").path(realm.getName()).build().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRelayState(AuthenticationRequest request) {
|
|
||||||
return getRequestParameter(request, RELAY_STATE_PARAMETER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthenticationResponse handleResponse(AuthenticationRequest request) {
|
|
||||||
String samlResponse = getRequestParameter(request, SAML_RESPONSE_PARAMETER);
|
|
||||||
|
|
||||||
if (samlResponse == null) {
|
|
||||||
throw new IdentityBrokerException("No response from SAML identity provider.");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
AssertionType assertion = getAssertion(samlResponse, request);
|
|
||||||
SubjectType subject = assertion.getSubject();
|
|
||||||
STSubType subType = subject.getSubType();
|
|
||||||
NameIDType subjectNameID = (NameIDType) subType.getBaseID();
|
|
||||||
FederatedIdentity identity = new FederatedIdentity(subjectNameID.getValue());
|
|
||||||
|
|
||||||
identity.setUsername(subjectNameID.getValue());
|
|
||||||
|
|
||||||
if (subjectNameID.getFormat().toString().equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) {
|
|
||||||
identity.setEmail(subjectNameID.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getConfig().isStoreToken()) {
|
|
||||||
identity.setToken(samlResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
return AuthenticationResponse.end(identity);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new IdentityBrokerException("Could not process response from SAML identity provider.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response retrieveToken(FederatedIdentityModel identity) {
|
public Response retrieveToken(FederatedIdentityModel identity) {
|
||||||
return Response.ok(identity.getToken()).build();
|
return Response.ok(identity.getToken()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AssertionType getAssertion(String samlResponse, AuthenticationRequest request) throws Exception {
|
@Override
|
||||||
SAML2Request saml2Request = new SAML2Request();
|
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
|
||||||
ResponseType responseType;
|
if (getConfig().getSingleLogoutServiceUrl() == null) return null;
|
||||||
|
|
||||||
if (getConfig().isPostBindingResponse()) {
|
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
|
||||||
responseType = (ResponseType) saml2Request
|
.issuer(getEntityId(uriInfo, realm))
|
||||||
.getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(samlResponse, "UTF-8")));
|
.sessionIndex(userSession.getNote("SAML_FEDERATED_SESSION_INDEX"))
|
||||||
} else {
|
.userPrincipal(userSession.getNote("SAML_FEDERATED_SUBJECT"), userSession.getNote("SAML_FEDERATED_SUBJECT_NAMEFORMAT"))
|
||||||
responseType = (ResponseType) saml2Request
|
.destination(getConfig().getSingleLogoutServiceUrl());
|
||||||
.getSAML2ObjectFromStream(RedirectBindingUtil.base64DeflateDecode((samlResponse)));
|
if (getConfig().isWantAuthnRequestsSigned()) {
|
||||||
|
logoutBuilder.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
|
||||||
|
.signDocument();
|
||||||
}
|
}
|
||||||
|
|
||||||
validateStatusResponse(responseType);
|
|
||||||
validateSignature(saml2Request);
|
|
||||||
|
|
||||||
List<RTChoiceType> assertions = responseType.getAssertions();
|
|
||||||
|
|
||||||
if (assertions.isEmpty()) {
|
|
||||||
throw new IdentityBrokerException("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().getSigningCertificate().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 IdentityBrokerException("Authentication failed with code [" + statusCode.getValue() + " and detail [" + detailMessage.toString() + ".");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ResponseType decryptAssertion(ResponseType responseType, PrivateKey privateKey) throws ProcessingException {
|
|
||||||
SAML2Response saml2Response = new SAML2Response();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Document doc = saml2Response.convert(responseType);
|
return logoutBuilder.relayState(userSession.getId()).postBinding().request();
|
||||||
Element enc = DocumentUtil.getElement(doc, new QName(JBossSAMLConstants.ENCRYPTED_ASSERTION.get()));
|
|
||||||
|
|
||||||
if (enc == null) {
|
|
||||||
throw new IdentityBrokerException("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) {
|
} catch (Exception e) {
|
||||||
throw new IdentityBrokerException("Could not decrypt assertion.", e);
|
throw new RuntimeException(e);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getRequestParameter(AuthenticationRequest request, String parameterName) {
|
|
||||||
MultivaluedMap<String, String> requestParameters;
|
|
||||||
|
|
||||||
if (getConfig().isPostBindingResponse()) {
|
|
||||||
HttpRequest httpRequest = request.getHttpRequest();
|
|
||||||
requestParameters = httpRequest.getFormParameters();
|
|
||||||
} else {
|
|
||||||
UriInfo uriInfo = request.getUriInfo();
|
|
||||||
requestParameters = uriInfo.getQueryParameters();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return requestParameters.getFirst(parameterName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -301,7 +146,12 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
authnBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
|
authnBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
String assertionConsumerService = uriInfo.getBaseUriBuilder().path("realms").path(realm.getName()).path("broker").path(getConfig().getId()).build().toString();
|
String endpoint = uriInfo.getBaseUriBuilder()
|
||||||
|
.path("realms").path(realm.getName())
|
||||||
|
.path("broker")
|
||||||
|
.path(getConfig().getAlias())
|
||||||
|
.path("endpoint")
|
||||||
|
.build().toString();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -311,10 +161,9 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n" +
|
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n" +
|
||||||
" <NameIDFormat>" + getConfig().getNameIDPolicyFormat() + "\n" +
|
" <NameIDFormat>" + getConfig().getNameIDPolicyFormat() + "\n" +
|
||||||
" </NameIDFormat>\n" +
|
" </NameIDFormat>\n" +
|
||||||
// todo single logout service description
|
" <SingleLogoutService Binding=\"" + authnBinding + "\" Location=\"" + endpoint + "\"/>\n" +
|
||||||
// " <SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8081/sales-metadata/\"/>\n" +
|
|
||||||
" <AssertionConsumerService\n" +
|
" <AssertionConsumerService\n" +
|
||||||
" Binding=\"" + authnBinding + "\" Location=\"" + assertionConsumerService + "\"\n" +
|
" Binding=\"" + authnBinding + "\" Location=\"" + endpoint + "\"\n" +
|
||||||
" index=\"1\" isDefault=\"true\" />\n";
|
" index=\"1\" isDefault=\"true\" />\n";
|
||||||
if (getConfig().isWantAuthnRequestsSigned()) {
|
if (getConfig().isWantAuthnRequestsSigned()) {
|
||||||
descriptor +=
|
descriptor +=
|
||||||
|
|
8
broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
Normal file → Executable file
8
broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
Normal file → Executable file
|
@ -39,6 +39,14 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
||||||
getConfig().put("singleSignOnServiceUrl", singleSignOnServiceUrl);
|
getConfig().put("singleSignOnServiceUrl", singleSignOnServiceUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSingleLogoutServiceUrl() {
|
||||||
|
return getConfig().get("singleLogoutServiceUrl");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSingleLogoutServiceUrl(String singleLogoutServiceUrl) {
|
||||||
|
getConfig().put("singleLogoutServiceUrl", singleLogoutServiceUrl);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isValidateSignature() {
|
public boolean isValidateSignature() {
|
||||||
return Boolean.valueOf(getConfig().get("validateSignature"));
|
return Boolean.valueOf(getConfig().get("validateSignature"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> parseConfig(InputStream inputStream) {
|
public Map<String, String> parseConfig(InputStream inputStream) {
|
||||||
try {
|
try {
|
||||||
Object parsedObject = new SAMLParser().parse(inputStream);
|
Object parsedObject = new SAMLParser().parse(inputStream);
|
||||||
EntityDescriptorType entityType;
|
EntityDescriptorType entityType;
|
||||||
|
@ -90,6 +90,18 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||||
singleSignOnServiceUrl = endpoint.getLocation().toString();
|
singleSignOnServiceUrl = endpoint.getLocation().toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
String singleLogoutServiceUrl = null;
|
||||||
|
for (EndpointType endpoint : idpDescriptor.getSingleLogoutService()) {
|
||||||
|
if (postBinding && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) {
|
||||||
|
singleLogoutServiceUrl = endpoint.getLocation().toString();
|
||||||
|
break;
|
||||||
|
} else if (!postBinding && endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){
|
||||||
|
singleLogoutServiceUrl = endpoint.getLocation().toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
samlIdentityProviderConfig.setSingleLogoutServiceUrl(singleLogoutServiceUrl);
|
||||||
samlIdentityProviderConfig.setSingleSignOnServiceUrl(singleSignOnServiceUrl);
|
samlIdentityProviderConfig.setSingleSignOnServiceUrl(singleSignOnServiceUrl);
|
||||||
samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned());
|
samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned());
|
||||||
samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned());
|
samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned());
|
||||||
|
|
|
@ -47,8 +47,7 @@
|
||||||
<constraints nullable="false"/>
|
<constraints nullable="false"/>
|
||||||
</column>
|
</column>
|
||||||
<column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false"/>
|
<column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||||
<column name="PROVIDER_NONIMAL_ID" type="VARCHAR(255)"/>
|
<column name="PROVIDER_ALIAS" type="VARCHAR(255)"/>
|
||||||
<column name="PROVIDER_NAME" type="VARCHAR(255)"/>
|
|
||||||
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
|
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
|
||||||
<column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN" defaultValueBoolean="false"/>
|
<column name="UPDATE_PROFILE_FIRST_LOGIN" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||||
<column name="STORE_TOKEN" type="BOOLEAN" defaultValueBoolean="false"/>
|
<column name="STORE_TOKEN" type="BOOLEAN" defaultValueBoolean="false"/>
|
||||||
|
@ -95,7 +94,7 @@
|
||||||
<addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
|
<addForeignKeyConstraint baseColumnNames="IDENTITY_PROVIDER_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_7CELWNIBJI49AVXSRTUF6XJ12" referencedColumnNames="INTERNAL_ID" referencedTableName="IDENTITY_PROVIDER"/>
|
||||||
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_56ELWNIBJI49AVXSRTUF6XJ23" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
<addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_IDENTITY_PROVIDER_MAPPING" constraintName="FK_56ELWNIBJI49AVXSRTUF6XJ23" referencedColumnNames="ID" referencedTableName="CLIENT"/>
|
||||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SUPPORTED_LOCALES" constraintName="FK_SUPPORTED_LOCALES_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SUPPORTED_LOCALES" constraintName="FK_SUPPORTED_LOCALES_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||||
<addUniqueConstraint columnNames="PROVIDER_NONIMAL_ID" constraintName="UK_2DAELWNIBJI49AVXSRTUF6XJ33" tableName="IDENTITY_PROVIDER"/>
|
<addUniqueConstraint columnNames="PROVIDER_ALIAS, REALM_ID" constraintName="UK_2DAELWNIBJI49AVXSRTUF6XJ33" tableName="IDENTITY_PROVIDER"/>
|
||||||
<addUniqueConstraint columnNames="IDENTITY_PROVIDER_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_IDENTITY_PROVIDER_MAPPING"/>
|
<addUniqueConstraint columnNames="IDENTITY_PROVIDER_ID,CLIENT_ID" constraintName="UK_7CAELWNIBJI49AVXSRTUF6XJ12" tableName="CLIENT_IDENTITY_PROVIDER_MAPPING"/>
|
||||||
|
|
||||||
<addColumn tableName="REALM">
|
<addColumn tableName="REALM">
|
||||||
|
|
28
core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
Normal file → Executable file
28
core/src/main/java/org/keycloak/representations/idm/IdentityProviderRepresentation.java
Normal file → Executable file
|
@ -25,15 +25,13 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class IdentityProviderRepresentation {
|
public class IdentityProviderRepresentation {
|
||||||
|
|
||||||
protected String id;
|
protected String alias;
|
||||||
protected String internalId;
|
protected String internalId;
|
||||||
protected String providerId;
|
protected String providerId;
|
||||||
protected String name;
|
|
||||||
protected boolean enabled = true;
|
protected boolean enabled = true;
|
||||||
protected boolean updateProfileFirstLogin = true;
|
protected boolean updateProfileFirstLogin = true;
|
||||||
protected boolean storeToken;
|
protected boolean storeToken;
|
||||||
protected boolean authenticateByDefault;
|
protected boolean authenticateByDefault;
|
||||||
protected String groupName;
|
|
||||||
protected Map<String, String> config = new HashMap<String, String>();
|
protected Map<String, String> config = new HashMap<String, String>();
|
||||||
|
|
||||||
public String getInternalId() {
|
public String getInternalId() {
|
||||||
|
@ -44,12 +42,12 @@ public class IdentityProviderRepresentation {
|
||||||
this.internalId = internalId;
|
this.internalId = internalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getAlias() {
|
||||||
return this.id;
|
return this.alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(String id) {
|
public void setAlias(String alias) {
|
||||||
this.id = id;
|
this.alias = alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getProviderId() {
|
public String getProviderId() {
|
||||||
|
@ -60,14 +58,6 @@ public class IdentityProviderRepresentation {
|
||||||
this.providerId = providerId;
|
this.providerId = providerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getConfig() {
|
public Map<String, String> getConfig() {
|
||||||
return this.config;
|
return this.config;
|
||||||
}
|
}
|
||||||
|
@ -107,12 +97,4 @@ public class IdentityProviderRepresentation {
|
||||||
public void setStoreToken(boolean storeToken) {
|
public void setStoreToken(boolean storeToken) {
|
||||||
this.storeToken = storeToken;
|
this.storeToken = storeToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getGroupName() {
|
|
||||||
return this.groupName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGroupName(String groupName) {
|
|
||||||
this.groupName = groupName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ public interface Errors {
|
||||||
String INVALID_REDIRECT_URI = "invalid_redirect_uri";
|
String INVALID_REDIRECT_URI = "invalid_redirect_uri";
|
||||||
String INVALID_CODE = "invalid_code";
|
String INVALID_CODE = "invalid_code";
|
||||||
String INVALID_TOKEN = "invalid_token";
|
String INVALID_TOKEN = "invalid_token";
|
||||||
|
String INVALID_SAML_RESPONSE = "invalid_saml_response";
|
||||||
String INVALID_SAML_AUTHN_REQUEST = "invalid_authn_request";
|
String INVALID_SAML_AUTHN_REQUEST = "invalid_authn_request";
|
||||||
String INVALID_SAML_LOGOUT_REQUEST = "invalid_logout_request";
|
String INVALID_SAML_LOGOUT_REQUEST = "invalid_logout_request";
|
||||||
String INVALID_SAML_LOGOUT_RESPONSE = "invalid_logout_response";
|
String INVALID_SAML_LOGOUT_RESPONSE = "invalid_logout_response";
|
||||||
|
@ -48,4 +49,5 @@ public interface Errors {
|
||||||
|
|
||||||
String EMAIL_SEND_FAILED = "email_send_failed";
|
String EMAIL_SEND_FAILED = "email_send_failed";
|
||||||
String INVALID_EMAIL = "invalid_email";
|
String INVALID_EMAIL = "invalid_email";
|
||||||
|
String IDENTITY_PROVIDER_LOGIN_FAILURE = "identity_provider_login_failure";
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class AccountFederatedIdentityBean {
|
||||||
int availableIdentities = 0;
|
int availableIdentities = 0;
|
||||||
if (identityProviders != null && !identityProviders.isEmpty()) {
|
if (identityProviders != null && !identityProviders.isEmpty()) {
|
||||||
for (IdentityProviderModel provider : identityProviders) {
|
for (IdentityProviderModel provider : identityProviders) {
|
||||||
String providerId = provider.getId();
|
String providerId = provider.getAlias();
|
||||||
|
|
||||||
FederatedIdentityModel identity = getIdentity(identities, providerId);
|
FederatedIdentityModel identity = getIdentity(identities, providerId);
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public class AccountFederatedIdentityBean {
|
||||||
.queryParam("stateChecker", stateChecker)
|
.queryParam("stateChecker", stateChecker)
|
||||||
.build().toString();
|
.build().toString();
|
||||||
|
|
||||||
FederatedIdentityEntry entry = new FederatedIdentityEntry(identity, provider.getId(), provider.getName(), actionUrl);
|
FederatedIdentityEntry entry = new FederatedIdentityEntry(identity, provider.getAlias(), actionUrl);
|
||||||
this.identities.add(entry);
|
this.identities.add(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,13 +79,11 @@ public class AccountFederatedIdentityBean {
|
||||||
|
|
||||||
private FederatedIdentityModel federatedIdentityModel;
|
private FederatedIdentityModel federatedIdentityModel;
|
||||||
private final String providerId;
|
private final String providerId;
|
||||||
private final String providerName;
|
|
||||||
private final String actionUrl;
|
private final String actionUrl;
|
||||||
|
|
||||||
public FederatedIdentityEntry(FederatedIdentityModel federatedIdentityModel, String providerId, String providerName, String actionUrl) {
|
public FederatedIdentityEntry(FederatedIdentityModel federatedIdentityModel, String providerId, String actionUrl) {
|
||||||
this.federatedIdentityModel = federatedIdentityModel;
|
this.federatedIdentityModel = federatedIdentityModel;
|
||||||
this.providerId = providerId;
|
this.providerId = providerId;
|
||||||
this.providerName = providerName;
|
|
||||||
this.actionUrl = actionUrl;
|
this.actionUrl = actionUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,10 +91,6 @@ public class AccountFederatedIdentityBean {
|
||||||
return providerId;
|
return providerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getProviderName() {
|
|
||||||
return providerName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUserId() {
|
public String getUserId() {
|
||||||
return federatedIdentityModel != null ? federatedIdentityModel.getUserId() : null;
|
return federatedIdentityModel != null ? federatedIdentityModel.getUserId() : null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'RealmIdentityProviderCtrl'
|
controller : 'RealmIdentityProviderCtrl'
|
||||||
})
|
})
|
||||||
.when('/realms/:realm/identity-provider-settings/provider/:provider_id/:id', {
|
.when('/realms/:realm/identity-provider-settings/provider/:provider_id/:alias', {
|
||||||
templateUrl : function(params){ return resourceUrl + '/partials/realm-identity-provider-' + params.provider_id + '.html'; },
|
templateUrl : function(params){ return resourceUrl + '/partials/realm-identity-provider-' + params.provider_id + '.html'; },
|
||||||
resolve : {
|
resolve : {
|
||||||
realm : function(RealmLoader) {
|
realm : function(RealmLoader) {
|
||||||
|
@ -188,7 +188,7 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
controller : 'RealmIdentityProviderCtrl'
|
controller : 'RealmIdentityProviderCtrl'
|
||||||
})
|
})
|
||||||
.when('/realms/:realm/identity-provider-settings/provider/:provider_id/:id/export', {
|
.when('/realms/:realm/identity-provider-settings/provider/:provider_id/:alias/export', {
|
||||||
templateUrl : resourceUrl + '/partials/realm-identity-provider-export.html',
|
templateUrl : resourceUrl + '/partials/realm-identity-provider-export.html',
|
||||||
resolve : {
|
resolve : {
|
||||||
realm : function(RealmLoader) {
|
realm : function(RealmLoader) {
|
||||||
|
|
|
@ -638,44 +638,93 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, appli
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload, $http, realm, instance, providerFactory, IdentityProvider, serverInfo, $location, Notifications) {
|
module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload, $http, realm, instance, providerFactory, IdentityProvider, serverInfo, $location, Notifications, Dialog) {
|
||||||
console.log('RealmIdentityProviderCtrl');
|
console.log('RealmIdentityProviderCtrl');
|
||||||
|
|
||||||
$scope.realm = angular.copy(realm);
|
$scope.realm = angular.copy(realm);
|
||||||
|
|
||||||
$scope.hidePassword = true;
|
$scope.initSamlProvider = function() {
|
||||||
|
$scope.nameIdFormats = [
|
||||||
|
{
|
||||||
|
format: "urn:oasis:names:tc:SAML:2.0:nameid-format:transient",
|
||||||
|
name: "Transient"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent",
|
||||||
|
name: "Persistent"
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
|
||||||
|
name: "Email"
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos",
|
||||||
|
name: "Kerberos"
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName",
|
||||||
|
name: "X.509 Subject Name"
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName",
|
||||||
|
name: "Windows Domain Qualified Name"
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
format: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
|
||||||
|
name: "Unspecified"
|
||||||
|
|
||||||
|
}
|
||||||
|
];
|
||||||
|
if (instance && instance.alias) {
|
||||||
|
|
||||||
$scope.getBoolean = function(value) {
|
|
||||||
if (value == 'true') {
|
|
||||||
return true;
|
|
||||||
} else if (value == 'false') {
|
|
||||||
return false;
|
|
||||||
} else {
|
} else {
|
||||||
return value;
|
$scope.identityProvider.config.nameIDPolicyFormat = $scope.nameIdFormats[0].format;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance && instance.id) {
|
$scope.hidePassword = true;
|
||||||
|
|
||||||
|
if (instance && instance.alias) {
|
||||||
$scope.identityProvider = angular.copy(instance);
|
$scope.identityProvider = angular.copy(instance);
|
||||||
$scope.newIdentityProvider = false;
|
$scope.newIdentityProvider = false;
|
||||||
} else {
|
} else {
|
||||||
$scope.identityProvider = {};
|
$scope.identityProvider = {};
|
||||||
$scope.identityProvider.config = {};
|
$scope.identityProvider.config = {};
|
||||||
$scope.identityProvider.id = "";
|
$scope.identityProvider.alias = providerFactory.name;
|
||||||
$scope.identityProvider.providerId = providerFactory.id;
|
$scope.identityProvider.providerId = providerFactory.id;
|
||||||
$scope.identityProvider.name = providerFactory.name;
|
|
||||||
$scope.identityProvider.enabled = true;
|
$scope.identityProvider.enabled = true;
|
||||||
$scope.identityProvider.updateProfileFirstLogin = true;
|
$scope.identityProvider.updateProfileFirstLogin = true;
|
||||||
$scope.identityProvider.authenticateByDefault = false;
|
$scope.identityProvider.authenticateByDefault = false;
|
||||||
$scope.newIdentityProvider = true;
|
$scope.newIdentityProvider = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$scope.changed = $scope.newIdentityProvider;
|
||||||
|
|
||||||
|
$scope.$watch('identityProvider', function() {
|
||||||
|
if (!angular.equals($scope.identityProvider, instance)) {
|
||||||
|
$scope.changed = true;
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
|
||||||
$scope.serverInfo = serverInfo;
|
$scope.serverInfo = serverInfo;
|
||||||
|
|
||||||
$scope.allProviders = angular.copy(serverInfo.identityProviders);
|
$scope.allProviders = angular.copy(serverInfo.identityProviders);
|
||||||
|
|
||||||
$scope.configuredProviders = angular.copy(realm.identityProviders);
|
$scope.configuredProviders = angular.copy(realm.identityProviders);
|
||||||
|
|
||||||
|
$scope.$watch(function() {
|
||||||
|
return $location.path();
|
||||||
|
}, function() {
|
||||||
|
$scope.path = $location.path().substring(1).split("/");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
$scope.files = [];
|
$scope.files = [];
|
||||||
$scope.importFile = false;
|
$scope.importFile = false;
|
||||||
$scope.importUrl = false;
|
$scope.importUrl = false;
|
||||||
|
@ -689,9 +738,23 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
||||||
$scope.importUrl = false;
|
$scope.importUrl = false;
|
||||||
$scope.importFile = false;
|
$scope.importFile = false;
|
||||||
$scope.files = null;
|
$scope.files = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
var setConfig = function(data) {
|
||||||
|
for (var key in data) {
|
||||||
|
$scope.identityProvider.config[key] = data[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$scope.uploadFile = function() {
|
$scope.uploadFile = function() {
|
||||||
|
if (!$scope.identityProvider.alias) {
|
||||||
|
Notifications.error("You must specify an alias");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var input = {
|
||||||
|
providerId: providerFactory.id
|
||||||
|
}
|
||||||
//$files: an array of files selected, each file has name, size, and type.
|
//$files: an array of files selected, each file has name, size, and type.
|
||||||
for (var i = 0; i < $scope.files.length; i++) {
|
for (var i = 0; i < $scope.files.length; i++) {
|
||||||
var $file = $scope.files[i];
|
var $file = $scope.files[i];
|
||||||
|
@ -699,7 +762,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
||||||
url: authUrl + '/admin/realms/' + realm.realm + '/identity-provider/import',
|
url: authUrl + '/admin/realms/' + realm.realm + '/identity-provider/import',
|
||||||
// method: POST or PUT,
|
// method: POST or PUT,
|
||||||
// headers: {'headerKey': 'headerValue'}, withCredential: true,
|
// headers: {'headerKey': 'headerValue'}, withCredential: true,
|
||||||
data: $scope.identityProvider,
|
data: input,
|
||||||
file: $file
|
file: $file
|
||||||
/* set file formData name for 'Content-Desposition' header. Default: 'file' */
|
/* set file formData name for 'Content-Desposition' header. Default: 'file' */
|
||||||
//fileFormDataName: myFile,
|
//fileFormDataName: myFile,
|
||||||
|
@ -708,8 +771,9 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
||||||
}).progress(function(evt) {
|
}).progress(function(evt) {
|
||||||
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
|
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
|
||||||
}).success(function(data, status, headers) {
|
}).success(function(data, status, headers) {
|
||||||
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
|
setConfig(data);
|
||||||
Notifications.success("The " + $scope.identityProvider.name + " provider has been created.");
|
$scope.clearFileSelect();
|
||||||
|
Notifications.success("The IDP metadata has been loaded from file.");
|
||||||
}).error(function() {
|
}).error(function() {
|
||||||
Notifications.error("The file can not be uploaded. Please verify the file.");
|
Notifications.error("The file can not be uploaded. Please verify the file.");
|
||||||
});
|
});
|
||||||
|
@ -717,13 +781,22 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.importFrom = function() {
|
$scope.importFrom = function() {
|
||||||
$scope.identityProvider.fromUrl = $scope.fromUrl;
|
if (!$scope.identityProvider.alias) {
|
||||||
$http.post(authUrl + '/admin/realms/' + realm.realm + '/identity-provider/import', $scope.identityProvider)
|
Notifications.error("You must specify an alias");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var input = {
|
||||||
|
fromUrl: $scope.fromUrl,
|
||||||
|
providerId: providerFactory.id
|
||||||
|
}
|
||||||
|
$http.post(authUrl + '/admin/realms/' + realm.realm + '/identity-provider/import-config', input)
|
||||||
.success(function(data, status, headers) {
|
.success(function(data, status, headers) {
|
||||||
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
|
setConfig(data);
|
||||||
Notifications.success("The " + $scope.identityProvider.name + " provider has been created.");
|
$scope.fromUrl = null;
|
||||||
|
$scope.importUrl = false;
|
||||||
|
Notifications.success("Imported config information from url.");
|
||||||
}).error(function() {
|
}).error(function() {
|
||||||
Notifications.error("The provider can not be imported. Please verify the url.");
|
Notifications.error("Config can not be imported. Please verify the url.");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
$scope.$watch('fromUrl', function(newVal, oldVal){
|
$scope.$watch('fromUrl', function(newVal, oldVal){
|
||||||
|
@ -756,24 +829,30 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
||||||
$scope.callbackUrl = $location.absUrl().replace(/\/admin.*/, "/realms/") + realm.realm + "/broker/" ;
|
$scope.callbackUrl = $location.absUrl().replace(/\/admin.*/, "/realms/") + realm.realm + "/broker/" ;
|
||||||
|
|
||||||
$scope.addProvider = function(provider) {
|
$scope.addProvider = function(provider) {
|
||||||
|
console.log('addProvider');
|
||||||
$location.url("/create/identity-provider/" + realm.realm + "/" + provider.id);
|
$location.url("/create/identity-provider/" + realm.realm + "/" + provider.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.remove = function() {
|
$scope.remove = function() {
|
||||||
IdentityProvider.delete({
|
Dialog.confirmDelete($scope.identityProvider.alias, 'provider', function() {
|
||||||
realm: $scope.realm.realm,
|
$scope.identityProvider.$remove({
|
||||||
id: $scope.identityProvider.id
|
realm : realm.realm,
|
||||||
}, $scope.identityProvider, function () {
|
alias : $scope.identityProvider.alias
|
||||||
$scope.changed = false;
|
}, function() {
|
||||||
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
|
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
|
||||||
Notifications.success("The " + $scope.identityProvider.name + " provider has been deleted.");
|
Notifications.success("The application has been deleted.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.save = function() {
|
$scope.save = function() {
|
||||||
if ($scope.newIdentityProvider) {
|
if ($scope.newIdentityProvider) {
|
||||||
IdentityProvider.create({
|
if (!$scope.identityProvider.alias) {
|
||||||
realm: $scope.realm.realm
|
Notifications.error("You must specify an alias");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IdentityProvider.save({
|
||||||
|
realm: $scope.realm.realm, alias: ''
|
||||||
}, $scope.identityProvider, function () {
|
}, $scope.identityProvider, function () {
|
||||||
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
|
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
|
||||||
Notifications.success("The " + $scope.identityProvider.name + " provider has been created.");
|
Notifications.success("The " + $scope.identityProvider.name + " provider has been created.");
|
||||||
|
@ -789,6 +868,11 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.cancel = function() {
|
||||||
|
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
$scope.reset = function() {
|
$scope.reset = function() {
|
||||||
$scope.identityProvider = {};
|
$scope.identityProvider = {};
|
||||||
$scope.configuredProviders = angular.copy($scope.realm.identityProviders);
|
$scope.configuredProviders = angular.copy($scope.realm.identityProviders);
|
||||||
|
@ -798,39 +882,6 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
||||||
$scope.hidePassword = flag;
|
$scope.hidePassword = flag;
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.getBoolean = function(value) {
|
|
||||||
if (value == 'true') {
|
|
||||||
return true;
|
|
||||||
} else if (value == 'false') {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.initSamlProvider = function() {
|
|
||||||
if (instance && instance.id) {
|
|
||||||
$scope.identityProvider.config.validateSignature = $scope.getBoolean($scope.identityProvider.config.validateSignature);
|
|
||||||
$scope.identityProvider.config.forceAuthn = $scope.getBoolean($scope.identityProvider.config.forceAuthn);
|
|
||||||
$scope.identityProvider.config.postBindingAuthnRequest = $scope.getBoolean($scope.identityProvider.config.postBindingAuthnRequest);
|
|
||||||
$scope.identityProvider.config.postBindingResponse = $scope.getBoolean($scope.identityProvider.config.postBindingResponse);
|
|
||||||
$scope.identityProvider.config.wantAuthnRequestsSigned = $scope.getBoolean($scope.identityProvider.config.wantAuthnRequestsSigned);
|
|
||||||
} else {
|
|
||||||
$scope.identityProvider.config.validateSignature = true;
|
|
||||||
$scope.identityProvider.config.postBindingAuthnRequest = true;
|
|
||||||
$scope.identityProvider.config.postBindingResponse = true;
|
|
||||||
$scope.identityProvider.config.wantAuthnRequestsSigned = true;
|
|
||||||
$scope.identityProvider.config.nameIDPolicyFormat = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.initKerberosProvider = function() {
|
|
||||||
if (instance && instance.id) {
|
|
||||||
$scope.identityProvider.config.debug = $scope.getBoolean($scope.identityProvider.config.debug);
|
|
||||||
} else {
|
|
||||||
$scope.identityProvider.config.debug = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('RealmIdentityProviderExportCtrl', function(realm, identityProvider, $scope, $http, IdentityProviderExport) {
|
module.controller('RealmIdentityProviderExportCtrl', function(realm, identityProvider, $scope, $http, IdentityProviderExport) {
|
||||||
|
@ -840,7 +891,7 @@ module.controller('RealmIdentityProviderExportCtrl', function(realm, identityPro
|
||||||
$scope.exported = "";
|
$scope.exported = "";
|
||||||
$scope.exportedType = "";
|
$scope.exportedType = "";
|
||||||
|
|
||||||
var url = IdentityProviderExport.url({realm: realm.realm, id: identityProvider.id}) ;
|
var url = IdentityProviderExport.url({realm: realm.realm, alias: identityProvider.alias}) ;
|
||||||
$http.get(url).success(function(data, status, headers, config) {
|
$http.get(url).success(function(data, status, headers, config) {
|
||||||
$scope.exportedType = headers('Content-Type');
|
$scope.exportedType = headers('Content-Type');
|
||||||
$scope.exported = data;
|
$scope.exported = data;
|
||||||
|
|
|
@ -301,7 +301,7 @@ module.factory('IdentityProviderLoader', function(Loader, IdentityProvider, $rou
|
||||||
return Loader.get(IdentityProvider, function () {
|
return Loader.get(IdentityProvider, function () {
|
||||||
return {
|
return {
|
||||||
realm: $route.current.params.realm,
|
realm: $route.current.params.realm,
|
||||||
id: $route.current.params.id
|
alias: $route.current.params.alias
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1102,15 +1102,10 @@ module.factory('PasswordPolicy', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('IdentityProvider', function($resource) {
|
module.factory('IdentityProvider', function($resource) {
|
||||||
return $resource(authUrl + '/admin/realms/:realm/identity-provider/instances/:id', {
|
return $resource(authUrl + '/admin/realms/:realm/identity-provider/instances/:alias', {
|
||||||
realm : '@realm'
|
realm : '@realm',
|
||||||
|
alias : '@alias'
|
||||||
}, {
|
}, {
|
||||||
create : {
|
|
||||||
method : 'POST'
|
|
||||||
},
|
|
||||||
delete : {
|
|
||||||
method : 'DELETE'
|
|
||||||
},
|
|
||||||
update: {
|
update: {
|
||||||
method : 'PUT'
|
method : 'PUT'
|
||||||
}
|
}
|
||||||
|
@ -1118,11 +1113,11 @@ module.factory('IdentityProvider', function($resource) {
|
||||||
});
|
});
|
||||||
|
|
||||||
module.factory('IdentityProviderExport', function($resource) {
|
module.factory('IdentityProviderExport', function($resource) {
|
||||||
var url = authUrl + '/admin/realms/:realm/identity-provider/instances/:id/export';
|
var url = authUrl + '/admin/realms/:realm/identity-provider/instances/:alias/export';
|
||||||
return {
|
return {
|
||||||
url : function(parameters)
|
url : function(parameters)
|
||||||
{
|
{
|
||||||
return url.replace(':realm', parameters.realm).replace(':id', parameters.id);
|
return url.replace(':realm', parameters.realm).replace(':alias', parameters.alias);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
|
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
|
||||||
<li class="active"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.id}}">{{identityProvider.name}} Provider</a></li>
|
<li class="active"><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}} Provider</a></li>
|
||||||
<li class="active">{{identityProvider.name}} Provider Export</li>
|
<li class="active">{{identityProvider.alias}} Provider Export</li>
|
||||||
</ol>
|
</ol>
|
||||||
<h2 class="pull-left">{{identityProvider.name}} Provider Export</h2>
|
<h2 class="pull-left">{{identityProvider.alias}} Provider Export</h2>
|
||||||
<form class="form-horizontal" name="realmForm" novalidate>
|
<form class="form-horizontal" name="realmForm" novalidate>
|
||||||
<fieldset class="border-top">
|
<fieldset class="border-top">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -18,13 +18,6 @@
|
||||||
</div>
|
</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>
|
<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>
|
||||||
<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">
|
<div class="form-group clearfix">
|
||||||
<label class="col-sm-2 control-label" for="authorizationUrl">Authorization Url <span class="required">*</span></label>
|
<label class="col-sm-2 control-label" for="authorizationUrl">Authorization Url <span class="required">*</span></label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
|
|
|
@ -5,26 +5,19 @@
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
|
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
|
||||||
<li class="active">{{identityProvider.name}} Provider Settings</li>
|
<li class="active">{{identityProvider.alias}} Provider Settings</li>
|
||||||
</ol>
|
</ol>
|
||||||
<h2 class="pull-left">{{identityProvider.name}} Provider Settings</h2>
|
<h2 class="pull-left">{{identityProvider.alias}} Provider Settings</h2>
|
||||||
<p class="subtitle"><span class="required">*</span> Required fields</p>
|
<p class="subtitle"><span class="required">*</span> Required fields</p>
|
||||||
<form class="form-horizontal" name="realmForm" novalidate>
|
<form class="form-horizontal" name="realmForm" novalidate>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="form-group clearfix">
|
<div class="form-group clearfix">
|
||||||
<label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label>
|
<label class="col-sm-2 control-label" for="identifier">Alias <span class="required">*</span></label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<input class="form-control" id="identifier" type="text" ng-model="identityProvider.id" data-ng-readonly="!newIdentityProvider" required>
|
<input class="form-control" id="identifier" type="text" ng-model="identityProvider.alias" data-ng-readonly="!newIdentityProvider" required>
|
||||||
</div>
|
</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>
|
<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>
|
||||||
<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 && !importFile">
|
<div class="form-group" data-ng-show="newIdentityProvider && !importFile">
|
||||||
<label class="col-sm-2 control-label" for="fromUrl">Import From Url</label>
|
<label class="col-sm-2 control-label" for="fromUrl">Import From Url</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
|
@ -51,18 +44,19 @@
|
||||||
</div>
|
</div>
|
||||||
<span tooltip-placement="right" tooltip="The Url that must be used to send authentication requests(SAML AuthnRequest)." class="fa fa-info-circle"></span>
|
<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>
|
||||||
|
<div class="form-group clearfix" data-ng-show="!importFile && !importUrl">
|
||||||
|
<label class="col-sm-2 control-label" for="singleSignOnServiceUrl">Single Logout Service Url</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input class="form-control" id="singleLogoutServiceUrl" type="text" ng-model="identityProvider.config.singleLogoutServiceUrl" required>
|
||||||
|
</div>
|
||||||
|
<span tooltip-placement="right" tooltip="The Url that must be used to send logout requests." class="fa fa-info-circle"></span>
|
||||||
|
</div>
|
||||||
<div class="form-group clearfix" data-ng-show="!importFile && !importUrl">
|
<div class="form-group clearfix" data-ng-show="!importFile && !importUrl">
|
||||||
<label class="col-sm-2 control-label" for="nameIDPolicyFormat">NameID Policy Format</label>
|
<label class="col-sm-2 control-label" for="nameIDPolicyFormat">NameID Policy Format</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<select id="nameIDPolicyFormat" ng-model="identityProvider.config.nameIDPolicyFormat">
|
<select id="nameIDPolicyFormat" ng-model="identityProvider.config.nameIDPolicyFormat"
|
||||||
<option value="urn:oasis:names:tc:SAML:2.0:nameid-format:transient">Transient</option>
|
ng-options="nameFormat.format as nameFormat.name for nameFormat in nameIdFormats">
|
||||||
<option value="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">Persistent</option>
|
</select>
|
||||||
<option value="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">Email</option>
|
|
||||||
<option value="urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos">Kerberos</option>
|
|
||||||
<option value="urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName">X.509 Subject Name</option>
|
|
||||||
<option value="urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName">Windows Domain Qualified Name</option>
|
|
||||||
<option value="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">Unspecified</option>
|
|
||||||
</select>
|
|
||||||
<!-- <input class="form-control" id="nameIDPolicyFormat" type="text" ng-model="identityProvider.config.nameIDPolicyFormat"> -->
|
<!-- <input class="form-control" id="nameIDPolicyFormat" type="text" ng-model="identityProvider.config.nameIDPolicyFormat"> -->
|
||||||
</div>
|
</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>
|
<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>
|
||||||
|
@ -77,35 +71,35 @@
|
||||||
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
||||||
<label class="col-sm-2 control-label" for="wantAuthnRequestsSigned">Want AuthnRequests Signed</label>
|
<label class="col-sm-2 control-label" for="wantAuthnRequestsSigned">Want AuthnRequests Signed</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<input ng-model="identityProvider.config.wantAuthnRequestsSigned" id="wantAuthnRequestsSigned" onoffswitch />
|
<input ng-model="identityProvider.config.wantAuthnRequestsSigned" id="wantAuthnRequestsSigned" value="'true'" onoffswitchmodel />
|
||||||
</div>
|
</div>
|
||||||
<span tooltip-placement="right" tooltip=" Indicates whether the identity provider expects signed a AuthnRequest." class="fa fa-info-circle"></span>
|
<span tooltip-placement="right" tooltip=" Indicates whether the identity provider expects signed a AuthnRequest." class="fa fa-info-circle"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
||||||
<label class="col-sm-2 control-label" for="forceAuthn">Force Authentication</label>
|
<label class="col-sm-2 control-label" for="forceAuthn">Force Authentication</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<input ng-model="identityProvider.config.forceAuthn" id="forceAuthn" onoffswitch />
|
<input ng-model="identityProvider.config.forceAuthn" id="forceAuthn" value="'true'" onoffswitchmodel />
|
||||||
</div>
|
</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>
|
<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>
|
||||||
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
||||||
<label class="col-sm-2 control-label" for="validateSignature">Validate Signature</label>
|
<label class="col-sm-2 control-label" for="validateSignature">Validate Signature</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<input ng-model="identityProvider.config.validateSignature" id="validateSignature" onoffswitch />
|
<input ng-model="identityProvider.config.validateSignature" id="validateSignature" value="'true'" onoffswitchmodel />
|
||||||
</div>
|
</div>
|
||||||
<span tooltip-placement="right" tooltip="Enable/disable signature validation of SAML responses." class="fa fa-info-circle"></span>
|
<span tooltip-placement="right" tooltip="Enable/disable signature validation of SAML responses." class="fa fa-info-circle"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
||||||
<label class="col-sm-2 control-label" for="postBindingResponse">HTTP-POST Binding Response</label>
|
<label class="col-sm-2 control-label" for="postBindingResponse">HTTP-POST Binding Response</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<input ng-model="identityProvider.config.postBindingResponse" id="postBindingResponse" onoffswitch />
|
<input ng-model="identityProvider.config.postBindingResponse" id="postBindingResponse" value="'true'" onoffswitchmodel />
|
||||||
</div>
|
</div>
|
||||||
<span tooltip-placement="right" tooltip="Indicates whether the identity provider must respond to the AuthnRequest using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span>
|
<span tooltip-placement="right" tooltip="Indicates whether the identity provider must respond to the AuthnRequest using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
<div class="form-group" data-ng-show="!importFile && !importUrl">
|
||||||
<label class="col-sm-2 control-label" for="postBindingAuthnRequest">HTTP-POST Binding for AuthnRequest</label>
|
<label class="col-sm-2 control-label" for="postBindingAuthnRequest">HTTP-POST Binding for AuthnRequest</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<input ng-model="identityProvider.config.postBindingAuthnRequest" id="postBindingAuthnRequest" onoffswitch />
|
<input ng-model="identityProvider.config.postBindingAuthnRequest" id="postBindingAuthnRequest" value="'true'" onoffswitchmodel />
|
||||||
</div>
|
</div>
|
||||||
<span tooltip-placement="right" tooltip="Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span>
|
<span tooltip-placement="right" tooltip="Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -140,9 +134,9 @@
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="pull-right form-actions">
|
<div class="pull-right form-actions">
|
||||||
<a class="btn btn-lg btn-primary" href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.id}}/export" data-ng-show="!importFile && !newIdentityProvider">Export</a>
|
<a class="btn btn-lg btn-primary" href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}/export" data-ng-show="!importFile && !newIdentityProvider">Export</a>
|
||||||
<button kc-save data-ng-show="!importFile && !importUrl">Save</button>
|
<button kc-save data-ng-show="changed">Save</button>
|
||||||
<button type="submit" data-ng-click="clearFileSelect()" data-ng-show="importFile || importUrl" class="btn btn-lg btn-default">Cancel</button>
|
<button type="submit" data-ng-click="cancel()" data-ng-show="changed" 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</button>
|
<button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-lg btn-primary">Import</button>
|
||||||
<button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-lg btn-primary">Import</button>
|
<button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-lg btn-primary">Import</button>
|
||||||
<button kc-delete data-ng-click="remove()" data-ng-show="!newIdentityProvider">Delete</button>
|
<button kc-delete data-ng-click="remove()" data-ng-show="!newIdentityProvider">Delete</button>
|
||||||
|
|
|
@ -5,26 +5,12 @@
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Social Providers</a></li>
|
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Social Providers</a></li>
|
||||||
<li class="active">{{identityProvider.name}} Settings</li>
|
<li class="active">{{identityProvider.alias}} Settings</li>
|
||||||
</ol>
|
</ol>
|
||||||
<h2 class="pull-left">{{identityProvider.name}} Provider Settings</h2>
|
<h2 class="pull-left">{{identityProvider.alias}} Provider Settings</h2>
|
||||||
<p class="subtitle"><span class="required">*</span> Required fields</p>
|
<p class="subtitle"><span class="required">*</span> Required fields</p>
|
||||||
<form class="form-horizontal" name="realmForm" novalidate>
|
<form class="form-horizontal" name="realmForm" novalidate>
|
||||||
<fieldset>
|
<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">
|
<div class="form-group clearfix">
|
||||||
<label class="col-sm-2 control-label" for="clientId">Client ID <span class="required">*</span></label>
|
<label class="col-sm-2 control-label" for="clientId">Client ID <span class="required">*</span></label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
<h2></h2>
|
<h2></h2>
|
||||||
<div id="content">
|
<div id="content">
|
||||||
<h2 class="pull-left"><span>{{realm.realm}}</span> Identity Providers Settings</h2>
|
<h2 class="pull-left"><span>{{realm.realm}}</span> Identity Providers Settings</h2>
|
||||||
<p class="subtitle"><span class="required">*</span> Required fields</p>
|
|
||||||
|
|
||||||
<form name="realmForm" novalidate class="form-horizontal">
|
<form name="realmForm" novalidate class="form-horizontal">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
@ -18,7 +17,6 @@
|
||||||
<div class="select-kc">
|
<div class="select-kc">
|
||||||
<select ng-model="provider"
|
<select ng-model="provider"
|
||||||
ng-options="p.name group by p.groupName for p in allProviders track by p.id"
|
ng-options="p.name group by p.groupName for p in allProviders track by p.id"
|
||||||
ng-
|
|
||||||
data-ng-change="addProvider(provider); provider = null">
|
data-ng-change="addProvider(provider); provider = null">
|
||||||
<option value="" disabled selected>Add provider...</option>
|
<option value="" disabled selected>Add provider...</option>
|
||||||
</select>
|
</select>
|
||||||
|
@ -28,27 +26,22 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-show="configuredProviders.length > 0">
|
<tr ng-show="configuredProviders.length > 0">
|
||||||
<th>Name</th>
|
<th>Name</th>
|
||||||
<th>Redirect URI</th>
|
<th>Callback URI</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody ng-show="configuredProviders.length > 0">
|
<tbody ng-show="configuredProviders.length > 0">
|
||||||
<tr ng-repeat="identityProvider in configuredProviders">
|
<tr ng-repeat="identityProvider in configuredProviders">
|
||||||
<td>
|
<td>
|
||||||
<a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.id}}">{{identityProvider.name}}</a>
|
<a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a>
|
||||||
</td>
|
</td>
|
||||||
<td ng-show="!changed">
|
<td>
|
||||||
{{callbackUrl}}{{identityProvider.id}}
|
{{callbackUrl}}{{identityProvider.alias}}/endpoint
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="pull-right form-actions">
|
|
||||||
<button type="submit" kc-reset data-ng-show="changed">Clear changes</button>
|
|
||||||
<button type="submit" kc-save class="primary" data-ng-show="changed">Save changes</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<ul data-ng-hide="createRealm">
|
<ul data-ng-hide="createRealm">
|
||||||
<li data-ng-show="access.viewRealm" data-ng-class="((!path[2] ||
|
<li data-ng-show="access.viewRealm" data-ng-class="((!path[2] ||
|
||||||
path[2] == 'social-settings' || path[2] == 'required-credentials'
|
path[2] == 'identity-provider-settings' || path[2] == 'required-credentials'
|
||||||
|| path[2] == 'login-settings'
|
|| path[2] == 'login-settings'
|
||||||
|| path[2] == 'theme-settings'
|
|| path[2] == 'theme-settings'
|
||||||
|| path[2] == 'cache-settings'
|
|| path[2] == 'cache-settings'
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
<div id="kc-social-providers">
|
<div id="kc-social-providers">
|
||||||
<ul>
|
<ul>
|
||||||
<#list social.providers as p>
|
<#list social.providers as p>
|
||||||
<li><a href="${p.loginUrl}" id="zocial-${p.id}" class="zocial ${p.providerId}"> <span class="text">${p.name}</span></a></li>
|
<li><a href="${p.loginUrl}" id="zocial-${p.alias}" class="zocial ${p.providerId}"> <span class="text">${p.alias}</span></a></li>
|
||||||
</#list>
|
</#list>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -64,8 +64,8 @@ public class IdentityProviderBean {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addIdentityProvider(RealmModel realm, URI baseURI, IdentityProviderModel identityProvider) {
|
private void addIdentityProvider(RealmModel realm, URI baseURI, IdentityProviderModel identityProvider) {
|
||||||
String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider.getId(), realm.getName()).toString();
|
String loginUrl = Urls.identityProviderAuthnRequest(baseURI, identityProvider.getAlias(), realm.getName()).toString();
|
||||||
providers.add(new IdentityProvider(identityProvider.getId(), identityProvider.getProviderId(), identityProvider.getName(), loginUrl));
|
providers.add(new IdentityProvider(identityProvider.getAlias(), identityProvider.getProviderId(), loginUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IdentityProvider> getProviders() {
|
public List<IdentityProvider> getProviders() {
|
||||||
|
@ -78,29 +78,19 @@ public class IdentityProviderBean {
|
||||||
|
|
||||||
public static class IdentityProvider {
|
public static class IdentityProvider {
|
||||||
|
|
||||||
private final String id;
|
private final String alias;
|
||||||
private final String providerId; // This refer to providerType (facebook, google, etc.)
|
private final String providerId; // This refer to providerType (facebook, google, etc.)
|
||||||
private final String name;
|
|
||||||
private final String loginUrl;
|
private final String loginUrl;
|
||||||
|
|
||||||
public IdentityProvider(String id, String providerId, String name, String loginUrl) {
|
public IdentityProvider(String alias, String providerId,String loginUrl) {
|
||||||
this.id = id;
|
this.alias = alias;
|
||||||
this.providerId = providerId;
|
this.providerId = providerId;
|
||||||
|
|
||||||
if (name == null) {
|
|
||||||
name = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.name = name;
|
|
||||||
this.loginUrl = loginUrl;
|
this.loginUrl = loginUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getAlias() {
|
||||||
return id;
|
return alias;
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLoginUrl() {
|
public String getLoginUrl() {
|
||||||
|
|
|
@ -20,10 +20,12 @@ public interface IdentityProvidersResource {
|
||||||
IdentityProviderResource get(@PathParam("id") String id);
|
IdentityProviderResource get(@PathParam("id") String id);
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@Path("instances")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
List<IdentityProviderRepresentation> findAll();
|
List<IdentityProviderRepresentation> findAll();
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
|
@Path("instances")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
void create(IdentityProviderRepresentation identityProvider);
|
void create(IdentityProviderRepresentation identityProvider);
|
||||||
}
|
}
|
||||||
|
|
26
model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
Normal file → Executable file
26
model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
Normal file → Executable file
|
@ -33,7 +33,7 @@ public class IdentityProviderModel {
|
||||||
/**
|
/**
|
||||||
* <p>An user-defined identifier to unique identify an identity provider instance.</p>
|
* <p>An user-defined identifier to unique identify an identity provider instance.</p>
|
||||||
*/
|
*/
|
||||||
private String id;
|
private String alias;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>An identifier used to reference a specific identity provider implementation. The value of this field is the same
|
* <p>An identifier used to reference a specific identity provider implementation. The value of this field is the same
|
||||||
|
@ -41,11 +41,6 @@ public class IdentityProviderModel {
|
||||||
*/
|
*/
|
||||||
private String providerId;
|
private String providerId;
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>An user-defined friendly name for an identity provider instance.</p>
|
|
||||||
*/
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
|
||||||
private boolean updateProfileFirstLogin = true;
|
private boolean updateProfileFirstLogin = true;
|
||||||
|
@ -69,8 +64,7 @@ public class IdentityProviderModel {
|
||||||
public IdentityProviderModel(IdentityProviderModel model) {
|
public IdentityProviderModel(IdentityProviderModel model) {
|
||||||
this.internalId = model.getInternalId();
|
this.internalId = model.getInternalId();
|
||||||
this.providerId = model.getProviderId();
|
this.providerId = model.getProviderId();
|
||||||
this.id = model.getId();
|
this.alias = model.getAlias();
|
||||||
this.name = model.getName();
|
|
||||||
this.config = new HashMap<String, String>(model.getConfig());
|
this.config = new HashMap<String, String>(model.getConfig());
|
||||||
this.enabled = model.isEnabled();
|
this.enabled = model.isEnabled();
|
||||||
this.updateProfileFirstLogin = model.isUpdateProfileFirstLogin();
|
this.updateProfileFirstLogin = model.isUpdateProfileFirstLogin();
|
||||||
|
@ -86,12 +80,12 @@ public class IdentityProviderModel {
|
||||||
this.internalId = internalId;
|
this.internalId = internalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getAlias() {
|
||||||
return this.id;
|
return this.alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(String id) {
|
public void setAlias(String id) {
|
||||||
this.id = id;
|
this.alias = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getProviderId() {
|
public String getProviderId() {
|
||||||
|
@ -102,14 +96,6 @@ public class IdentityProviderModel {
|
||||||
this.providerId = providerId;
|
this.providerId = providerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return this.enabled;
|
return this.enabled;
|
||||||
}
|
}
|
||||||
|
|
|
@ -185,9 +185,9 @@ public interface RealmModel extends RoleContainerModel {
|
||||||
void setSmtpConfig(Map<String, String> smtpConfig);
|
void setSmtpConfig(Map<String, String> smtpConfig);
|
||||||
|
|
||||||
List<IdentityProviderModel> getIdentityProviders();
|
List<IdentityProviderModel> getIdentityProviders();
|
||||||
IdentityProviderModel getIdentityProviderById(String identityProviderId);
|
IdentityProviderModel getIdentityProviderByAlias(String alias);
|
||||||
void addIdentityProvider(IdentityProviderModel identityProvider);
|
void addIdentityProvider(IdentityProviderModel identityProvider);
|
||||||
void removeIdentityProviderById(String providerId);
|
void removeIdentityProviderByAlias(String alias);
|
||||||
void updateIdentityProvider(IdentityProviderModel identityProvider);
|
void updateIdentityProvider(IdentityProviderModel identityProvider);
|
||||||
|
|
||||||
List<UserFederationProviderModel> getUserFederationProviders();
|
List<UserFederationProviderModel> getUserFederationProviders();
|
||||||
|
|
10
model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
Normal file → Executable file
10
model/api/src/main/java/org/keycloak/models/entities/IdentityProviderEntity.java
Normal file → Executable file
|
@ -26,7 +26,7 @@ import java.util.Map;
|
||||||
public class IdentityProviderEntity {
|
public class IdentityProviderEntity {
|
||||||
|
|
||||||
private String internalId;
|
private String internalId;
|
||||||
private String id;
|
private String alias;
|
||||||
private String providerId;
|
private String providerId;
|
||||||
private String name;
|
private String name;
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
@ -92,12 +92,12 @@ public class IdentityProviderEntity {
|
||||||
this.providerId = providerId;
|
this.providerId = providerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getAlias() {
|
||||||
return id;
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(String providerNonimalId) {
|
public void setAlias(String alias) {
|
||||||
this.id = providerNonimalId;
|
this.alias = alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getConfig() {
|
public Map<String, String> getConfig() {
|
||||||
|
|
|
@ -353,8 +353,7 @@ public class ModelToRepresentation {
|
||||||
|
|
||||||
providerRep.setInternalId(identityProviderModel.getInternalId());
|
providerRep.setInternalId(identityProviderModel.getInternalId());
|
||||||
providerRep.setProviderId(identityProviderModel.getProviderId());
|
providerRep.setProviderId(identityProviderModel.getProviderId());
|
||||||
providerRep.setId(identityProviderModel.getId());
|
providerRep.setAlias(identityProviderModel.getAlias());
|
||||||
providerRep.setName(identityProviderModel.getName());
|
|
||||||
providerRep.setEnabled(identityProviderModel.isEnabled());
|
providerRep.setEnabled(identityProviderModel.isEnabled());
|
||||||
providerRep.setStoreToken(identityProviderModel.isStoreToken());
|
providerRep.setStoreToken(identityProviderModel.isStoreToken());
|
||||||
providerRep.setUpdateProfileFirstLogin(identityProviderModel.isUpdateProfileFirstLogin());
|
providerRep.setUpdateProfileFirstLogin(identityProviderModel.isUpdateProfileFirstLogin());
|
||||||
|
|
|
@ -272,9 +272,8 @@ public class RepresentationToModel {
|
||||||
String secret = rep.getSocialProviders().get(k.replace(".key", ".secret"));
|
String secret = rep.getSocialProviders().get(k.replace(".key", ".secret"));
|
||||||
|
|
||||||
IdentityProviderRepresentation identityProvider = new IdentityProviderRepresentation();
|
IdentityProviderRepresentation identityProvider = new IdentityProviderRepresentation();
|
||||||
identityProvider.setId(providerId);
|
identityProvider.setAlias(providerId);
|
||||||
identityProvider.setProviderId(providerId);
|
identityProvider.setProviderId(providerId);
|
||||||
identityProvider.setName(providerId);
|
|
||||||
identityProvider.setEnabled(true);
|
identityProvider.setEnabled(true);
|
||||||
identityProvider.setUpdateProfileFirstLogin(updateProfileFirstLogin);
|
identityProvider.setUpdateProfileFirstLogin(updateProfileFirstLogin);
|
||||||
|
|
||||||
|
@ -850,9 +849,8 @@ public class RepresentationToModel {
|
||||||
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
||||||
|
|
||||||
identityProviderModel.setInternalId(representation.getInternalId());
|
identityProviderModel.setInternalId(representation.getInternalId());
|
||||||
identityProviderModel.setId(representation.getId());
|
identityProviderModel.setAlias(representation.getAlias());
|
||||||
identityProviderModel.setProviderId(representation.getProviderId());
|
identityProviderModel.setProviderId(representation.getProviderId());
|
||||||
identityProviderModel.setName(representation.getName());
|
|
||||||
identityProviderModel.setEnabled(representation.isEnabled());
|
identityProviderModel.setEnabled(representation.isEnabled());
|
||||||
identityProviderModel.setUpdateProfileFirstLogin(representation.isUpdateProfileFirstLogin());
|
identityProviderModel.setUpdateProfileFirstLogin(representation.isUpdateProfileFirstLogin());
|
||||||
identityProviderModel.setAuthenticateByDefault(representation.isAuthenticateByDefault());
|
identityProviderModel.setAuthenticateByDefault(representation.isAuthenticateByDefault());
|
||||||
|
|
|
@ -871,9 +871,9 @@ public class RealmAdapter implements RealmModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentityProviderModel getIdentityProviderById(String identityProviderId) {
|
public IdentityProviderModel getIdentityProviderByAlias(String alias) {
|
||||||
for (IdentityProviderModel identityProviderModel : getIdentityProviders()) {
|
for (IdentityProviderModel identityProviderModel : getIdentityProviders()) {
|
||||||
if (identityProviderModel.getId().equals(identityProviderId)) {
|
if (identityProviderModel.getAlias().equals(alias)) {
|
||||||
return identityProviderModel;
|
return identityProviderModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -883,15 +883,15 @@ public class RealmAdapter implements RealmModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addIdentityProvider(IdentityProviderModel identityProvider) {
|
public void addIdentityProvider(IdentityProviderModel identityProvider) {
|
||||||
if (identityProvider.getId() == null) throw new NullPointerException("identityProvider.getId() == null");
|
if (identityProvider.getAlias() == null) throw new NullPointerException("identityProvider.getAlias() == null");
|
||||||
if (identityProvider.getInternalId() == null) identityProvider.setInternalId(KeycloakModelUtils.generateId());
|
if (identityProvider.getInternalId() == null) identityProvider.setInternalId(KeycloakModelUtils.generateId());
|
||||||
allIdProviders.put(identityProvider.getInternalId(), identityProvider);
|
allIdProviders.put(identityProvider.getInternalId(), identityProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeIdentityProviderById(String providerId) {
|
public void removeIdentityProviderByAlias(String alias) {
|
||||||
for (IdentityProviderModel provider : getIdentityProviders()) {
|
for (IdentityProviderModel provider : getIdentityProviders()) {
|
||||||
if (provider.getId().equals(providerId)) {
|
if (provider.getAlias().equals(alias)) {
|
||||||
allIdProviders.remove(provider.getInternalId());
|
allIdProviders.remove(provider.getInternalId());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -900,7 +900,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateIdentityProvider(IdentityProviderModel identityProvider) {
|
public void updateIdentityProvider(IdentityProviderModel identityProvider) {
|
||||||
removeIdentityProviderById(identityProvider.getId());
|
removeIdentityProviderByAlias(identityProvider.getAlias());
|
||||||
addIdentityProvider(identityProvider);
|
addIdentityProvider(identityProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -643,9 +643,9 @@ public class RealmAdapter implements RealmModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentityProviderModel getIdentityProviderById(String identityProviderId) {
|
public IdentityProviderModel getIdentityProviderByAlias(String alias) {
|
||||||
for (IdentityProviderModel identityProviderModel : getIdentityProviders()) {
|
for (IdentityProviderModel identityProviderModel : getIdentityProviders()) {
|
||||||
if (identityProviderModel.getId().equals(identityProviderId)) {
|
if (identityProviderModel.getAlias().equals(alias)) {
|
||||||
return identityProviderModel;
|
return identityProviderModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -666,9 +666,9 @@ public class RealmAdapter implements RealmModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeIdentityProviderById(String providerId) {
|
public void removeIdentityProviderByAlias(String alias) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
updated.removeIdentityProviderById(providerId);
|
updated.removeIdentityProviderByAlias(alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -317,7 +317,7 @@ public abstract class ClientAdapter implements ClientModel {
|
||||||
boolean toRemove = true;
|
boolean toRemove = true;
|
||||||
|
|
||||||
for (ClientIdentityProviderMappingModel model : identityProviders) {
|
for (ClientIdentityProviderMappingModel model : identityProviders) {
|
||||||
if (model.getIdentityProvider().equals(identityProvider.getId())) {
|
if (model.getIdentityProvider().equals(identityProvider.getAlias())) {
|
||||||
toRemove = false;
|
toRemove = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -326,7 +326,7 @@ public abstract class ClientAdapter implements ClientModel {
|
||||||
if (toRemove) {
|
if (toRemove) {
|
||||||
remove.add(entity);
|
remove.add(entity);
|
||||||
} else {
|
} else {
|
||||||
already.add(entity.getIdentityProvider().getId());
|
already.add(entity.getIdentityProvider().getAlias());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (ClientIdentityProviderMappingEntity entity : remove) {
|
for (ClientIdentityProviderMappingEntity entity : remove) {
|
||||||
|
@ -342,14 +342,14 @@ public abstract class ClientAdapter implements ClientModel {
|
||||||
entities.add(mappingEntity);
|
entities.add(mappingEntity);
|
||||||
} else {
|
} else {
|
||||||
for (ClientIdentityProviderMappingEntity entity : entities) {
|
for (ClientIdentityProviderMappingEntity entity : entities) {
|
||||||
if (entity.getIdentityProvider().getId().equals(model.getIdentityProvider())) {
|
if (entity.getIdentityProvider().getAlias().equals(model.getIdentityProvider())) {
|
||||||
mappingEntity = entity;
|
mappingEntity = entity;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedQuery<IdentityProviderEntity> query = em.createNamedQuery("findIdentityProviderById", IdentityProviderEntity.class).setParameter("id", model.getIdentityProvider());
|
TypedQuery<IdentityProviderEntity> query = em.createNamedQuery("findIdentityProviderByAlias", IdentityProviderEntity.class).setParameter("alias", model.getIdentityProvider());
|
||||||
IdentityProviderEntity identityProviderEntity = query.getSingleResult();
|
IdentityProviderEntity identityProviderEntity = query.getSingleResult();
|
||||||
|
|
||||||
mappingEntity.setIdentityProvider(identityProviderEntity);
|
mappingEntity.setIdentityProvider(identityProviderEntity);
|
||||||
|
@ -368,7 +368,7 @@ public abstract class ClientAdapter implements ClientModel {
|
||||||
for (ClientIdentityProviderMappingEntity entity : this.entity.getIdentityProviders()) {
|
for (ClientIdentityProviderMappingEntity entity : this.entity.getIdentityProviders()) {
|
||||||
ClientIdentityProviderMappingModel model = new ClientIdentityProviderMappingModel();
|
ClientIdentityProviderMappingModel model = new ClientIdentityProviderMappingModel();
|
||||||
|
|
||||||
model.setIdentityProvider(entity.getIdentityProvider().getId());
|
model.setIdentityProvider(entity.getIdentityProvider().getAlias());
|
||||||
model.setRetrieveToken(entity.isRetrieveToken());
|
model.setRetrieveToken(entity.isRetrieveToken());
|
||||||
|
|
||||||
models.add(model);
|
models.add(model);
|
||||||
|
|
|
@ -1160,9 +1160,8 @@ public class RealmAdapter implements RealmModel {
|
||||||
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
||||||
|
|
||||||
identityProviderModel.setProviderId(entity.getProviderId());
|
identityProviderModel.setProviderId(entity.getProviderId());
|
||||||
identityProviderModel.setId(entity.getId());
|
identityProviderModel.setAlias(entity.getAlias());
|
||||||
identityProviderModel.setInternalId(entity.getInternalId());
|
identityProviderModel.setInternalId(entity.getInternalId());
|
||||||
identityProviderModel.setName(entity.getName());
|
|
||||||
identityProviderModel.setConfig(entity.getConfig());
|
identityProviderModel.setConfig(entity.getConfig());
|
||||||
identityProviderModel.setEnabled(entity.isEnabled());
|
identityProviderModel.setEnabled(entity.isEnabled());
|
||||||
identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
|
identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
|
||||||
|
@ -1176,9 +1175,9 @@ public class RealmAdapter implements RealmModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentityProviderModel getIdentityProviderById(String identityProviderId) {
|
public IdentityProviderModel getIdentityProviderByAlias(String alias) {
|
||||||
for (IdentityProviderModel identityProviderModel : getIdentityProviders()) {
|
for (IdentityProviderModel identityProviderModel : getIdentityProviders()) {
|
||||||
if (identityProviderModel.getId().equals(identityProviderId)) {
|
if (identityProviderModel.getAlias().equals(alias)) {
|
||||||
return identityProviderModel;
|
return identityProviderModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1191,9 +1190,8 @@ public class RealmAdapter implements RealmModel {
|
||||||
IdentityProviderEntity entity = new IdentityProviderEntity();
|
IdentityProviderEntity entity = new IdentityProviderEntity();
|
||||||
|
|
||||||
entity.setInternalId(KeycloakModelUtils.generateId());
|
entity.setInternalId(KeycloakModelUtils.generateId());
|
||||||
entity.setId(identityProvider.getId());
|
entity.setAlias(identityProvider.getAlias());
|
||||||
entity.setProviderId(identityProvider.getProviderId());
|
entity.setProviderId(identityProvider.getProviderId());
|
||||||
entity.setName(identityProvider.getName());
|
|
||||||
entity.setEnabled(identityProvider.isEnabled());
|
entity.setEnabled(identityProvider.isEnabled());
|
||||||
entity.setStoreToken(identityProvider.isStoreToken());
|
entity.setStoreToken(identityProvider.isStoreToken());
|
||||||
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
||||||
|
@ -1207,9 +1205,9 @@ public class RealmAdapter implements RealmModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeIdentityProviderById(String providerId) {
|
public void removeIdentityProviderByAlias(String alias) {
|
||||||
for (IdentityProviderEntity entity : realm.getIdentityProviders()) {
|
for (IdentityProviderEntity entity : realm.getIdentityProviders()) {
|
||||||
if (entity.getId().equals(providerId)) {
|
if (entity.getAlias().equals(alias)) {
|
||||||
em.remove(entity);
|
em.remove(entity);
|
||||||
em.flush();
|
em.flush();
|
||||||
}
|
}
|
||||||
|
@ -1220,8 +1218,7 @@ public class RealmAdapter implements RealmModel {
|
||||||
public void updateIdentityProvider(IdentityProviderModel identityProvider) {
|
public void updateIdentityProvider(IdentityProviderModel identityProvider) {
|
||||||
for (IdentityProviderEntity entity : this.realm.getIdentityProviders()) {
|
for (IdentityProviderEntity entity : this.realm.getIdentityProviders()) {
|
||||||
if (entity.getInternalId().equals(identityProvider.getInternalId())) {
|
if (entity.getInternalId().equals(identityProvider.getInternalId())) {
|
||||||
entity.setId(identityProvider.getId());
|
entity.setAlias(identityProvider.getAlias());
|
||||||
entity.setName(identityProvider.getName());
|
|
||||||
entity.setEnabled(identityProvider.isEnabled());
|
entity.setEnabled(identityProvider.isEnabled());
|
||||||
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
||||||
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
|
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
|
||||||
|
|
|
@ -83,7 +83,7 @@ public class ClientIdentityProviderMappingEntity {
|
||||||
|
|
||||||
Key key = (Key) o;
|
Key key = (Key) o;
|
||||||
|
|
||||||
if (identityProvider != null ? !identityProvider.getId().equals(key.identityProvider.getId()) : key.identityProvider != null)
|
if (identityProvider != null ? !identityProvider.getAlias().equals(key.identityProvider.getAlias()) : key.identityProvider != null)
|
||||||
return false;
|
return false;
|
||||||
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ public class ClientIdentityProviderMappingEntity {
|
||||||
|
|
||||||
ClientIdentityProviderMappingEntity key = (ClientIdentityProviderMappingEntity) o;
|
ClientIdentityProviderMappingEntity key = (ClientIdentityProviderMappingEntity) o;
|
||||||
|
|
||||||
if (identityProvider != null ? !identityProvider.getId().equals(key.identityProvider.getId()) : key.identityProvider != null)
|
if (identityProvider != null ? !identityProvider.getAlias().equals(key.identityProvider.getAlias()) : key.identityProvider != null)
|
||||||
return false;
|
return false;
|
||||||
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
if (client != null ? !client.getId().equals(key.client != null ? key.client.getId() : null) : key.client != null) return false;
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ import java.util.Map;
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name="IDENTITY_PROVIDER")
|
@Table(name="IDENTITY_PROVIDER")
|
||||||
@NamedQueries({
|
@NamedQueries({
|
||||||
@NamedQuery(name="findIdentityProviderById", query="select identityProvider from IdentityProviderEntity identityProvider where identityProvider.id = :id")
|
@NamedQuery(name="findIdentityProviderByAlias", query="select identityProvider from IdentityProviderEntity identityProvider where identityProvider.alias = :alias")
|
||||||
})
|
})
|
||||||
public class IdentityProviderEntity {
|
public class IdentityProviderEntity {
|
||||||
|
|
||||||
|
@ -35,11 +35,8 @@ public class IdentityProviderEntity {
|
||||||
@Column(name="PROVIDER_ID")
|
@Column(name="PROVIDER_ID")
|
||||||
private String providerId;
|
private String providerId;
|
||||||
|
|
||||||
@Column(name="PROVIDER_NONIMAL_ID")
|
@Column(name="PROVIDER_ALIAS")
|
||||||
private String id;
|
private String alias;
|
||||||
|
|
||||||
@Column(name="PROVIDER_NAME")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@Column(name="ENABLED")
|
@Column(name="ENABLED")
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
@ -83,20 +80,12 @@ public class IdentityProviderEntity {
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getAlias() {
|
||||||
return this.id;
|
return this.alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(String id) {
|
public void setAlias(String alias) {
|
||||||
this.id = id;
|
this.alias = alias;
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
|
||||||
this.name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
|
|
|
@ -833,9 +833,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
||||||
|
|
||||||
identityProviderModel.setProviderId(entity.getProviderId());
|
identityProviderModel.setProviderId(entity.getProviderId());
|
||||||
identityProviderModel.setId(entity.getId());
|
identityProviderModel.setAlias(entity.getAlias());
|
||||||
identityProviderModel.setInternalId(entity.getInternalId());
|
identityProviderModel.setInternalId(entity.getInternalId());
|
||||||
identityProviderModel.setName(entity.getName());
|
|
||||||
identityProviderModel.setConfig(entity.getConfig());
|
identityProviderModel.setConfig(entity.getConfig());
|
||||||
identityProviderModel.setEnabled(entity.isEnabled());
|
identityProviderModel.setEnabled(entity.isEnabled());
|
||||||
identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
|
identityProviderModel.setUpdateProfileFirstLogin(entity.isUpdateProfileFirstLogin());
|
||||||
|
@ -849,9 +848,9 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentityProviderModel getIdentityProviderById(String identityProviderId) {
|
public IdentityProviderModel getIdentityProviderByAlias(String alias) {
|
||||||
for (IdentityProviderModel identityProviderModel : getIdentityProviders()) {
|
for (IdentityProviderModel identityProviderModel : getIdentityProviders()) {
|
||||||
if (identityProviderModel.getId().equals(identityProviderId)) {
|
if (identityProviderModel.getAlias().equals(alias)) {
|
||||||
return identityProviderModel;
|
return identityProviderModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -864,9 +863,8 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
IdentityProviderEntity entity = new IdentityProviderEntity();
|
IdentityProviderEntity entity = new IdentityProviderEntity();
|
||||||
|
|
||||||
entity.setInternalId(KeycloakModelUtils.generateId());
|
entity.setInternalId(KeycloakModelUtils.generateId());
|
||||||
entity.setId(identityProvider.getId());
|
entity.setAlias(identityProvider.getAlias());
|
||||||
entity.setProviderId(identityProvider.getProviderId());
|
entity.setProviderId(identityProvider.getProviderId());
|
||||||
entity.setName(identityProvider.getName());
|
|
||||||
entity.setEnabled(identityProvider.isEnabled());
|
entity.setEnabled(identityProvider.isEnabled());
|
||||||
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
||||||
entity.setStoreToken(identityProvider.isStoreToken());
|
entity.setStoreToken(identityProvider.isStoreToken());
|
||||||
|
@ -878,10 +876,10 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeIdentityProviderById(String providerId) {
|
public void removeIdentityProviderByAlias(String alias) {
|
||||||
IdentityProviderEntity toRemove;
|
IdentityProviderEntity toRemove;
|
||||||
for (IdentityProviderEntity entity : realm.getIdentityProviders()) {
|
for (IdentityProviderEntity entity : realm.getIdentityProviders()) {
|
||||||
if (entity.getId().equals(providerId)) {
|
if (entity.getAlias().equals(alias)) {
|
||||||
realm.getIdentityProviders().remove(entity);
|
realm.getIdentityProviders().remove(entity);
|
||||||
updateRealm();
|
updateRealm();
|
||||||
break;
|
break;
|
||||||
|
@ -893,8 +891,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
public void updateIdentityProvider(IdentityProviderModel identityProvider) {
|
public void updateIdentityProvider(IdentityProviderModel identityProvider) {
|
||||||
for (IdentityProviderEntity entity : this.realm.getIdentityProviders()) {
|
for (IdentityProviderEntity entity : this.realm.getIdentityProviders()) {
|
||||||
if (entity.getInternalId().equals(identityProvider.getInternalId())) {
|
if (entity.getInternalId().equals(identityProvider.getInternalId())) {
|
||||||
entity.setId(identityProvider.getId());
|
entity.setAlias(identityProvider.getAlias());
|
||||||
entity.setName(identityProvider.getName());
|
|
||||||
entity.setEnabled(identityProvider.isEnabled());
|
entity.setEnabled(identityProvider.isEnabled());
|
||||||
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
entity.setUpdateProfileFirstLogin(identityProvider.isUpdateProfileFirstLogin());
|
||||||
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
|
entity.setAuthenticateByDefault(identityProvider.isAuthenticateByDefault());
|
||||||
|
|
|
@ -43,8 +43,14 @@ public class SALM2LoginResponseBuilder {
|
||||||
protected String requestID;
|
protected String requestID;
|
||||||
protected String authMethod;
|
protected String authMethod;
|
||||||
protected String requestIssuer;
|
protected String requestIssuer;
|
||||||
|
protected String sessionIndex;
|
||||||
|
|
||||||
|
|
||||||
|
public SALM2LoginResponseBuilder sessionIndex(String sessionIndex) {
|
||||||
|
this.sessionIndex = sessionIndex;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public SALM2LoginResponseBuilder destination(String destination) {
|
public SALM2LoginResponseBuilder destination(String destination) {
|
||||||
this.destination = destination;
|
this.destination = destination;
|
||||||
return this;
|
return this;
|
||||||
|
@ -135,8 +141,8 @@ public class SALM2LoginResponseBuilder {
|
||||||
|
|
||||||
AuthnStatementType authnStatement = StatementUtil.createAuthnStatement(XMLTimeUtil.getIssueInstant(),
|
AuthnStatementType authnStatement = StatementUtil.createAuthnStatement(XMLTimeUtil.getIssueInstant(),
|
||||||
authContextRef);
|
authContextRef);
|
||||||
|
if (sessionIndex != null) authnStatement.setSessionIndex(sessionIndex);
|
||||||
authnStatement.setSessionIndex(assertion.getID());
|
else authnStatement.setSessionIndex(assertion.getID());
|
||||||
|
|
||||||
assertion.addStatement(authnStatement);
|
assertion.addStatement(authnStatement);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.net.URI;
|
||||||
public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRequestBuilder> {
|
public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRequestBuilder> {
|
||||||
protected String userPrincipal;
|
protected String userPrincipal;
|
||||||
protected String userPrincipalFormat;
|
protected String userPrincipalFormat;
|
||||||
|
protected String sessionIndex;
|
||||||
|
|
||||||
public SAML2LogoutRequestBuilder userPrincipal(String nameID, String nameIDformat) {
|
public SAML2LogoutRequestBuilder userPrincipal(String nameID, String nameIDformat) {
|
||||||
this.userPrincipal = nameID;
|
this.userPrincipal = nameID;
|
||||||
|
@ -26,6 +27,11 @@ public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRe
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SAML2LogoutRequestBuilder sessionIndex(String index) {
|
||||||
|
this.sessionIndex = index;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public RedirectBindingBuilder redirectBinding() throws ConfigurationException, ProcessingException, ParsingException {
|
public RedirectBindingBuilder redirectBinding() throws ConfigurationException, ProcessingException, ParsingException {
|
||||||
Document samlResponseDocument = buildDocument();
|
Document samlResponseDocument = buildDocument();
|
||||||
return new RedirectBindingBuilder(samlResponseDocument);
|
return new RedirectBindingBuilder(samlResponseDocument);
|
||||||
|
@ -58,7 +64,7 @@ public class SAML2LogoutRequestBuilder extends SAML2BindingBuilder<SAML2LogoutRe
|
||||||
issuerID.setValue(issuer);
|
issuerID.setValue(issuer);
|
||||||
lort.setIssuer(issuerID);
|
lort.setIssuer(issuerID);
|
||||||
}
|
}
|
||||||
|
if (sessionIndex != null) lort.addSessionIndex(sessionIndex);
|
||||||
|
|
||||||
long assertionValidity = PicketLinkCoreSTS.instance().getConfiguration().getIssuedTokenTimeout();
|
long assertionValidity = PicketLinkCoreSTS.instance().getConfiguration().getIssuedTokenTimeout();
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.client.ClientRequest;
|
import org.jboss.resteasy.client.ClientRequest;
|
||||||
import org.jboss.resteasy.client.ClientResponse;
|
import org.jboss.resteasy.client.ClientResponse;
|
||||||
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
|
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
|
@ -94,6 +95,8 @@ public class SamlProtocol implements LoginProtocol {
|
||||||
|
|
||||||
protected HttpHeaders headers;
|
protected HttpHeaders headers;
|
||||||
|
|
||||||
|
protected EventBuilder event;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SamlProtocol setSession(KeycloakSession session) {
|
public SamlProtocol setSession(KeycloakSession session) {
|
||||||
|
@ -119,6 +122,13 @@ public class SamlProtocol implements LoginProtocol {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SamlProtocol setEventBuilder(EventBuilder event) {
|
||||||
|
this.event = event;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response cancelLogin(ClientSessionModel clientSession) {
|
public Response cancelLogin(ClientSessionModel clientSession) {
|
||||||
return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
|
return getErrorResponse(clientSession, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.get());
|
||||||
|
@ -262,6 +272,7 @@ public class SamlProtocol implements LoginProtocol {
|
||||||
builder.requestID(requestID)
|
builder.requestID(requestID)
|
||||||
.destination(redirectUri)
|
.destination(redirectUri)
|
||||||
.issuer(responseIssuer)
|
.issuer(responseIssuer)
|
||||||
|
.sessionIndex(clientSession.getId())
|
||||||
.requestIssuer(clientSession.getClient().getClientId())
|
.requestIssuer(clientSession.getClient().getClientId())
|
||||||
.nameIdentifier(nameIdFormat, nameId)
|
.nameIdentifier(nameIdFormat, nameId)
|
||||||
.authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
|
.authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get());
|
||||||
|
@ -350,10 +361,6 @@ public class SamlProtocol implements LoginProtocol {
|
||||||
return "true".equals(client.getAttribute(SAML_AUTHNSTATEMENT));
|
return "true".equals(client.getAttribute(SAML_AUTHNSTATEMENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean multivaluedRoles(ClientModel client) {
|
|
||||||
return "true".equals(client.getAttribute(SAML_MULTIVALUED_ROLES));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SignatureAlgorithm getSignatureAlgorithm(ClientModel client) {
|
public static SignatureAlgorithm getSignatureAlgorithm(ClientModel client) {
|
||||||
String alg = client.getAttribute(SAML_SIGNATURE_ALGORITHM);
|
String alg = client.getAttribute(SAML_SIGNATURE_ALGORITHM);
|
||||||
if (alg != null) {
|
if (alg != null) {
|
||||||
|
@ -457,10 +464,13 @@ public class SamlProtocol implements LoginProtocol {
|
||||||
@Override
|
@Override
|
||||||
public Response finishLogout(UserSessionModel userSession) {
|
public Response finishLogout(UserSessionModel userSession) {
|
||||||
logger.debug("finishLogout");
|
logger.debug("finishLogout");
|
||||||
|
String logoutBindingUri = userSession.getNote(SAML_LOGOUT_BINDING_URI);
|
||||||
|
String logoutRelayState = userSession.getNote(SAML_LOGOUT_RELAY_STATE);
|
||||||
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
|
SAML2LogoutResponseBuilder builder = new SAML2LogoutResponseBuilder();
|
||||||
builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
|
builder.logoutRequestID(userSession.getNote(SAML_LOGOUT_REQUEST_ID));
|
||||||
builder.destination(userSession.getNote(SAML_LOGOUT_ISSUER));
|
builder.destination(logoutBindingUri);
|
||||||
builder.issuer(getResponseIssuer(realm));
|
builder.issuer(getResponseIssuer(realm));
|
||||||
|
builder.relayState(logoutRelayState);
|
||||||
String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
|
String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
|
||||||
if (signingAlgorithm != null) {
|
if (signingAlgorithm != null) {
|
||||||
SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
|
SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
|
||||||
|
@ -471,9 +481,9 @@ public class SamlProtocol implements LoginProtocol {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isLogoutPostBindingForInitiator(userSession)) {
|
if (isLogoutPostBindingForInitiator(userSession)) {
|
||||||
return builder.postBinding().response(userSession.getNote(SAML_LOGOUT_BINDING_URI));
|
return builder.postBinding().response(logoutBindingUri);
|
||||||
} else {
|
} else {
|
||||||
return builder.redirectBinding().response(userSession.getNote(SAML_LOGOUT_BINDING_URI));
|
return builder.redirectBinding().response(logoutBindingUri);
|
||||||
}
|
}
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|
|
@ -3,11 +3,17 @@ package org.keycloak.protocol.saml;
|
||||||
import org.keycloak.VerificationException;
|
import org.keycloak.VerificationException;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.util.PemUtils;
|
import org.keycloak.util.PemUtils;
|
||||||
|
import org.picketlink.common.constants.GeneralConstants;
|
||||||
import org.picketlink.common.exceptions.ProcessingException;
|
import org.picketlink.common.exceptions.ProcessingException;
|
||||||
import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature;
|
import org.picketlink.identity.federation.api.saml.v2.sig.SAML2Signature;
|
||||||
|
import org.picketlink.identity.federation.web.util.RedirectBindingUtil;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
import javax.ws.rs.core.UriBuilder;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
import java.security.Signature;
|
||||||
import java.security.cert.Certificate;
|
import java.security.cert.Certificate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,8 +26,12 @@ public class SamlProtocolUtils {
|
||||||
if (!"true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
|
if (!"true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
SAML2Signature saml2Signature = new SAML2Signature();
|
|
||||||
PublicKey publicKey = getSignatureValidationKey(client);
|
PublicKey publicKey = getSignatureValidationKey(client);
|
||||||
|
verifyDocumentSignature(document, publicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void verifyDocumentSignature(Document document, PublicKey publicKey) throws VerificationException {
|
||||||
|
SAML2Signature saml2Signature = new SAML2Signature();
|
||||||
try {
|
try {
|
||||||
if (!saml2Signature.validate(document, publicKey)) {
|
if (!saml2Signature.validate(document, publicKey)) {
|
||||||
throw new VerificationException("Invalid signature on document");
|
throw new VerificationException("Invalid signature on document");
|
||||||
|
@ -51,5 +61,43 @@ public class SamlProtocolUtils {
|
||||||
return cert.getPublicKey();
|
return cert.getPublicKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void verifyRedirectSignature(PublicKey publicKey, UriInfo uriInformation) throws VerificationException {
|
||||||
|
MultivaluedMap<String, String> encodedParams = uriInformation.getQueryParameters(false);
|
||||||
|
String request = encodedParams.getFirst(GeneralConstants.SAML_REQUEST_KEY);
|
||||||
|
String algorithm = encodedParams.getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
|
||||||
|
String signature = encodedParams.getFirst(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
|
||||||
|
String decodedAlgorithm = uriInformation.getQueryParameters(true).getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
|
||||||
|
|
||||||
|
if (request == null) throw new VerificationException("SAMLRequest as null");
|
||||||
|
if (algorithm == null) throw new VerificationException("SigAlg as null");
|
||||||
|
if (signature == null) throw new VerificationException("Signature as null");
|
||||||
|
|
||||||
|
// Shibboleth doesn't sign the document for redirect binding.
|
||||||
|
// todo maybe a flag?
|
||||||
|
|
||||||
|
|
||||||
|
UriBuilder builder = UriBuilder.fromPath("/")
|
||||||
|
.queryParam(GeneralConstants.SAML_REQUEST_KEY, request);
|
||||||
|
if (encodedParams.containsKey(GeneralConstants.RELAY_STATE)) {
|
||||||
|
builder.queryParam(GeneralConstants.RELAY_STATE, encodedParams.getFirst(GeneralConstants.RELAY_STATE));
|
||||||
|
}
|
||||||
|
builder.queryParam(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, algorithm);
|
||||||
|
String rawQuery = builder.build().getRawQuery();
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
|
||||||
|
|
||||||
|
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
|
||||||
|
Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
|
||||||
|
validator.initVerify(publicKey);
|
||||||
|
validator.update(rawQuery.getBytes("UTF-8"));
|
||||||
|
if (!validator.verify(decodedSignature)) {
|
||||||
|
throw new VerificationException("Invalid query param signature");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new VerificationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -358,7 +358,6 @@ public class SamlService {
|
||||||
if (relayState != null) userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState);
|
if (relayState != null) userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState);
|
||||||
userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID());
|
userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID());
|
||||||
userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding);
|
userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding);
|
||||||
userSession.setNote(SamlProtocol.SAML_LOGOUT_ISSUER, logoutRequest.getIssuer().getValue());
|
|
||||||
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
|
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
|
||||||
// remove client from logout requests
|
// remove client from logout requests
|
||||||
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
|
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
|
||||||
|
@ -446,47 +445,12 @@ public class SamlService {
|
||||||
if (!"true".equals(client.getAttribute("saml.client.signature"))) {
|
if (!"true".equals(client.getAttribute("saml.client.signature"))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MultivaluedMap<String, String> encodedParams = uriInfo.getQueryParameters(false);
|
|
||||||
String request = encodedParams.getFirst(GeneralConstants.SAML_REQUEST_KEY);
|
|
||||||
String algorithm = encodedParams.getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
|
|
||||||
String signature = encodedParams.getFirst(GeneralConstants.SAML_SIGNATURE_REQUEST_KEY);
|
|
||||||
|
|
||||||
if (request == null) throw new VerificationException("SAMLRequest as null");
|
|
||||||
if (algorithm == null) throw new VerificationException("SigAlg as null");
|
|
||||||
if (signature == null) throw new VerificationException("Signature as null");
|
|
||||||
|
|
||||||
// Shibboleth doesn't sign the document for redirect binding.
|
|
||||||
// todo maybe a flag?
|
|
||||||
// SamlProtocolUtils.verifyDocumentSignature(client, documentHolder.getSamlDocument());
|
|
||||||
|
|
||||||
PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client);
|
PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client);
|
||||||
|
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo);
|
||||||
|
|
||||||
UriBuilder builder = UriBuilder.fromPath("/")
|
|
||||||
.queryParam(GeneralConstants.SAML_REQUEST_KEY, request);
|
|
||||||
if (encodedParams.containsKey(GeneralConstants.RELAY_STATE)) {
|
|
||||||
builder.queryParam(GeneralConstants.RELAY_STATE, encodedParams.getFirst(GeneralConstants.RELAY_STATE));
|
|
||||||
}
|
|
||||||
builder.queryParam(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY, algorithm);
|
|
||||||
String rawQuery = builder.build().getRawQuery();
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] decodedSignature = RedirectBindingUtil.urlBase64Decode(signature);
|
|
||||||
|
|
||||||
SignatureAlgorithm signatureAlgorithm = SamlProtocol.getSignatureAlgorithm(client);
|
|
||||||
Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
|
|
||||||
validator.initVerify(publicKey);
|
|
||||||
validator.update(rawQuery.getBytes("UTF-8"));
|
|
||||||
if (!validator.verify(decodedSignature)) {
|
|
||||||
throw new VerificationException("Invalid query param signature");
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new VerificationException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
|
protected SAMLDocumentHolder extractRequestDocument(String samlRequest) {
|
||||||
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
|
return SAMLRequestParser.parseRequestRedirectBinding(samlRequest);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package org.keycloak.protocol.saml;
|
package org.keycloak.protocol.saml;
|
||||||
|
|
||||||
import java.security.Signature;
|
import java.security.Signature;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -16,6 +18,29 @@ public enum SignatureAlgorithm {
|
||||||
private final String xmlSignatureDigestMethod;
|
private final String xmlSignatureDigestMethod;
|
||||||
private final String javaSignatureAlgorithm;
|
private final String javaSignatureAlgorithm;
|
||||||
|
|
||||||
|
private static final Map<String, SignatureAlgorithm> signatureMethodMap = new HashMap<>();
|
||||||
|
private static final Map<String, SignatureAlgorithm> signatureDigestMethodMap = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
signatureMethodMap.put(RSA_SHA1.getXmlSignatureMethod(), RSA_SHA1);
|
||||||
|
signatureMethodMap.put(RSA_SHA256.getXmlSignatureMethod(), RSA_SHA256);
|
||||||
|
signatureMethodMap.put(RSA_SHA512.getXmlSignatureMethod(), RSA_SHA512);
|
||||||
|
signatureMethodMap.put(DSA_SHA1.getXmlSignatureMethod(), DSA_SHA1);
|
||||||
|
|
||||||
|
signatureDigestMethodMap.put(RSA_SHA1.getXmlSignatureDigestMethod(), RSA_SHA1);
|
||||||
|
signatureDigestMethodMap.put(RSA_SHA256.getXmlSignatureDigestMethod(), RSA_SHA256);
|
||||||
|
signatureDigestMethodMap.put(RSA_SHA512.getXmlSignatureDigestMethod(), RSA_SHA512);
|
||||||
|
signatureDigestMethodMap.put(DSA_SHA1.getXmlSignatureDigestMethod(), DSA_SHA1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignatureAlgorithm getFromXmlMethod(String xml) {
|
||||||
|
return signatureMethodMap.get(xml);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignatureAlgorithm getFromXmlDigest(String xml) {
|
||||||
|
return signatureDigestMethodMap.get(xml);
|
||||||
|
}
|
||||||
|
|
||||||
SignatureAlgorithm(String xmlSignatureMethod, String xmlSignatureDigestMethod, String javaSignatureAlgorithm) {
|
SignatureAlgorithm(String xmlSignatureMethod, String xmlSignatureDigestMethod, String javaSignatureAlgorithm) {
|
||||||
this.xmlSignatureMethod = xmlSignatureMethod;
|
this.xmlSignatureMethod = xmlSignatureMethod;
|
||||||
this.xmlSignatureDigestMethod = xmlSignatureDigestMethod;
|
this.xmlSignatureDigestMethod = xmlSignatureDigestMethod;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.keycloak.protocol;
|
package org.keycloak.protocol;
|
||||||
|
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -24,6 +25,8 @@ public interface LoginProtocol extends Provider {
|
||||||
|
|
||||||
LoginProtocol setHttpHeaders(HttpHeaders headers);
|
LoginProtocol setHttpHeaders(HttpHeaders headers);
|
||||||
|
|
||||||
|
LoginProtocol setEventBuilder(EventBuilder event);
|
||||||
|
|
||||||
Response cancelLogin(ClientSessionModel clientSession);
|
Response cancelLogin(ClientSessionModel clientSession);
|
||||||
Response invalidSessionError(ClientSessionModel clientSession);
|
Response invalidSessionError(ClientSessionModel clientSession);
|
||||||
Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode);
|
Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode);
|
||||||
|
|
|
@ -24,6 +24,9 @@ package org.keycloak.protocol.oidc;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
|
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.events.Details;
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -54,6 +57,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
||||||
public static final String CLIENT_ID_PARAM = "client_id";
|
public static final String CLIENT_ID_PARAM = "client_id";
|
||||||
public static final String PROMPT_PARAM = "prompt";
|
public static final String PROMPT_PARAM = "prompt";
|
||||||
public static final String LOGIN_HINT_PARAM = "login_hint";
|
public static final String LOGIN_HINT_PARAM = "login_hint";
|
||||||
|
public static final String LOGOUT_REDIRECT_URI = "OIDC_LOGOUT_REDIRECT_URI";
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(OIDCLoginProtocol.class);
|
private static final Logger log = Logger.getLogger(OIDCLoginProtocol.class);
|
||||||
|
|
||||||
|
@ -65,11 +69,14 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
||||||
|
|
||||||
protected HttpHeaders headers;
|
protected HttpHeaders headers;
|
||||||
|
|
||||||
public OIDCLoginProtocol(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers) {
|
protected EventBuilder event;
|
||||||
|
|
||||||
|
public OIDCLoginProtocol(KeycloakSession session, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, EventBuilder event) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.uriInfo = uriInfo;
|
this.uriInfo = uriInfo;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
|
this.event = event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OIDCLoginProtocol(){
|
public OIDCLoginProtocol(){
|
||||||
|
@ -100,6 +107,12 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OIDCLoginProtocol setEventBuilder(EventBuilder event) {
|
||||||
|
this.event = event;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response cancelLogin(ClientSessionModel clientSession) {
|
public Response cancelLogin(ClientSessionModel clientSession) {
|
||||||
String redirect = clientSession.getRedirectUri();
|
String redirect = clientSession.getRedirectUri();
|
||||||
|
@ -168,7 +181,19 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response finishLogout(UserSessionModel userSession) {
|
public Response finishLogout(UserSessionModel userSession) {
|
||||||
throw new RuntimeException("NOT IMPLEMENTED");
|
String redirectUri = userSession.getNote(OIDCLoginProtocol.LOGOUT_REDIRECT_URI);
|
||||||
|
event.event(EventType.LOGOUT);
|
||||||
|
if (redirectUri != null) {
|
||||||
|
event.detail(Details.REDIRECT_URI, redirectUri);
|
||||||
|
}
|
||||||
|
event.user(userSession.getUser()).session(userSession).success();
|
||||||
|
|
||||||
|
|
||||||
|
if (redirectUri != null) {
|
||||||
|
return Response.status(302).location(UriBuilder.fromUri(redirectUri).build()).build();
|
||||||
|
} else {
|
||||||
|
return Response.ok().build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
1
services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
Normal file → Executable file
1
services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
Normal file → Executable file
|
@ -37,6 +37,7 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
|
||||||
config.setAuthorizationEndpoint(uriBuilder.clone().path(OIDCLoginProtocolService.class, "auth").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
|
config.setAuthorizationEndpoint(uriBuilder.clone().path(OIDCLoginProtocolService.class, "auth").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
|
||||||
config.setTokenEndpoint(uriBuilder.clone().path(OIDCLoginProtocolService.class, "token").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
|
config.setTokenEndpoint(uriBuilder.clone().path(OIDCLoginProtocolService.class, "token").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
|
||||||
config.setUserinfoEndpoint(uriBuilder.clone().path(OIDCLoginProtocolService.class, "issueUserInfo").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
|
config.setUserinfoEndpoint(uriBuilder.clone().path(OIDCLoginProtocolService.class, "issueUserInfo").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
|
||||||
|
config.setLogoutEndpoint(uriBuilder.clone().path(OIDCLoginProtocolService.class, "logout").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
|
||||||
config.setJwksUri(uriBuilder.clone().path(OIDCLoginProtocolService.class, "certs").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
|
config.setJwksUri(uriBuilder.clone().path(OIDCLoginProtocolService.class, "certs").build(realm.getName(), OIDCLoginProtocol.LOGIN_PROTOCOL).toString());
|
||||||
|
|
||||||
config.setIdTokenSigningAlgValuesSupported(DEFAULT_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);
|
config.setIdTokenSigningAlgValuesSupported(DEFAULT_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);
|
||||||
|
|
8
services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
Normal file → Executable file
8
services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
Normal file → Executable file
|
@ -235,7 +235,7 @@ public class AuthorizationEndpoint {
|
||||||
String accessCode = new ClientSessionCode(realm, clientSession).getCode();
|
String accessCode = new ClientSessionCode(realm, clientSession).getCode();
|
||||||
|
|
||||||
if (idpHint != null && !"".equals(idpHint)) {
|
if (idpHint != null && !"".equals(idpHint)) {
|
||||||
IdentityProviderModel identityProviderModel = realm.getIdentityProviderById(idpHint);
|
IdentityProviderModel identityProviderModel = realm.getIdentityProviderByAlias(idpHint);
|
||||||
|
|
||||||
if (identityProviderModel == null) {
|
if (identityProviderModel == null) {
|
||||||
return Flows.forms(session, realm, null, uriInfo, headers)
|
return Flows.forms(session, realm, null, uriInfo, headers)
|
||||||
|
@ -254,14 +254,14 @@ public class AuthorizationEndpoint {
|
||||||
if (httpAuthOutput.getResponse() != null) return httpAuthOutput.getResponse();
|
if (httpAuthOutput.getResponse() != null) return httpAuthOutput.getResponse();
|
||||||
|
|
||||||
if (prompt != null && prompt.equals("none")) {
|
if (prompt != null && prompt.equals("none")) {
|
||||||
OIDCLoginProtocol oauth = new OIDCLoginProtocol(session, realm, uriInfo, headers);
|
OIDCLoginProtocol oauth = new OIDCLoginProtocol(session, realm, uriInfo, headers, event);
|
||||||
return oauth.cancelLogin(clientSession);
|
return oauth.cancelLogin(clientSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||||
for (IdentityProviderModel identityProvider : identityProviders) {
|
for (IdentityProviderModel identityProvider : identityProviders) {
|
||||||
if (identityProvider.isAuthenticateByDefault()) {
|
if (identityProvider.isAuthenticateByDefault()) {
|
||||||
return buildRedirectToIdentityProvider(identityProvider.getId(), accessCode);
|
return buildRedirectToIdentityProvider(identityProvider.getAlias(), accessCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,7 @@ public class AuthorizationEndpoint {
|
||||||
if (requiredCredentials.isEmpty()) {
|
if (requiredCredentials.isEmpty()) {
|
||||||
if (!identityProviders.isEmpty()) {
|
if (!identityProviders.isEmpty()) {
|
||||||
if (identityProviders.size() == 1) {
|
if (identityProviders.size() == 1) {
|
||||||
return buildRedirectToIdentityProvider(identityProviders.get(0).getId(), accessCode);
|
return buildRedirectToIdentityProvider(identityProviders.get(0).getAlias(), accessCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Flows.forms(session, realm, null, uriInfo, headers).setError(Messages.IDENTITY_PROVIDER_NOT_UNIQUE, realm.getName()).createErrorPage();
|
return Flows.forms(session, realm, null, uriInfo, headers).setError(Messages.IDENTITY_PROVIDER_NOT_UNIQUE, realm.getName()).createErrorPage();
|
||||||
|
|
23
services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
Normal file → Executable file
23
services/src/main/java/org/keycloak/protocol/oidc/endpoints/LogoutEndpoint.java
Normal file → Executable file
|
@ -78,23 +78,28 @@ public class LogoutEndpoint {
|
||||||
*/
|
*/
|
||||||
@GET
|
@GET
|
||||||
@NoCache
|
@NoCache
|
||||||
public Response logout(final @QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri) {
|
public Response logout(@QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String redirectUri) {
|
||||||
event.event(EventType.LOGOUT);
|
|
||||||
if (redirectUri != null) {
|
if (redirectUri != null) {
|
||||||
event.detail(Details.REDIRECT_URI, redirectUri);
|
String validatedUri = RedirectUtils.verifyRealmRedirectUri(uriInfo, redirectUri, realm);
|
||||||
|
if (validatedUri == null) {
|
||||||
|
event.event(EventType.LOGOUT);
|
||||||
|
event.detail(Details.REDIRECT_URI, redirectUri);
|
||||||
|
event.error(Errors.INVALID_REDIRECT_URI);
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI);
|
||||||
|
}
|
||||||
|
redirectUri = validatedUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
|
// authenticate identity cookie, but ignore an access token timeout as we're logging out anyways.
|
||||||
AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
|
AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, uriInfo, clientConnection, headers, false);
|
||||||
if (authResult != null) {
|
if (authResult != null) {
|
||||||
logout(authResult.getSession());
|
if (redirectUri != null) authResult.getSession().setNote(OIDCLoginProtocol.LOGOUT_REDIRECT_URI, redirectUri);
|
||||||
|
authResult.getSession().setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||||
|
return AuthenticationManager.browserLogout(session, realm, authResult.getSession(), uriInfo, clientConnection, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redirectUri != null) {
|
if (redirectUri != null) {
|
||||||
String validatedRedirect = RedirectUtils.verifyRealmRedirectUri(uriInfo, redirectUri, realm);
|
return Response.status(302).location(UriBuilder.fromUri(redirectUri).build()).build();
|
||||||
if (validatedRedirect == null) {
|
|
||||||
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.INVALID_REDIRECT_URI);
|
|
||||||
}
|
|
||||||
return Response.status(302).location(UriBuilder.fromUri(validatedRedirect).build()).build();
|
|
||||||
} else {
|
} else {
|
||||||
return Response.ok().build();
|
return Response.ok().build();
|
||||||
}
|
}
|
||||||
|
|
11
services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
Normal file → Executable file
11
services/src/main/java/org/keycloak/protocol/oidc/representations/OIDCConfigurationRepresentation.java
Normal file → Executable file
|
@ -23,6 +23,9 @@ public class OIDCConfigurationRepresentation {
|
||||||
@JsonProperty("userinfo_endpoint")
|
@JsonProperty("userinfo_endpoint")
|
||||||
private String userinfoEndpoint;
|
private String userinfoEndpoint;
|
||||||
|
|
||||||
|
@JsonProperty("end_session_endpoint")
|
||||||
|
private String logoutEndpoint;
|
||||||
|
|
||||||
@JsonProperty("jwks_uri")
|
@JsonProperty("jwks_uri")
|
||||||
private String jwksUri;
|
private String jwksUri;
|
||||||
|
|
||||||
|
@ -81,6 +84,14 @@ public class OIDCConfigurationRepresentation {
|
||||||
this.jwksUri = jwksUri;
|
this.jwksUri = jwksUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getLogoutEndpoint() {
|
||||||
|
return logoutEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLogoutEndpoint(String logoutEndpoint) {
|
||||||
|
this.logoutEndpoint = logoutEndpoint;
|
||||||
|
}
|
||||||
|
|
||||||
public List<String> getGrantTypesSupported() {
|
public List<String> getGrantTypesSupported() {
|
||||||
return grantTypesSupported;
|
return grantTypesSupported;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.jboss.resteasy.spi.HttpRequest;
|
||||||
import org.keycloak.ClientConnection;
|
import org.keycloak.ClientConnection;
|
||||||
import org.keycloak.RSATokenVerifier;
|
import org.keycloak.RSATokenVerifier;
|
||||||
import org.keycloak.VerificationException;
|
import org.keycloak.VerificationException;
|
||||||
|
import org.keycloak.broker.provider.IdentityProvider;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
|
@ -27,6 +28,7 @@ import org.keycloak.protocol.LoginProtocol;
|
||||||
import org.keycloak.protocol.oidc.TokenManager;
|
import org.keycloak.protocol.oidc.TokenManager;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
|
import org.keycloak.services.resources.IdentityBrokerService;
|
||||||
import org.keycloak.services.resources.LoginActionsService;
|
import org.keycloak.services.resources.LoginActionsService;
|
||||||
import org.keycloak.services.resources.RealmsResource;
|
import org.keycloak.services.resources.RealmsResource;
|
||||||
import org.keycloak.services.resources.flows.Flows;
|
import org.keycloak.services.resources.flows.Flows;
|
||||||
|
@ -144,9 +146,6 @@ public class AuthenticationManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redirectClients.size() == 0) {
|
|
||||||
return finishBrowserLogout(session, realm, userSession, uriInfo, connection, headers);
|
|
||||||
}
|
|
||||||
for (ClientSessionModel nextRedirectClient : redirectClients) {
|
for (ClientSessionModel nextRedirectClient : redirectClients) {
|
||||||
String authMethod = nextRedirectClient.getAuthMethod();
|
String authMethod = nextRedirectClient.getAuthMethod();
|
||||||
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
|
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
|
||||||
|
@ -167,18 +166,26 @@ public class AuthenticationManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
String brokerId = userSession.getNote(IdentityBrokerService.BROKER_PROVIDER_ID);
|
||||||
|
if (brokerId != null) {
|
||||||
|
IdentityProvider identityProvider = IdentityBrokerService.getIdentityProvider(session, realm, brokerId);
|
||||||
|
Response response = identityProvider.keycloakInitiatedBrowserLogout(userSession, uriInfo, realm);
|
||||||
|
if (response != null) return response;
|
||||||
|
}
|
||||||
return finishBrowserLogout(session, realm, userSession, uriInfo, connection, headers);
|
return finishBrowserLogout(session, realm, userSession, uriInfo, connection, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static Response finishBrowserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
|
public static Response finishBrowserLogout(KeycloakSession session, RealmModel realm, UserSessionModel userSession, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
|
||||||
expireIdentityCookie(realm, uriInfo, connection);
|
expireIdentityCookie(realm, uriInfo, connection);
|
||||||
expireRememberMeCookie(realm, uriInfo, connection);
|
expireRememberMeCookie(realm, uriInfo, connection);
|
||||||
userSession.setState(UserSessionModel.State.LOGGED_OUT);
|
userSession.setState(UserSessionModel.State.LOGGED_OUT);
|
||||||
String method = userSession.getNote(KEYCLOAK_LOGOUT_PROTOCOL);
|
String method = userSession.getNote(KEYCLOAK_LOGOUT_PROTOCOL);
|
||||||
|
EventBuilder event = new EventsManager(realm, session, connection).createEventBuilder();
|
||||||
LoginProtocol protocol = session.getProvider(LoginProtocol.class, method);
|
LoginProtocol protocol = session.getProvider(LoginProtocol.class, method);
|
||||||
protocol.setRealm(realm)
|
protocol.setRealm(realm)
|
||||||
.setHttpHeaders(headers)
|
.setHttpHeaders(headers)
|
||||||
.setUriInfo(uriInfo);
|
.setUriInfo(uriInfo)
|
||||||
|
.setEventBuilder(event);
|
||||||
Response response = protocol.finishLogout(userSession);
|
Response response = protocol.finishLogout(userSession);
|
||||||
session.sessions().removeUserSession(realm, userSession);
|
session.sessions().removeUserSession(realm, userSession);
|
||||||
return response;
|
return response;
|
||||||
|
|
|
@ -171,4 +171,6 @@ public class Messages {
|
||||||
public static final String CLIENT_NOT_FOUND = "clientNotFoundMessage";
|
public static final String CLIENT_NOT_FOUND = "clientNotFoundMessage";
|
||||||
|
|
||||||
public static final String INVALID_PARAMETER = "invalidParameterMessage";
|
public static final String INVALID_PARAMETER = "invalidParameterMessage";
|
||||||
|
|
||||||
|
public static final String IDENTITY_PROVIDER_LOGIN_FAILURE = "identityProviderLoginFailure";
|
||||||
}
|
}
|
||||||
|
|
|
@ -652,7 +652,7 @@ public class AccountService {
|
||||||
boolean hasProvider = false;
|
boolean hasProvider = false;
|
||||||
|
|
||||||
for (IdentityProviderModel model : realm.getIdentityProviders()) {
|
for (IdentityProviderModel model : realm.getIdentityProviders()) {
|
||||||
if (model.getId().equals(providerId)) {
|
if (model.getAlias().equals(providerId)) {
|
||||||
hasProvider = true;
|
hasProvider = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@ package org.keycloak.services.resources;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
|
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||||
import org.keycloak.ClientConnection;
|
import org.keycloak.ClientConnection;
|
||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
|
||||||
import org.keycloak.broker.provider.FederatedIdentity;
|
import org.keycloak.broker.provider.FederatedIdentity;
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
import org.keycloak.broker.provider.IdentityProvider;
|
import org.keycloak.broker.provider.IdentityProvider;
|
||||||
|
@ -77,9 +77,10 @@ import static org.keycloak.models.UserModel.RequiredAction.UPDATE_PROFILE;
|
||||||
* @author Pedro Igor
|
* @author Pedro Igor
|
||||||
*/
|
*/
|
||||||
@Path("/broker")
|
@Path("/broker")
|
||||||
public class IdentityBrokerService {
|
public class IdentityBrokerService implements IdentityProvider.AuthenticationCallback {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(IdentityBrokerService.class);
|
private static final Logger LOGGER = Logger.getLogger(IdentityBrokerService.class);
|
||||||
|
public static final String BROKER_PROVIDER_ID = "BROKER_PROVIDER_ID";
|
||||||
|
|
||||||
private final RealmModel realmModel;
|
private final RealmModel realmModel;
|
||||||
|
|
||||||
|
@ -122,11 +123,9 @@ public class IdentityBrokerService {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ClientSessionCode clientSessionCode = parseClientSessionCode(code, providerId);
|
ClientSessionCode clientSessionCode = parseClientSessionCode(code);
|
||||||
IdentityProvider identityProvider = getIdentityProvider(providerId);
|
IdentityProvider identityProvider = getIdentityProvider(session, realmModel, providerId);
|
||||||
AuthenticationResponse authenticationResponse = identityProvider.handleRequest(createAuthenticationRequest(providerId, clientSessionCode));
|
Response response = identityProvider.handleRequest(createAuthenticationRequest(providerId, clientSessionCode));
|
||||||
|
|
||||||
Response response = authenticationResponse.getResponse();
|
|
||||||
|
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
this.event.success();
|
this.event.success();
|
||||||
|
@ -144,16 +143,15 @@ public class IdentityBrokerService {
|
||||||
return redirectToErrorPage(Messages.COULD_NOT_PROCEED_WITH_AUTHENTICATION_REQUEST);
|
return redirectToErrorPage(Messages.COULD_NOT_PROCEED_WITH_AUTHENTICATION_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@Path("{provider_id}/endpoint")
|
||||||
@Path("{provider_id}")
|
public Object getEndpoint(@PathParam("provider_id") String providerId) {
|
||||||
public Response handleResponseGet(@PathParam("provider_id") String providerId) {
|
IdentityProvider identityProvider = getIdentityProvider(session, realmModel, providerId);
|
||||||
return handleResponse(providerId);
|
Object callback = identityProvider.callback(realmModel, this);
|
||||||
}
|
ResteasyProviderFactory.getInstance().injectProperties(callback);
|
||||||
|
//resourceContext.initResource(brokerService);
|
||||||
|
return callback;
|
||||||
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("{provider_id}")
|
|
||||||
public Response handleResponsePost(@PathParam("provider_id") String providerId) {
|
|
||||||
return handleResponse(providerId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("{provider_id}/token")
|
@Path("{provider_id}/token")
|
||||||
|
@ -196,7 +194,7 @@ public class IdentityBrokerService {
|
||||||
.createOAuthGrant(null), clientModel);
|
.createOAuthGrant(null), clientModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentityProvider identityProvider = getIdentityProvider(providerId);
|
IdentityProvider identityProvider = getIdentityProvider(session, realmModel, providerId);
|
||||||
IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(providerId);
|
IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(providerId);
|
||||||
|
|
||||||
if (identityProviderConfig.isStoreToken()) {
|
if (identityProviderConfig.isStoreToken()) {
|
||||||
|
@ -234,87 +232,45 @@ public class IdentityBrokerService {
|
||||||
return getToken(providerId, true);
|
return getToken(providerId, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response handleResponse(String providerId) {
|
public Response authenticated(Map<String, String> userNotes, IdentityProviderModel identityProviderConfig, FederatedIdentity federatedIdentity, String code) {
|
||||||
if (isDebugEnabled()) {
|
ClientSessionCode clientCode = null;
|
||||||
LOGGER.debugf("Handling authentication response from identity provider [%s].", providerId);
|
|
||||||
}
|
|
||||||
this.event.event(EventType.IDENTITY_PROVIDER_RESPONSE);
|
|
||||||
this.event.detail(Details.IDENTITY_PROVIDER, providerId);
|
|
||||||
IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(providerId);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
IdentityProvider identityProvider = getIdentityProvider(providerId);
|
clientCode = parseClientSessionCode(code);
|
||||||
String relayState = identityProvider.getRelayState(createAuthenticationRequest(providerId, null));
|
|
||||||
|
|
||||||
if (relayState == null) {
|
|
||||||
return redirectToErrorPage(Messages.NO_RELAY_STATE_IN_RESPONSE, providerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDebugEnabled()) {
|
|
||||||
LOGGER.debugf("Relay state is valid: [%s].", relayState);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientSessionCode clientSessionCode = parseClientSessionCode(relayState, providerId);
|
|
||||||
AuthenticationResponse authenticationResponse = identityProvider.handleResponse(createAuthenticationRequest(providerId, clientSessionCode));
|
|
||||||
Response response = authenticationResponse.getResponse();
|
|
||||||
|
|
||||||
if (response != null) {
|
|
||||||
if (isDebugEnabled()) {
|
|
||||||
LOGGER.debugf("Identity provider [%s] is going to send a response [%s].", identityProvider, response);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
FederatedIdentity identity = authenticationResponse.getUser();
|
|
||||||
|
|
||||||
if (isDebugEnabled()) {
|
|
||||||
LOGGER.debugf("Identity provider [%s] returned with identity [%s].", providerId, identity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!identityProviderConfig.isStoreToken()) {
|
|
||||||
if (isDebugEnabled()) {
|
|
||||||
LOGGER.debugf("Token will not be stored for identity provider [%s].", providerId);
|
|
||||||
}
|
|
||||||
identity.setToken(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
identity.setIdentityProviderId(providerId);
|
|
||||||
|
|
||||||
return performLocalAuthentication(identity, clientSessionCode);
|
|
||||||
} catch (IdentityBrokerException e) {
|
|
||||||
rollback();
|
|
||||||
return redirectToErrorPage(Messages.IDENTITY_PROVIDER_AUTHENTICATION_FAILED, e, providerId);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
rollback();
|
return redirectToErrorPage(Messages.IDENTITY_PROVIDER_AUTHENTICATION_FAILED, e, identityProviderConfig.getProviderId());
|
||||||
return redirectToErrorPage(Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE, e, providerId);
|
|
||||||
} finally {
|
|
||||||
if (this.session.getTransaction().isActive()) {
|
|
||||||
this.session.getTransaction().commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Response performLocalAuthentication(FederatedIdentity updatedIdentity, ClientSessionCode clientCode) {
|
}
|
||||||
|
String providerId = identityProviderConfig.getAlias();
|
||||||
|
if (!identityProviderConfig.isStoreToken()) {
|
||||||
|
if (isDebugEnabled()) {
|
||||||
|
LOGGER.debugf("Token will not be stored for identity provider [%s].", providerId);
|
||||||
|
}
|
||||||
|
federatedIdentity.setToken(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
federatedIdentity.setIdentityProviderId(providerId);
|
||||||
ClientSessionModel clientSession = clientCode.getClientSession();
|
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||||
IdentityProviderModel identityProviderConfig = getIdentityProviderConfig(updatedIdentity.getIdentityProviderId());
|
FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(providerId, federatedIdentity.getId(),
|
||||||
String providerId = identityProviderConfig.getId();
|
federatedIdentity.getUsername(), federatedIdentity.getToken());
|
||||||
FederatedIdentityModel federatedIdentityModel = new FederatedIdentityModel(providerId, updatedIdentity.getId(),
|
|
||||||
updatedIdentity.getUsername(), updatedIdentity.getToken());
|
|
||||||
|
|
||||||
this.event.event(EventType.IDENTITY_PROVIDER_LOGIN)
|
this.event.event(EventType.IDENTITY_PROVIDER_LOGIN)
|
||||||
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
|
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
|
||||||
.detail(Details.IDENTITY_PROVIDER_IDENTITY, updatedIdentity.getUsername());
|
.detail(Details.IDENTITY_PROVIDER_IDENTITY, federatedIdentity.getUsername());
|
||||||
|
|
||||||
UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
|
UserModel federatedUser = this.session.users().getUserByFederatedIdentity(federatedIdentityModel, this.realmModel);
|
||||||
|
|
||||||
// Check if federatedUser is already authenticated (this means linking social into existing federatedUser account)
|
// Check if federatedUser is already authenticated (this means linking social into existing federatedUser account)
|
||||||
if (clientSession.getUserSession() != null) {
|
if (clientSession.getUserSession() != null) {
|
||||||
|
UserSessionModel userSession = clientSession.getUserSession();
|
||||||
|
for (Map.Entry<String, String> entry : userNotes.entrySet()) {
|
||||||
|
userSession.setNote(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
return performAccountLinking(clientSession, providerId, federatedIdentityModel, federatedUser);
|
return performAccountLinking(clientSession, providerId, federatedIdentityModel, federatedUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (federatedUser == null) {
|
if (federatedUser == null) {
|
||||||
try {
|
try {
|
||||||
federatedUser = createUser(updatedIdentity);
|
federatedUser = createUser(federatedIdentity);
|
||||||
|
|
||||||
if (identityProviderConfig.isUpdateProfileFirstLogin()) {
|
if (identityProviderConfig.isUpdateProfileFirstLogin()) {
|
||||||
if (isDebugEnabled()) {
|
if (isDebugEnabled()) {
|
||||||
|
@ -327,7 +283,7 @@ public class IdentityBrokerService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateFederatedIdentity(updatedIdentity, federatedUser);
|
updateFederatedIdentity(federatedIdentity, federatedUser);
|
||||||
|
|
||||||
UserSessionModel userSession = this.session.sessions()
|
UserSessionModel userSession = this.session.sessions()
|
||||||
.createUserSession(this.realmModel, federatedUser, federatedUser.getUsername(), this.clientConnection.getRemoteAddr(), "broker", false);
|
.createUserSession(this.realmModel, federatedUser, federatedUser.getUsername(), this.clientConnection.getRemoteAddr(), "broker", false);
|
||||||
|
@ -336,6 +292,10 @@ public class IdentityBrokerService {
|
||||||
this.event.session(userSession);
|
this.event.session(userSession);
|
||||||
|
|
||||||
TokenManager.attachClientSession(userSession, clientSession);
|
TokenManager.attachClientSession(userSession, clientSession);
|
||||||
|
for (Map.Entry<String, String> entry : userNotes.entrySet()) {
|
||||||
|
userSession.setNote(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
userSession.setNote(BROKER_PROVIDER_ID, providerId);
|
||||||
|
|
||||||
if (isDebugEnabled()) {
|
if (isDebugEnabled()) {
|
||||||
LOGGER.debugf("Performing local authentication for user [%s].", federatedUser);
|
LOGGER.debugf("Performing local authentication for user [%s].", federatedUser);
|
||||||
|
@ -387,7 +347,7 @@ public class IdentityBrokerService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientSessionCode parseClientSessionCode(String code, String providerId) {
|
private ClientSessionCode parseClientSessionCode(String code) {
|
||||||
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
|
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
|
||||||
|
|
||||||
if (clientCode != null && clientCode.isValid(AUTHENTICATE)) {
|
if (clientCode != null && clientCode.isValid(AUTHENTICATE)) {
|
||||||
|
@ -466,28 +426,28 @@ public class IdentityBrokerService {
|
||||||
return Flows.errors().error(message, Status.BAD_REQUEST);
|
return Flows.errors().error(message, Status.BAD_REQUEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdentityProvider getIdentityProvider(String providerId) {
|
public static IdentityProvider getIdentityProvider(KeycloakSession session, RealmModel realm, String alias) {
|
||||||
IdentityProviderModel identityProviderModel = this.realmModel.getIdentityProviderById(providerId);
|
IdentityProviderModel identityProviderModel = realm.getIdentityProviderByAlias(alias);
|
||||||
|
|
||||||
if (identityProviderModel != null) {
|
if (identityProviderModel != null) {
|
||||||
IdentityProviderFactory providerFactory = getIdentityProviderFactory(identityProviderModel);
|
IdentityProviderFactory providerFactory = getIdentityProviderFactory(session, identityProviderModel);
|
||||||
|
|
||||||
if (providerFactory == null) {
|
if (providerFactory == null) {
|
||||||
throw new IdentityBrokerException("Could not find factory for identity provider [" + providerId + "].");
|
throw new IdentityBrokerException("Could not find factory for identity provider [" + alias + "].");
|
||||||
}
|
}
|
||||||
|
|
||||||
return providerFactory.create(identityProviderModel);
|
return providerFactory.create(identityProviderModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IdentityBrokerException("Identity Provider [" + providerId + "] not found.");
|
throw new IdentityBrokerException("Identity Provider [" + alias + "] not found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private IdentityProviderFactory getIdentityProviderFactory(IdentityProviderModel model) {
|
private static IdentityProviderFactory getIdentityProviderFactory(KeycloakSession session, IdentityProviderModel model) {
|
||||||
Map<String, IdentityProviderFactory> availableProviders = new HashMap<String, IdentityProviderFactory>();
|
Map<String, IdentityProviderFactory> availableProviders = new HashMap<String, IdentityProviderFactory>();
|
||||||
List<ProviderFactory> allProviders = new ArrayList<ProviderFactory>();
|
List<ProviderFactory> allProviders = new ArrayList<ProviderFactory>();
|
||||||
|
|
||||||
allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class));
|
allProviders.addAll(session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class));
|
||||||
allProviders.addAll(this.session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class));
|
allProviders.addAll(session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class));
|
||||||
|
|
||||||
for (ProviderFactory providerFactory : allProviders) {
|
for (ProviderFactory providerFactory : allProviders) {
|
||||||
availableProviders.put(providerFactory.getId(), (IdentityProviderFactory) providerFactory);
|
availableProviders.put(providerFactory.getId(), (IdentityProviderFactory) providerFactory);
|
||||||
|
@ -498,7 +458,7 @@ public class IdentityBrokerService {
|
||||||
|
|
||||||
private IdentityProviderModel getIdentityProviderConfig(String providerId) {
|
private IdentityProviderModel getIdentityProviderConfig(String providerId) {
|
||||||
for (IdentityProviderModel model : this.realmModel.getIdentityProviders()) {
|
for (IdentityProviderModel model : this.realmModel.getIdentityProviders()) {
|
||||||
if (model.getId().equals(providerId)) {
|
if (model.getAlias().equals(providerId)) {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,6 +189,7 @@ public class RealmsResource {
|
||||||
|
|
||||||
IdentityBrokerService brokerService = new IdentityBrokerService(realm);
|
IdentityBrokerService brokerService = new IdentityBrokerService(realm);
|
||||||
ResteasyProviderFactory.getInstance().injectProperties(brokerService);
|
ResteasyProviderFactory.getInstance().injectProperties(brokerService);
|
||||||
|
//resourceContext.initResource(brokerService);
|
||||||
|
|
||||||
brokerService.init();
|
brokerService.init();
|
||||||
|
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class IdentityProviderResource {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
removeClientIdentityProviders(this.realm.getApplications(), this.identityProviderModel);
|
removeClientIdentityProviders(this.realm.getApplications(), this.identityProviderModel);
|
||||||
removeClientIdentityProviders(this.realm.getOAuthClients(), this.identityProviderModel);
|
removeClientIdentityProviders(this.realm.getOAuthClients(), this.identityProviderModel);
|
||||||
this.realm.removeIdentityProviderById(this.identityProviderModel.getId());
|
this.realm.removeIdentityProviderByAlias(this.identityProviderModel.getAlias());
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ public class IdentityProviderResource {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
|
|
||||||
String internalId = providerRep.getInternalId();
|
String internalId = providerRep.getInternalId();
|
||||||
String newProviderId = providerRep.getId();
|
String newProviderId = providerRep.getAlias();
|
||||||
String oldProviderId = getProviderIdByInternalId(this.realm, internalId);
|
String oldProviderId = getProviderIdByInternalId(this.realm, internalId);
|
||||||
|
|
||||||
this.realm.updateIdentityProvider(RepresentationToModel.toModel(providerRep));
|
this.realm.updateIdentityProvider(RepresentationToModel.toModel(providerRep));
|
||||||
|
@ -92,7 +92,7 @@ public class IdentityProviderResource {
|
||||||
|
|
||||||
return Response.noContent().build();
|
return Response.noContent().build();
|
||||||
} catch (ModelDuplicateException e) {
|
} catch (ModelDuplicateException e) {
|
||||||
return Flows.errors().exists("Identity Provider " + providerRep.getId() + " already exists");
|
return Flows.errors().exists("Identity Provider " + providerRep.getAlias() + " already exists");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ public class IdentityProviderResource {
|
||||||
List<IdentityProviderModel> providerModels = realm.getIdentityProviders();
|
List<IdentityProviderModel> providerModels = realm.getIdentityProviders();
|
||||||
for (IdentityProviderModel providerModel : providerModels) {
|
for (IdentityProviderModel providerModel : providerModels) {
|
||||||
if (providerModel.getInternalId().equals(providerInternalId)) {
|
if (providerModel.getInternalId().equals(providerInternalId)) {
|
||||||
return providerModel.getId();
|
return providerModel.getAlias();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ public class IdentityProviderResource {
|
||||||
List<ClientIdentityProviderMappingModel> identityProviders = clientModel.getIdentityProviders();
|
List<ClientIdentityProviderMappingModel> identityProviders = clientModel.getIdentityProviders();
|
||||||
|
|
||||||
for (ClientIdentityProviderMappingModel providerMappingModel : new ArrayList<ClientIdentityProviderMappingModel>(identityProviders)) {
|
for (ClientIdentityProviderMappingModel providerMappingModel : new ArrayList<ClientIdentityProviderMappingModel>(identityProviders)) {
|
||||||
if (providerMappingModel.getIdentityProvider().equals(identityProvider.getId())) {
|
if (providerMappingModel.getIdentityProvider().equals(identityProvider.getAlias())) {
|
||||||
identityProviders.remove(providerMappingModel);
|
identityProviders.remove(providerMappingModel);
|
||||||
clientModel.updateIdentityProviders(identityProviders);
|
clientModel.updateIdentityProviders(identityProviders);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -56,21 +56,6 @@ public class IdentityProvidersResource {
|
||||||
this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER);
|
this.auth.init(RealmAuth.Resource.IDENTITY_PROVIDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
|
||||||
@NoCache
|
|
||||||
@Produces("application/json")
|
|
||||||
public List<IdentityProviderRepresentation> getIdentityProviders() {
|
|
||||||
this.auth.requireView();
|
|
||||||
|
|
||||||
List<IdentityProviderRepresentation> representations = new ArrayList<IdentityProviderRepresentation>();
|
|
||||||
|
|
||||||
for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
|
|
||||||
representations.add(ModelToRepresentation.toRepresentation(identityProviderModel));
|
|
||||||
}
|
|
||||||
|
|
||||||
return representations;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Path("/providers/{provider_id}")
|
@Path("/providers/{provider_id}")
|
||||||
@GET
|
@GET
|
||||||
@NoCache
|
@NoCache
|
||||||
|
@ -87,71 +72,28 @@ public class IdentityProvidersResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Path("import-config")
|
||||||
public Response create(@Context UriInfo uriInfo, IdentityProviderRepresentation representation) {
|
|
||||||
this.auth.requireManage();
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.realm.addIdentityProvider(RepresentationToModel.toModel(representation));
|
|
||||||
|
|
||||||
return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getProviderId()).build()).build();
|
|
||||||
} catch (ModelDuplicateException e) {
|
|
||||||
return Flows.errors().exists("Identity Provider " + representation.getId() + " already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@POST
|
|
||||||
@Path("import")
|
|
||||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||||
public Response importFrom(@Context UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Map<String, String> importFrom(@Context UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
Map<String, List<InputPart>> formDataMap = input.getFormDataMap();
|
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 providerId = formDataMap.get("providerId").get(0).getBodyAsString();
|
||||||
String enabled = formDataMap.get("enabled").get(0).getBodyAsString();
|
|
||||||
String updateProfileFirstLogin = formDataMap.get("updateProfileFirstLogin").get(0).getBodyAsString();
|
|
||||||
String storeToken = "false";
|
|
||||||
|
|
||||||
if (formDataMap.containsKey("storeToken")) {
|
|
||||||
storeToken = formDataMap.get("storeToken").get(0).getBodyAsString();
|
|
||||||
}
|
|
||||||
|
|
||||||
InputPart file = formDataMap.get("file").get(0);
|
InputPart file = formDataMap.get("file").get(0);
|
||||||
InputStream inputStream = file.getBody(InputStream.class, null);
|
InputStream inputStream = file.getBody(InputStream.class, null);
|
||||||
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
||||||
Map config = providerFactory.parseConfig(inputStream);
|
Map<String, String> config = providerFactory.parseConfig(inputStream);
|
||||||
IdentityProviderRepresentation representation = new IdentityProviderRepresentation();
|
return config;
|
||||||
|
|
||||||
representation.setId(id);
|
|
||||||
representation.setName(name);
|
|
||||||
representation.setProviderId(providerId);
|
|
||||||
representation.setEnabled(Boolean.valueOf(enabled));
|
|
||||||
representation.setUpdateProfileFirstLogin(Boolean.valueOf(updateProfileFirstLogin));
|
|
||||||
representation.setStoreToken(Boolean.valueOf(storeToken));
|
|
||||||
representation.setConfig(config);
|
|
||||||
|
|
||||||
return create(uriInfo, representation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("import")
|
@Path("import-config")
|
||||||
@Consumes(MediaType.APPLICATION_JSON)
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
public Response importFrom(@Context UriInfo uriInfo, Map<String, Object> data) throws IOException {
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
public Map<String, String> importFrom(@Context UriInfo uriInfo, Map<String, Object> data) throws IOException {
|
||||||
this.auth.requireManage();
|
this.auth.requireManage();
|
||||||
|
|
||||||
String id = data.get("id").toString();
|
|
||||||
String name = data.get("name").toString();
|
|
||||||
String providerId = data.get("providerId").toString();
|
String providerId = data.get("providerId").toString();
|
||||||
String enabled = data.get("enabled").toString();
|
|
||||||
String updateProfileFirstLogin = data.get("updateProfileFirstLogin").toString();
|
|
||||||
String storeToken = "false";
|
|
||||||
|
|
||||||
if (data.containsKey("storeToken")) {
|
|
||||||
storeToken = data.get("storeToken").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
String from = data.get("fromUrl").toString();
|
String from = data.get("fromUrl").toString();
|
||||||
ApacheHttpClient4Executor executor = ResourceAdminManager.createExecutor();
|
ApacheHttpClient4Executor executor = ResourceAdminManager.createExecutor();
|
||||||
InputStream inputStream = null;
|
InputStream inputStream = null;
|
||||||
|
@ -161,34 +103,55 @@ public class IdentityProvidersResource {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
|
||||||
Map config = providerFactory.parseConfig(inputStream);
|
Map<String, String> config = providerFactory.parseConfig(inputStream);
|
||||||
IdentityProviderRepresentation representation = new IdentityProviderRepresentation();
|
return config;
|
||||||
|
|
||||||
representation.setId(id);
|
|
||||||
representation.setName(name);
|
|
||||||
representation.setProviderId(providerId);
|
|
||||||
representation.setEnabled(Boolean.valueOf(enabled));
|
|
||||||
representation.setUpdateProfileFirstLogin(Boolean.valueOf(updateProfileFirstLogin));
|
|
||||||
representation.setStoreToken(Boolean.valueOf(storeToken));
|
|
||||||
representation.setConfig(config);
|
|
||||||
|
|
||||||
return create(uriInfo, representation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("instances/{id}")
|
@GET
|
||||||
public IdentityProviderResource getIdentityProvider(@PathParam("id") String providerId) {
|
@Path("instances")
|
||||||
|
@NoCache
|
||||||
|
@Produces("application/json")
|
||||||
|
public List<IdentityProviderRepresentation> getIdentityProviders() {
|
||||||
|
this.auth.requireView();
|
||||||
|
|
||||||
|
List<IdentityProviderRepresentation> representations = new ArrayList<IdentityProviderRepresentation>();
|
||||||
|
|
||||||
|
for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
|
||||||
|
representations.add(ModelToRepresentation.toRepresentation(identityProviderModel));
|
||||||
|
}
|
||||||
|
|
||||||
|
return representations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("instances")
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
public Response create(@Context UriInfo uriInfo, IdentityProviderRepresentation representation) {
|
||||||
|
this.auth.requireManage();
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.realm.addIdentityProvider(RepresentationToModel.toModel(representation));
|
||||||
|
|
||||||
|
return Response.created(uriInfo.getAbsolutePathBuilder().path(representation.getProviderId()).build()).build();
|
||||||
|
} catch (ModelDuplicateException e) {
|
||||||
|
return Flows.errors().exists("Identity Provider " + representation.getAlias() + " already exists");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Path("instances/{alias}")
|
||||||
|
public IdentityProviderResource getIdentityProvider(@PathParam("alias") String alias) {
|
||||||
this.auth.requireView();
|
this.auth.requireView();
|
||||||
IdentityProviderModel identityProviderModel = null;
|
IdentityProviderModel identityProviderModel = null;
|
||||||
|
|
||||||
for (IdentityProviderModel storedIdentityProvider : this.realm.getIdentityProviders()) {
|
for (IdentityProviderModel storedIdentityProvider : this.realm.getIdentityProviders()) {
|
||||||
if (storedIdentityProvider.getId().equals(providerId)
|
if (storedIdentityProvider.getAlias().equals(alias)
|
||||||
|| storedIdentityProvider.getInternalId().equals(providerId)) {
|
|| storedIdentityProvider.getInternalId().equals(alias)) {
|
||||||
identityProviderModel = storedIdentityProvider;
|
identityProviderModel = storedIdentityProvider;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identityProviderModel == null) {
|
if (identityProviderModel == null) {
|
||||||
throw new NotFoundException("Could not find identity provider: " + providerId);
|
throw new NotFoundException("Could not find identity provider: " + alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentityProviderResource identityProviderResource = new IdentityProviderResource(this.auth, realm, session, identityProviderModel);
|
IdentityProviderResource identityProviderResource = new IdentityProviderResource(this.auth, realm, session, identityProviderModel);
|
||||||
|
|
|
@ -84,13 +84,13 @@ public class ServerInfoAdminResource {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSocialProviders(ServerInfoRepresentation info) {
|
private void setSocialProviders(ServerInfoRepresentation info) {
|
||||||
info.socialProviders = new LinkedList<IdentityProviderRepresentation>();
|
info.socialProviders = new LinkedList<>();
|
||||||
List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class);
|
List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class);
|
||||||
setIdentityProviders(providerFactories, info.socialProviders, "Social");
|
setIdentityProviders(providerFactories, info.socialProviders, "Social");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setIdentityProviders(ServerInfoRepresentation info) {
|
private void setIdentityProviders(ServerInfoRepresentation info) {
|
||||||
info.identityProviders = new LinkedList<IdentityProviderRepresentation>();
|
info.identityProviders = new LinkedList<>();
|
||||||
List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class);
|
List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class);
|
||||||
setIdentityProviders(providerFactories, info.identityProviders, "User-defined");
|
setIdentityProviders(providerFactories, info.identityProviders, "User-defined");
|
||||||
|
|
||||||
|
@ -98,24 +98,16 @@ public class ServerInfoAdminResource {
|
||||||
setIdentityProviders(providerFactories, info.identityProviders, "Social");
|
setIdentityProviders(providerFactories, info.identityProviders, "Social");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIdentityProviders(List<ProviderFactory> factories, List<IdentityProviderRepresentation> providers, String groupName) {
|
public void setIdentityProviders(List<ProviderFactory> factories, List<Map<String, String>> providers, String groupName) {
|
||||||
for (ProviderFactory providerFactory : factories) {
|
for (ProviderFactory providerFactory : factories) {
|
||||||
IdentityProviderFactory factory = (IdentityProviderFactory) providerFactory;
|
IdentityProviderFactory factory = (IdentityProviderFactory) providerFactory;
|
||||||
IdentityProviderRepresentation rep = new IdentityProviderRepresentation();
|
Map<String, String> data = new HashMap<>();
|
||||||
|
data.put("groupName", groupName);
|
||||||
|
data.put("name", factory.getName());
|
||||||
|
data.put("id", factory.getId());
|
||||||
|
|
||||||
rep.setId(factory.getId());
|
providers.add(data);
|
||||||
rep.setName(factory.getName());
|
|
||||||
rep.setGroupName(groupName);
|
|
||||||
|
|
||||||
providers.add(rep);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
private void setEventListeners(ServerInfoRepresentation info) {
|
||||||
|
@ -194,8 +186,8 @@ public class ServerInfoAdminResource {
|
||||||
|
|
||||||
private Map<String, List<String>> themes;
|
private Map<String, List<String>> themes;
|
||||||
|
|
||||||
private List<IdentityProviderRepresentation> socialProviders;
|
private List<Map<String, String>> socialProviders;
|
||||||
public List<IdentityProviderRepresentation> identityProviders;
|
public List<Map<String, String>> identityProviders;
|
||||||
private List<String> protocols;
|
private List<String> protocols;
|
||||||
private List<Map<String, String>> applicationImporters;
|
private List<Map<String, String>> applicationImporters;
|
||||||
|
|
||||||
|
@ -220,11 +212,11 @@ public class ServerInfoAdminResource {
|
||||||
return themes;
|
return themes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IdentityProviderRepresentation> getSocialProviders() {
|
public List<Map<String, String>> getSocialProviders() {
|
||||||
return socialProviders;
|
return socialProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IdentityProviderRepresentation> getIdentityProviders() {
|
public List<Map<String, String>> getIdentityProviders() {
|
||||||
return this.identityProviders;
|
return this.identityProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,7 @@ public class UsersResource {
|
||||||
|
|
||||||
for (FederatedIdentityModel identity : identities) {
|
for (FederatedIdentityModel identity : identities) {
|
||||||
for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
|
for (IdentityProviderModel identityProviderModel : realm.getIdentityProviders()) {
|
||||||
if (identityProviderModel.getId().equals(identity.getIdentityProvider())) {
|
if (identityProviderModel.getAlias().equals(identity.getIdentityProvider())) {
|
||||||
FederatedIdentityRepresentation rep = ModelToRepresentation.toRepresentation(identity);
|
FederatedIdentityRepresentation rep = ModelToRepresentation.toRepresentation(identity);
|
||||||
result.add(rep);
|
result.add(rep);
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class Urls {
|
||||||
|
|
||||||
public static URI identityProviderAuthnResponse(URI baseUri, String providerId, String realmName) {
|
public static URI identityProviderAuthnResponse(URI baseUri, String providerId, String realmName) {
|
||||||
return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
|
return realmBase(baseUri).path(RealmsResource.class, "getBrokerService")
|
||||||
.path(IdentityBrokerService.class, "handleResponseGet")
|
.path(IdentityBrokerService.class, "getEndpoint")
|
||||||
.build(realmName, providerId);
|
.build(realmName, providerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,18 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-events-api</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-services</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.codehaus.jackson</groupId>
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
<artifactId>jackson-mapper-asl</artifactId>
|
<artifactId>jackson-mapper-asl</artifactId>
|
||||||
|
|
|
@ -21,25 +21,41 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.social.twitter;
|
package org.keycloak.social.twitter;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.ClientConnection;
|
||||||
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
|
import org.keycloak.broker.oidc.OAuth2IdentityProviderConfig;
|
||||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
|
||||||
import org.keycloak.broker.provider.FederatedIdentity;
|
import org.keycloak.broker.provider.FederatedIdentity;
|
||||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||||
|
import org.keycloak.events.EventBuilder;
|
||||||
|
import org.keycloak.events.EventType;
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ClientSessionModel;
|
import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.services.managers.ClientSessionCode;
|
||||||
|
import org.keycloak.services.managers.EventsManager;
|
||||||
|
import org.keycloak.services.messages.Messages;
|
||||||
|
import org.keycloak.services.resources.flows.Flows;
|
||||||
import org.keycloak.social.SocialIdentityProvider;
|
import org.keycloak.social.SocialIdentityProvider;
|
||||||
import twitter4j.Twitter;
|
import twitter4j.Twitter;
|
||||||
import twitter4j.TwitterFactory;
|
import twitter4j.TwitterFactory;
|
||||||
import twitter4j.auth.AccessToken;
|
import twitter4j.auth.AccessToken;
|
||||||
import twitter4j.auth.RequestToken;
|
import twitter4j.auth.RequestToken;
|
||||||
|
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import static org.keycloak.models.ClientSessionModel.Action.AUTHENTICATE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -47,12 +63,18 @@ import java.net.URI;
|
||||||
public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2IdentityProviderConfig> implements
|
public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2IdentityProviderConfig> implements
|
||||||
SocialIdentityProvider<OAuth2IdentityProviderConfig> {
|
SocialIdentityProvider<OAuth2IdentityProviderConfig> {
|
||||||
|
|
||||||
|
protected static final Logger logger = Logger.getLogger(TwitterIdentityProvider.class);
|
||||||
public TwitterIdentityProvider(OAuth2IdentityProviderConfig config) {
|
public TwitterIdentityProvider(OAuth2IdentityProviderConfig config) {
|
||||||
super(config);
|
super(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationResponse handleRequest(AuthenticationRequest request) {
|
public Object callback(RealmModel realm, AuthenticationCallback callback) {
|
||||||
|
return new Endpoint(realm, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response handleRequest(AuthenticationRequest request) {
|
||||||
try {
|
try {
|
||||||
Twitter twitter = new TwitterFactory().getInstance();
|
Twitter twitter = new TwitterFactory().getInstance();
|
||||||
twitter.setOAuthConsumer(getConfig().getClientId(), getConfig().getClientSecret());
|
twitter.setOAuthConsumer(getConfig().getClientId(), getConfig().getClientSecret());
|
||||||
|
@ -67,63 +89,103 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
||||||
|
|
||||||
URI authenticationUrl = URI.create(requestToken.getAuthenticationURL());
|
URI authenticationUrl = URI.create(requestToken.getAuthenticationURL());
|
||||||
|
|
||||||
return AuthenticationResponse.temporaryRedirect(authenticationUrl);
|
return Response.temporaryRedirect(authenticationUrl).build();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IdentityBrokerException("Could send authentication request to twitter.", e);
|
throw new IdentityBrokerException("Could send authentication request to twitter.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected class Endpoint {
|
||||||
public String getRelayState(AuthenticationRequest request) {
|
protected RealmModel realm;
|
||||||
UriInfo uriInfo = request.getUriInfo();
|
protected AuthenticationCallback callback;
|
||||||
return uriInfo.getQueryParameters().getFirst("state");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Context
|
||||||
public AuthenticationResponse handleResponse(AuthenticationRequest request) {
|
protected KeycloakSession session;
|
||||||
MultivaluedMap<String, String> queryParameters = request.getUriInfo().getQueryParameters();
|
|
||||||
|
|
||||||
if (queryParameters.getFirst("denied") != null) {
|
@Context
|
||||||
throw new IdentityBrokerException("Access denied.");
|
protected ClientConnection clientConnection;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected HttpHeaders headers;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected UriInfo uriInfo;
|
||||||
|
|
||||||
|
public Endpoint(RealmModel realm, AuthenticationCallback callback) {
|
||||||
|
this.realm = realm;
|
||||||
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
@GET
|
||||||
Twitter twitter = new TwitterFactory().getInstance();
|
public Response authResponse(@QueryParam("state") String state,
|
||||||
|
@QueryParam("denied") String denied,
|
||||||
|
@QueryParam("oauth_verifier") String verifier) {
|
||||||
|
|
||||||
twitter.setOAuthConsumer(getConfig().getClientId(), getConfig().getClientSecret());
|
try {
|
||||||
|
Twitter twitter = new TwitterFactory().getInstance();
|
||||||
|
|
||||||
String verifier = queryParameters.getFirst("oauth_verifier");
|
twitter.setOAuthConsumer(getConfig().getClientId(), getConfig().getClientSecret());
|
||||||
|
|
||||||
ClientSessionModel clientSession = request.getClientSession();
|
ClientSessionModel clientSession = parseClientSessionCode(state).getClientSession();
|
||||||
|
|
||||||
String twitterToken = clientSession.getNote("twitter_token");
|
String twitterToken = clientSession.getNote("twitter_token");
|
||||||
String twitterSecret = clientSession.getNote("twitter_tokenSecret");
|
String twitterSecret = clientSession.getNote("twitter_tokenSecret");
|
||||||
|
|
||||||
RequestToken requestToken = new RequestToken(twitterToken, twitterSecret);
|
RequestToken requestToken = new RequestToken(twitterToken, twitterSecret);
|
||||||
|
|
||||||
AccessToken oAuthAccessToken = twitter.getOAuthAccessToken(requestToken, verifier);
|
AccessToken oAuthAccessToken = twitter.getOAuthAccessToken(requestToken, verifier);
|
||||||
twitter4j.User twitterUser = twitter.verifyCredentials();
|
twitter4j.User twitterUser = twitter.verifyCredentials();
|
||||||
|
|
||||||
FederatedIdentity identity = new FederatedIdentity(Long.toString(twitterUser.getId()));
|
FederatedIdentity identity = new FederatedIdentity(Long.toString(twitterUser.getId()));
|
||||||
|
|
||||||
identity.setUsername(twitterUser.getScreenName());
|
identity.setUsername(twitterUser.getScreenName());
|
||||||
identity.setName(twitterUser.getName());
|
identity.setName(twitterUser.getName());
|
||||||
|
|
||||||
StringBuilder tokenBuilder = new StringBuilder();
|
StringBuilder tokenBuilder = new StringBuilder();
|
||||||
|
|
||||||
tokenBuilder.append("{");
|
tokenBuilder.append("{");
|
||||||
tokenBuilder.append("\"oauth_token\":").append("\"").append(oAuthAccessToken.getToken()).append("\"").append(",");
|
tokenBuilder.append("\"oauth_token\":").append("\"").append(oAuthAccessToken.getToken()).append("\"").append(",");
|
||||||
tokenBuilder.append("\"oauth_token_secret\":").append("\"").append(oAuthAccessToken.getTokenSecret()).append("\"").append(",");
|
tokenBuilder.append("\"oauth_token_secret\":").append("\"").append(oAuthAccessToken.getTokenSecret()).append("\"").append(",");
|
||||||
tokenBuilder.append("\"screen_name\":").append("\"").append(oAuthAccessToken.getScreenName()).append("\"").append(",");
|
tokenBuilder.append("\"screen_name\":").append("\"").append(oAuthAccessToken.getScreenName()).append("\"").append(",");
|
||||||
tokenBuilder.append("\"user_id\":").append("\"").append(oAuthAccessToken.getUserId()).append("\"");
|
tokenBuilder.append("\"user_id\":").append("\"").append(oAuthAccessToken.getUserId()).append("\"");
|
||||||
tokenBuilder.append("}");
|
tokenBuilder.append("}");
|
||||||
|
|
||||||
identity.setToken(tokenBuilder.toString());
|
identity.setToken(tokenBuilder.toString());
|
||||||
|
|
||||||
return AuthenticationResponse.end(identity);
|
return callback.authenticated(new HashMap<String, String>(), getConfig(), identity, state);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IdentityBrokerException("Could get user profile from twitter.", e);
|
logger.error("Could get user profile from twitter.", e);
|
||||||
|
}
|
||||||
|
EventBuilder event = new EventsManager(realm, session, clientConnection).createEventBuilder();
|
||||||
|
event.event(EventType.LOGIN);
|
||||||
|
event.error("twitter_login_failed");
|
||||||
|
return Flows.forwardToSecurityFailurePage(session, realm, uriInfo, headers, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClientSessionCode parseClientSessionCode(String code) {
|
||||||
|
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realm);
|
||||||
|
|
||||||
|
if (clientCode != null && clientCode.isValid(AUTHENTICATE)) {
|
||||||
|
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||||
|
|
||||||
|
if (clientSession != null) {
|
||||||
|
ClientModel client = clientSession.getClient();
|
||||||
|
|
||||||
|
if (client == null) {
|
||||||
|
throw new IdentityBrokerException("Invalid client");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debugf("Got authorization code from client [%s].", client.getClientId());
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debugf("Authorization code is valid.");
|
||||||
|
|
||||||
|
return clientCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IdentityBrokerException("Invalid code, please login again through your application.");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -102,7 +102,7 @@ public abstract class AbstractClientTest {
|
||||||
} else if (o1 instanceof OAuthClientRepresentation) {
|
} else if (o1 instanceof OAuthClientRepresentation) {
|
||||||
return ((OAuthClientRepresentation) o1).getName();
|
return ((OAuthClientRepresentation) o1).getName();
|
||||||
} else if (o1 instanceof IdentityProviderRepresentation) {
|
} else if (o1 instanceof IdentityProviderRepresentation) {
|
||||||
return ((IdentityProviderRepresentation) o1).getId();
|
return ((IdentityProviderRepresentation) o1).getAlias();
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
10
testsuite/integration/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java
Normal file → Executable file
10
testsuite/integration/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java
Normal file → Executable file
|
@ -46,8 +46,7 @@ public class IdentityProviderTest extends AbstractClientTest {
|
||||||
assertNotNull(representation);
|
assertNotNull(representation);
|
||||||
|
|
||||||
assertNotNull(representation.getInternalId());
|
assertNotNull(representation.getInternalId());
|
||||||
assertEquals("New Identity Provider", representation.getName());
|
assertEquals("new-identity-provider", representation.getAlias());
|
||||||
assertEquals("new-identity-provider", representation.getId());
|
|
||||||
assertEquals("oidc", representation.getProviderId());
|
assertEquals("oidc", representation.getProviderId());
|
||||||
assertEquals("clientId", representation.getConfig().get("clientId"));
|
assertEquals("clientId", representation.getConfig().get("clientId"));
|
||||||
assertEquals("clientSecret", representation.getConfig().get("clientSecret"));
|
assertEquals("clientSecret", representation.getConfig().get("clientSecret"));
|
||||||
|
@ -72,9 +71,9 @@ public class IdentityProviderTest extends AbstractClientTest {
|
||||||
|
|
||||||
assertNotNull(representation);
|
assertNotNull(representation);
|
||||||
|
|
||||||
assertEquals("update-identity-provider", representation.getId());
|
assertEquals("update-identity-provider", representation.getAlias());
|
||||||
|
|
||||||
representation.setId("changed-alias");
|
representation.setAlias("changed-alias");
|
||||||
representation.setEnabled(false);
|
representation.setEnabled(false);
|
||||||
representation.setStoreToken(true);
|
representation.setStoreToken(true);
|
||||||
representation.getConfig().put("clientId", "changedClientId");
|
representation.getConfig().put("clientId", "changedClientId");
|
||||||
|
@ -113,9 +112,8 @@ public class IdentityProviderTest extends AbstractClientTest {
|
||||||
private IdentityProviderRepresentation create(String id, String providerId, String name) {
|
private IdentityProviderRepresentation create(String id, String providerId, String name) {
|
||||||
IdentityProviderRepresentation identityProviderRepresentation = new IdentityProviderRepresentation();
|
IdentityProviderRepresentation identityProviderRepresentation = new IdentityProviderRepresentation();
|
||||||
|
|
||||||
identityProviderRepresentation.setId(id);
|
identityProviderRepresentation.setAlias(id);
|
||||||
identityProviderRepresentation.setProviderId(providerId);
|
identityProviderRepresentation.setProviderId(providerId);
|
||||||
identityProviderRepresentation.setName(name);
|
|
||||||
identityProviderRepresentation.setEnabled(true);
|
identityProviderRepresentation.setEnabled(true);
|
||||||
|
|
||||||
return identityProviderRepresentation;
|
return identityProviderRepresentation;
|
||||||
|
|
|
@ -227,8 +227,7 @@ public class UserTest extends AbstractClientTest {
|
||||||
Assert.assertEquals(0, providers.size());
|
Assert.assertEquals(0, providers.size());
|
||||||
|
|
||||||
IdentityProviderRepresentation rep = new IdentityProviderRepresentation();
|
IdentityProviderRepresentation rep = new IdentityProviderRepresentation();
|
||||||
rep.setId("social-provider-id");
|
rep.setAlias("social-provider-id");
|
||||||
rep.setName("social-provider-name");
|
|
||||||
rep.setProviderId("social-provider-type");
|
rep.setProviderId("social-provider-type");
|
||||||
realm.identityProviders().create(rep);
|
realm.identityProviders().create(rep);
|
||||||
}
|
}
|
||||||
|
|
18
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
Normal file → Executable file
18
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
Normal file → Executable file
|
@ -369,7 +369,7 @@ public abstract class AbstractIdentityProviderTest {
|
||||||
|
|
||||||
// Link my "pedroigor" identity with "test-user" from brokered Keycloak
|
// Link my "pedroigor" identity with "test-user" from brokered Keycloak
|
||||||
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
|
||||||
accountFederatedIdentityPage.clickAddProvider(identityProviderModel.getId());
|
accountFederatedIdentityPage.clickAddProvider(identityProviderModel.getAlias());
|
||||||
|
|
||||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
|
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
|
||||||
this.loginPage.login("test-user", "password");
|
this.loginPage.login("test-user", "password");
|
||||||
|
@ -377,28 +377,28 @@ public abstract class AbstractIdentityProviderTest {
|
||||||
|
|
||||||
// Assert identity linked in account management
|
// Assert identity linked in account management
|
||||||
assertTrue(accountFederatedIdentityPage.isCurrent());
|
assertTrue(accountFederatedIdentityPage.isCurrent());
|
||||||
assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getId() + "\""));
|
assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
|
||||||
|
|
||||||
// Logout from account management
|
// Logout from account management
|
||||||
accountFederatedIdentityPage.logout();
|
accountFederatedIdentityPage.logout();
|
||||||
assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
|
assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
|
||||||
|
|
||||||
// Assert I am logged immediately to account management due to previously linked "test-user" identity
|
// Assert I am logged immediately to account management due to previously linked "test-user" identity
|
||||||
loginPage.clickSocial(identityProviderModel.getId());
|
loginPage.clickSocial(identityProviderModel.getAlias());
|
||||||
doAfterProviderAuthentication();
|
doAfterProviderAuthentication();
|
||||||
assertTrue(accountFederatedIdentityPage.isCurrent());
|
assertTrue(accountFederatedIdentityPage.isCurrent());
|
||||||
assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getId() + "\""));
|
assertTrue(driver.getPageSource().contains("id=\"remove-" + identityProviderModel.getAlias() + "\""));
|
||||||
|
|
||||||
// Unlink my "test-user"
|
// Unlink my "test-user"
|
||||||
accountFederatedIdentityPage.clickRemoveProvider(identityProviderModel.getId());
|
accountFederatedIdentityPage.clickRemoveProvider(identityProviderModel.getAlias());
|
||||||
assertTrue(driver.getPageSource().contains("id=\"add-" + identityProviderModel.getId() + "\""));
|
assertTrue(driver.getPageSource().contains("id=\"add-" + identityProviderModel.getAlias() + "\""));
|
||||||
|
|
||||||
// Logout from account management
|
// Logout from account management
|
||||||
accountFederatedIdentityPage.logout();
|
accountFederatedIdentityPage.logout();
|
||||||
assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
|
assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
|
||||||
|
|
||||||
// Try to login. Previous link is not valid anymore, so now it should try to register new user
|
// Try to login. Previous link is not valid anymore, so now it should try to register new user
|
||||||
this.loginPage.clickSocial(identityProviderModel.getId());
|
this.loginPage.clickSocial(identityProviderModel.getAlias());
|
||||||
doAfterProviderAuthentication();
|
doAfterProviderAuthentication();
|
||||||
this.updateProfilePage.assertCurrent();
|
this.updateProfilePage.assertCurrent();
|
||||||
}
|
}
|
||||||
|
@ -589,7 +589,7 @@ public abstract class AbstractIdentityProviderTest {
|
||||||
this.loginPage.clickSocial(getProviderId());
|
this.loginPage.clickSocial(getProviderId());
|
||||||
|
|
||||||
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
|
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
|
||||||
|
System.out.println(this.driver.getCurrentUrl());
|
||||||
// log in to identity provider
|
// log in to identity provider
|
||||||
this.loginPage.login(username, "password");
|
this.loginPage.login(username, "password");
|
||||||
|
|
||||||
|
@ -622,7 +622,7 @@ public abstract class AbstractIdentityProviderTest {
|
||||||
protected abstract String getProviderId();
|
protected abstract String getProviderId();
|
||||||
|
|
||||||
protected IdentityProviderModel getIdentityProviderModel() {
|
protected IdentityProviderModel getIdentityProviderModel() {
|
||||||
IdentityProviderModel identityProviderModel = getRealm().getIdentityProviderById(getProviderId());
|
IdentityProviderModel identityProviderModel = getRealm().getIdentityProviderByAlias(getProviderId());
|
||||||
|
|
||||||
assertNotNull(identityProviderModel);
|
assertNotNull(identityProviderModel);
|
||||||
|
|
||||||
|
|
8
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderRegistrationTest.java
Normal file → Executable file
8
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/IdentityProviderRegistrationTest.java
Normal file → Executable file
|
@ -61,7 +61,7 @@ public class IdentityProviderRegistrationTest extends AbstractIdentityProviderMo
|
||||||
|
|
||||||
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
||||||
|
|
||||||
identityProviderModel.setId("custom-provider");
|
identityProviderModel.setAlias("custom-provider");
|
||||||
|
|
||||||
CustomSocialProvider customSocialProvider = providerFactory.create(identityProviderModel);
|
CustomSocialProvider customSocialProvider = providerFactory.create(identityProviderModel);
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ public class IdentityProviderRegistrationTest extends AbstractIdentityProviderMo
|
||||||
IdentityProviderModel config = customSocialProvider.getConfig();
|
IdentityProviderModel config = customSocialProvider.getConfig();
|
||||||
|
|
||||||
assertNotNull(config);
|
assertNotNull(config);
|
||||||
assertEquals("custom-provider", config.getId());
|
assertEquals("custom-provider", config.getAlias());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -84,7 +84,7 @@ public class IdentityProviderRegistrationTest extends AbstractIdentityProviderMo
|
||||||
|
|
||||||
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
IdentityProviderModel identityProviderModel = new IdentityProviderModel();
|
||||||
|
|
||||||
identityProviderModel.setId("custom-provider");
|
identityProviderModel.setAlias("custom-provider");
|
||||||
|
|
||||||
CustomIdentityProvider provider = providerFactory.create(identityProviderModel);
|
CustomIdentityProvider provider = providerFactory.create(identityProviderModel);
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ public class IdentityProviderRegistrationTest extends AbstractIdentityProviderMo
|
||||||
IdentityProviderModel config = provider.getConfig();
|
IdentityProviderModel config = provider.getConfig();
|
||||||
|
|
||||||
assertNotNull(config);
|
assertNotNull(config);
|
||||||
assertEquals("custom-provider", config.getId());
|
assertEquals("custom-provider", config.getAlias());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> getInstalledProviders() {
|
private Set<String> getInstalledProviders() {
|
||||||
|
|
|
@ -72,9 +72,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
assertFalse(identityProviders.isEmpty());
|
assertFalse(identityProviders.isEmpty());
|
||||||
|
|
||||||
IdentityProviderModel identityProviderModel = identityProviders.get(0);
|
IdentityProviderModel identityProviderModel = identityProviders.get(0);
|
||||||
String identityProviderId = identityProviderModel.getId();
|
String identityProviderId = identityProviderModel.getAlias();
|
||||||
|
|
||||||
identityProviderModel.setName("Changed Name");
|
|
||||||
identityProviderModel.getConfig().put("config-added", "value-added");
|
identityProviderModel.getConfig().put("config-added", "value-added");
|
||||||
identityProviderModel.setEnabled(false);
|
identityProviderModel.setEnabled(false);
|
||||||
identityProviderModel.setUpdateProfileFirstLogin(false);
|
identityProviderModel.setUpdateProfileFirstLogin(false);
|
||||||
|
@ -87,16 +86,14 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
|
|
||||||
realm = this.realmManager.getRealm(realm.getId());
|
realm = this.realmManager.getRealm(realm.getId());
|
||||||
|
|
||||||
identityProviderModel = realm.getIdentityProviderById(identityProviderId);
|
identityProviderModel = realm.getIdentityProviderByAlias(identityProviderId);
|
||||||
|
|
||||||
assertEquals("Changed Name", identityProviderModel.getName());
|
|
||||||
assertEquals("value-added", identityProviderModel.getConfig().get("config-added"));
|
assertEquals("value-added", identityProviderModel.getConfig().get("config-added"));
|
||||||
assertFalse(identityProviderModel.isEnabled());
|
assertFalse(identityProviderModel.isEnabled());
|
||||||
assertFalse(identityProviderModel.isUpdateProfileFirstLogin());
|
assertFalse(identityProviderModel.isUpdateProfileFirstLogin());
|
||||||
assertTrue(identityProviderModel.isStoreToken());
|
assertTrue(identityProviderModel.isStoreToken());
|
||||||
assertTrue(identityProviderModel.isAuthenticateByDefault());
|
assertTrue(identityProviderModel.isAuthenticateByDefault());
|
||||||
|
|
||||||
identityProviderModel.setName("Changed Name Again");
|
|
||||||
identityProviderModel.getConfig().remove("config-added");
|
identityProviderModel.getConfig().remove("config-added");
|
||||||
identityProviderModel.setEnabled(true);
|
identityProviderModel.setEnabled(true);
|
||||||
identityProviderModel.setUpdateProfileFirstLogin(true);
|
identityProviderModel.setUpdateProfileFirstLogin(true);
|
||||||
|
@ -107,9 +104,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
commit();
|
commit();
|
||||||
|
|
||||||
realm = this.realmManager.getRealm(realm.getId());
|
realm = this.realmManager.getRealm(realm.getId());
|
||||||
identityProviderModel = realm.getIdentityProviderById(identityProviderId);
|
identityProviderModel = realm.getIdentityProviderByAlias(identityProviderId);
|
||||||
|
|
||||||
assertEquals("Changed Name Again", identityProviderModel.getName());
|
|
||||||
assertFalse(identityProviderModel.getConfig().containsKey("config-added"));
|
assertFalse(identityProviderModel.getConfig().containsKey("config-added"));
|
||||||
assertTrue(identityProviderModel.isEnabled());
|
assertTrue(identityProviderModel.isEnabled());
|
||||||
assertTrue(identityProviderModel.isUpdateProfileFirstLogin());
|
assertTrue(identityProviderModel.isUpdateProfileFirstLogin());
|
||||||
|
@ -149,7 +145,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
Set<String> checkedProviders = new HashSet<String>(getExpectedProviders());
|
Set<String> checkedProviders = new HashSet<String>(getExpectedProviders());
|
||||||
|
|
||||||
for (IdentityProviderModel identityProvider : identityProviders) {
|
for (IdentityProviderModel identityProvider : identityProviders) {
|
||||||
if (identityProvider.getId().startsWith("model-")) {
|
if (identityProvider.getAlias().startsWith("model-")) {
|
||||||
String providerId = identityProvider.getProviderId();
|
String providerId = identityProvider.getProviderId();
|
||||||
|
|
||||||
if (SAMLIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
|
if (SAMLIdentityProviderFactory.PROVIDER_ID.equals(providerId)) {
|
||||||
|
@ -179,9 +175,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
GoogleIdentityProvider googleIdentityProvider = new GoogleIdentityProviderFactory().create(identityProvider);
|
GoogleIdentityProvider googleIdentityProvider = new GoogleIdentityProviderFactory().create(identityProvider);
|
||||||
OIDCIdentityProviderConfig config = googleIdentityProvider.getConfig();
|
OIDCIdentityProviderConfig config = googleIdentityProvider.getConfig();
|
||||||
|
|
||||||
assertEquals("model-google", config.getId());
|
assertEquals("model-google", config.getAlias());
|
||||||
assertEquals(GoogleIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
assertEquals(GoogleIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
||||||
assertEquals("Google", config.getName());
|
|
||||||
assertEquals(true, config.isEnabled());
|
assertEquals(true, config.isEnabled());
|
||||||
assertEquals(true, config.isUpdateProfileFirstLogin());
|
assertEquals(true, config.isUpdateProfileFirstLogin());
|
||||||
assertEquals(false, config.isAuthenticateByDefault());
|
assertEquals(false, config.isAuthenticateByDefault());
|
||||||
|
@ -198,9 +193,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
SAMLIdentityProvider samlIdentityProvider = new SAMLIdentityProviderFactory().create(identityProvider);
|
SAMLIdentityProvider samlIdentityProvider = new SAMLIdentityProviderFactory().create(identityProvider);
|
||||||
SAMLIdentityProviderConfig config = samlIdentityProvider.getConfig();
|
SAMLIdentityProviderConfig config = samlIdentityProvider.getConfig();
|
||||||
|
|
||||||
assertEquals("model-saml-signed-idp", config.getId());
|
assertEquals("model-saml-signed-idp", config.getAlias());
|
||||||
assertEquals(SAMLIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
assertEquals(SAMLIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
||||||
assertEquals("SAML Signed IdP", config.getName());
|
|
||||||
assertEquals(true, config.isEnabled());
|
assertEquals(true, config.isEnabled());
|
||||||
assertEquals(true, config.isUpdateProfileFirstLogin());
|
assertEquals(true, config.isUpdateProfileFirstLogin());
|
||||||
assertEquals(false, config.isAuthenticateByDefault());
|
assertEquals(false, config.isAuthenticateByDefault());
|
||||||
|
@ -219,9 +213,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
OIDCIdentityProvider googleIdentityProvider = new OIDCIdentityProviderFactory().create(identityProvider);
|
OIDCIdentityProvider googleIdentityProvider = new OIDCIdentityProviderFactory().create(identityProvider);
|
||||||
OIDCIdentityProviderConfig config = googleIdentityProvider.getConfig();
|
OIDCIdentityProviderConfig config = googleIdentityProvider.getConfig();
|
||||||
|
|
||||||
assertEquals("model-oidc-idp", config.getId());
|
assertEquals("model-oidc-idp", config.getAlias());
|
||||||
assertEquals(OIDCIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
assertEquals(OIDCIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
||||||
assertEquals("OIDC IdP", config.getName());
|
|
||||||
assertEquals(false, config.isEnabled());
|
assertEquals(false, config.isEnabled());
|
||||||
assertEquals(false, config.isUpdateProfileFirstLogin());
|
assertEquals(false, config.isUpdateProfileFirstLogin());
|
||||||
assertEquals(false, config.isAuthenticateByDefault());
|
assertEquals(false, config.isAuthenticateByDefault());
|
||||||
|
@ -234,9 +227,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
FacebookIdentityProvider facebookIdentityProvider = new FacebookIdentityProviderFactory().create(identityProvider);
|
FacebookIdentityProvider facebookIdentityProvider = new FacebookIdentityProviderFactory().create(identityProvider);
|
||||||
OAuth2IdentityProviderConfig config = facebookIdentityProvider.getConfig();
|
OAuth2IdentityProviderConfig config = facebookIdentityProvider.getConfig();
|
||||||
|
|
||||||
assertEquals("model-facebook", config.getId());
|
assertEquals("model-facebook", config.getAlias());
|
||||||
assertEquals(FacebookIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
assertEquals(FacebookIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
||||||
assertEquals("Facebook", config.getName());
|
|
||||||
assertEquals(true, config.isEnabled());
|
assertEquals(true, config.isEnabled());
|
||||||
assertEquals(true, config.isUpdateProfileFirstLogin());
|
assertEquals(true, config.isUpdateProfileFirstLogin());
|
||||||
assertEquals(false, config.isAuthenticateByDefault());
|
assertEquals(false, config.isAuthenticateByDefault());
|
||||||
|
@ -252,9 +244,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
GitHubIdentityProvider gitHubIdentityProvider = new GitHubIdentityProviderFactory().create(identityProvider);
|
GitHubIdentityProvider gitHubIdentityProvider = new GitHubIdentityProviderFactory().create(identityProvider);
|
||||||
OAuth2IdentityProviderConfig config = gitHubIdentityProvider.getConfig();
|
OAuth2IdentityProviderConfig config = gitHubIdentityProvider.getConfig();
|
||||||
|
|
||||||
assertEquals("model-github", config.getId());
|
assertEquals("model-github", config.getAlias());
|
||||||
assertEquals(GitHubIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
assertEquals(GitHubIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
||||||
assertEquals("GitHub", config.getName());
|
|
||||||
assertEquals(true, config.isEnabled());
|
assertEquals(true, config.isEnabled());
|
||||||
assertEquals(true, config.isUpdateProfileFirstLogin());
|
assertEquals(true, config.isUpdateProfileFirstLogin());
|
||||||
assertEquals(false, config.isAuthenticateByDefault());
|
assertEquals(false, config.isAuthenticateByDefault());
|
||||||
|
@ -270,9 +261,8 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
|
||||||
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(identityProvider);
|
TwitterIdentityProvider twitterIdentityProvider = new TwitterIdentityProviderFactory().create(identityProvider);
|
||||||
OAuth2IdentityProviderConfig config = twitterIdentityProvider.getConfig();
|
OAuth2IdentityProviderConfig config = twitterIdentityProvider.getConfig();
|
||||||
|
|
||||||
assertEquals("model-twitter", config.getId());
|
assertEquals("model-twitter", config.getAlias());
|
||||||
assertEquals(TwitterIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
assertEquals(TwitterIdentityProviderFactory.PROVIDER_ID, config.getProviderId());
|
||||||
assertEquals("Twitter", config.getName());
|
|
||||||
assertEquals(true, config.isEnabled());
|
assertEquals(true, config.isEnabled());
|
||||||
assertEquals(true, config.isUpdateProfileFirstLogin());
|
assertEquals(true, config.isUpdateProfileFirstLogin());
|
||||||
assertEquals(false, config.isAuthenticateByDefault());
|
assertEquals(false, config.isAuthenticateByDefault());
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.testsuite.broker;
|
package org.keycloak.testsuite.broker;
|
||||||
|
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
|
import org.junit.Test;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -70,7 +71,8 @@ public class SAMLKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
|
||||||
try {
|
try {
|
||||||
SAML2Request saml2Request = new SAML2Request();
|
SAML2Request saml2Request = new SAML2Request();
|
||||||
ResponseType responseType = (ResponseType) saml2Request
|
ResponseType responseType = (ResponseType) saml2Request
|
||||||
.getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(pageSource, "UTF-8")));
|
.getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(pageSource));
|
||||||
|
//.getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(pageSource, "UTF-8")));
|
||||||
|
|
||||||
assertNotNull(responseType);
|
assertNotNull(responseType);
|
||||||
assertFalse(responseType.getAssertions().isEmpty());
|
assertFalse(responseType.getAssertions().isEmpty());
|
||||||
|
@ -78,4 +80,16 @@ public class SAMLKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
|
||||||
fail("Could not parse token.");
|
fail("Could not parse token.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Test
|
||||||
|
public void testSuccessfulAuthenticationWithoutUpdateProfile() {
|
||||||
|
super.testSuccessfulAuthenticationWithoutUpdateProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Test
|
||||||
|
public void testTokenStorageAndRetrievalByOAuthClient() {
|
||||||
|
super.testTokenStorageAndRetrievalByOAuthClient();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractIdentityP
|
||||||
try {
|
try {
|
||||||
SAML2Request saml2Request = new SAML2Request();
|
SAML2Request saml2Request = new SAML2Request();
|
||||||
ResponseType responseType = (ResponseType) saml2Request
|
ResponseType responseType = (ResponseType) saml2Request
|
||||||
.getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(URLDecoder.decode(pageSource, "UTF-8")));
|
.getSAML2ObjectFromStream(PostBindingUtil.base64DecodeAsStream(pageSource));
|
||||||
|
|
||||||
assertNotNull(responseType);
|
assertNotNull(responseType);
|
||||||
assertFalse(responseType.getAssertions().isEmpty());
|
assertFalse(responseType.getAssertions().isEmpty());
|
||||||
|
|
13
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/CustomIdentityProvider.java
Normal file → Executable file
13
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/CustomIdentityProvider.java
Normal file → Executable file
|
@ -19,7 +19,6 @@ package org.keycloak.testsuite.broker.provider;
|
||||||
|
|
||||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
|
|
||||||
|
@ -35,17 +34,7 @@ public class CustomIdentityProvider extends AbstractIdentityProvider<IdentityPro
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationResponse handleRequest(AuthenticationRequest request) {
|
public Response handleRequest(AuthenticationRequest request) {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRelayState(AuthenticationRequest request) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthenticationResponse handleResponse(AuthenticationRequest request) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
13
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/social/CustomSocialProvider.java
Normal file → Executable file
13
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/provider/social/CustomSocialProvider.java
Normal file → Executable file
|
@ -19,7 +19,6 @@ package org.keycloak.testsuite.broker.provider.social;
|
||||||
|
|
||||||
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
import org.keycloak.broker.provider.AbstractIdentityProvider;
|
||||||
import org.keycloak.broker.provider.AuthenticationRequest;
|
import org.keycloak.broker.provider.AuthenticationRequest;
|
||||||
import org.keycloak.broker.provider.AuthenticationResponse;
|
|
||||||
import org.keycloak.models.FederatedIdentityModel;
|
import org.keycloak.models.FederatedIdentityModel;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.social.SocialIdentityProvider;
|
import org.keycloak.social.SocialIdentityProvider;
|
||||||
|
@ -36,17 +35,7 @@ public class CustomSocialProvider extends AbstractIdentityProvider<IdentityProvi
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationResponse handleRequest(AuthenticationRequest request) {
|
public Response handleRequest(AuthenticationRequest request) {
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getRelayState(AuthenticationRequest request) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AuthenticationResponse handleResponse(AuthenticationRequest request) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -207,9 +207,8 @@ public class ImportTest extends AbstractModelTest {
|
||||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||||
Assert.assertEquals(1, identityProviders.size());
|
Assert.assertEquals(1, identityProviders.size());
|
||||||
IdentityProviderModel google = identityProviders.get(0);
|
IdentityProviderModel google = identityProviders.get(0);
|
||||||
Assert.assertEquals("google1", google.getId());
|
Assert.assertEquals("google1", google.getAlias());
|
||||||
Assert.assertEquals("google", google.getProviderId());
|
Assert.assertEquals("google", google.getProviderId());
|
||||||
Assert.assertEquals("Google", google.getName());
|
|
||||||
Assert.assertTrue(google.isEnabled());
|
Assert.assertTrue(google.isEnabled());
|
||||||
Assert.assertEquals("googleId", google.getConfig().get("clientId"));
|
Assert.assertEquals("googleId", google.getConfig().get("clientId"));
|
||||||
Assert.assertEquals("googleSecret", google.getConfig().get("clientSecret"));
|
Assert.assertEquals("googleSecret", google.getConfig().get("clientSecret"));
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"secret": "secret",
|
"secret": "secret",
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-oidc-idp"
|
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-oidc-idp/endpoint"
|
||||||
],
|
],
|
||||||
"claims": {
|
"claims": {
|
||||||
"name" : true,
|
"name" : true,
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
"name": "http://localhost:8081/auth/realms/realm-with-broker",
|
"name": "http://localhost:8081/auth/realms/realm-with-broker",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-signed-idp"
|
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-signed-idp/endpoint"
|
||||||
],
|
],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml.assertion.signature": "true",
|
"saml.assertion.signature": "true",
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
"name": "http://localhost:8081/auth/realms/realm-with-broker",
|
"name": "http://localhost:8081/auth/realms/realm-with-broker",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic"
|
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint"
|
||||||
],
|
],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml.authnstatement": "true"
|
"saml.authnstatement": "true"
|
||||||
|
|
|
@ -10,9 +10,8 @@
|
||||||
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgj8r0029eL0jJKXv6XbNj+QqsZO25HhZ0IjTEtb8mfh0tju/X8c6dXgILh5wU7OF00U+0mSYSE/+rrYKmY5g4oCleTe1+abavATP1tamtXGAUYqdutaXPrVn9yMsCWEPchSPZlEGq5iBJdA+xh9ejUmZJYXmln26HUVWq71/jC9GpjbRmFQ37f0X7WJoGyiqyttfKkKfUeBmRbX/0P0Zm6DVze8HjCDVPBllZE0a3HCgSF0rp0+s1xn7o91qdWKVattAVsGNjjDPz/sgwHOyyhDtSyajwXU+K/QUZ9pV4moGtwC9uIEymTylP7bu7qnxXIhfouEa+fEjAzTs0HJ5JQIDAQAB",
|
"publicKey": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgj8r0029eL0jJKXv6XbNj+QqsZO25HhZ0IjTEtb8mfh0tju/X8c6dXgILh5wU7OF00U+0mSYSE/+rrYKmY5g4oCleTe1+abavATP1tamtXGAUYqdutaXPrVn9yMsCWEPchSPZlEGq5iBJdA+xh9ejUmZJYXmln26HUVWq71/jC9GpjbRmFQ37f0X7WJoGyiqyttfKkKfUeBmRbX/0P0Zm6DVze8HjCDVPBllZE0a3HCgSF0rp0+s1xn7o91qdWKVattAVsGNjjDPz/sgwHOyyhDtSyajwXU+K/QUZ9pV4moGtwC9uIEymTylP7bu7qnxXIhfouEa+fEjAzTs0HJ5JQIDAQAB",
|
||||||
"identityProviders" : [
|
"identityProviders" : [
|
||||||
{
|
{
|
||||||
"id" : "model-google",
|
"alias" : "model-google",
|
||||||
"providerId" : "google",
|
"providerId" : "google",
|
||||||
"name" : "Google",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"updateProfileFirstLogin" : "true",
|
"updateProfileFirstLogin" : "true",
|
||||||
"storeToken": "true",
|
"storeToken": "true",
|
||||||
|
@ -22,9 +21,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : "model-facebook",
|
"alias" : "model-facebook",
|
||||||
"providerId" : "facebook",
|
"providerId" : "facebook",
|
||||||
"name" : "Facebook",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"updateProfileFirstLogin" : "true",
|
"updateProfileFirstLogin" : "true",
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -36,9 +34,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : "model-github",
|
"alias" : "model-github",
|
||||||
"providerId" : "github",
|
"providerId" : "github",
|
||||||
"name" : "GitHub",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"updateProfileFirstLogin" : "true",
|
"updateProfileFirstLogin" : "true",
|
||||||
"storeToken": "false",
|
"storeToken": "false",
|
||||||
|
@ -51,9 +48,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : "model-twitter",
|
"alias" : "model-twitter",
|
||||||
"providerId" : "twitter",
|
"providerId" : "twitter",
|
||||||
"name" : "Twitter",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"updateProfileFirstLogin" : "true",
|
"updateProfileFirstLogin" : "true",
|
||||||
"storeToken": true,
|
"storeToken": true,
|
||||||
|
@ -66,9 +62,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : "model-saml-signed-idp",
|
"alias" : "model-saml-signed-idp",
|
||||||
"providerId" : "saml",
|
"providerId" : "saml",
|
||||||
"name" : "SAML Signed IdP",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"updateProfileFirstLogin" : "true",
|
"updateProfileFirstLogin" : "true",
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -83,9 +78,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : "kc-saml-signed-idp",
|
"alias" : "kc-saml-signed-idp",
|
||||||
"providerId" : "saml",
|
"providerId" : "saml",
|
||||||
"name" : "SAML Signed IdP",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"updateProfileFirstLogin" : "true",
|
"updateProfileFirstLogin" : "true",
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -100,9 +94,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : "kc-saml-idp-basic",
|
"alias" : "kc-saml-idp-basic",
|
||||||
"providerId" : "saml",
|
"providerId" : "saml",
|
||||||
"name" : "SAML Signed IdP",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"updateProfileFirstLogin" : "true",
|
"updateProfileFirstLogin" : "true",
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -114,9 +107,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : "model-oidc-idp",
|
"alias" : "model-oidc-idp",
|
||||||
"providerId" : "oidc",
|
"providerId" : "oidc",
|
||||||
"name" : "OIDC IdP",
|
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"updateProfileFirstLogin" : "false",
|
"updateProfileFirstLogin" : "false",
|
||||||
"authenticateByDefault" : "false",
|
"authenticateByDefault" : "false",
|
||||||
|
@ -131,9 +123,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : "kc-oidc-idp",
|
"alias" : "kc-oidc-idp",
|
||||||
"providerId" : "oidc",
|
"providerId" : "oidc",
|
||||||
"name" : "KeyCloak OIDC IdP",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"updateProfileFirstLogin" : "false",
|
"updateProfileFirstLogin" : "false",
|
||||||
"config": {
|
"config": {
|
||||||
|
@ -147,9 +138,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id" : "model-kerberos",
|
"alias" : "model-kerberos",
|
||||||
"providerId" : "kerberos",
|
"providerId" : "kerberos",
|
||||||
"name" : "Kerberos",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"updateProfileFirstLogin" : "true",
|
"updateProfileFirstLogin" : "true",
|
||||||
"authenticateByDefault" : "false",
|
"authenticateByDefault" : "false",
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
"identityProviders" : [
|
"identityProviders" : [
|
||||||
{
|
{
|
||||||
"providerId" : "google",
|
"providerId" : "google",
|
||||||
"id" : "google1",
|
"alias" : "google1",
|
||||||
"name" : "Google",
|
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"config": {
|
"config": {
|
||||||
"clientId": "googleId",
|
"clientId": "googleId",
|
||||||
|
|
|
@ -45,10 +45,10 @@
|
||||||
],
|
],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml.authnstatement": "true",
|
"saml.authnstatement": "true",
|
||||||
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post",
|
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post/",
|
||||||
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post",
|
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post/",
|
||||||
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post",
|
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post/",
|
||||||
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post"
|
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -61,10 +61,10 @@
|
||||||
"http://localhost:8081/sales-post-sig/*"
|
"http://localhost:8081/sales-post-sig/*"
|
||||||
],
|
],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig",
|
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig/",
|
||||||
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig",
|
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig/",
|
||||||
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig",
|
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig/",
|
||||||
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig",
|
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig/",
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.signature.algorithm": "RSA_SHA256",
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
|
@ -84,10 +84,10 @@
|
||||||
"http://localhost:8081/sales-post-sig-transient/*"
|
"http://localhost:8081/sales-post-sig-transient/*"
|
||||||
],
|
],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-transient",
|
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-transient/",
|
||||||
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-transient",
|
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-transient/",
|
||||||
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-transient",
|
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-transient/",
|
||||||
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig-transient",
|
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig-transient/",
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.signature.algorithm": "RSA_SHA256",
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
|
@ -106,10 +106,10 @@
|
||||||
"http://localhost:8081/sales-post-sig-persistent/*"
|
"http://localhost:8081/sales-post-sig-persistent/*"
|
||||||
],
|
],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-persistent",
|
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-persistent/",
|
||||||
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-persistent",
|
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-persistent/",
|
||||||
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-persistent",
|
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-persistent/",
|
||||||
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig-persistent",
|
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig-persistent/",
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.signature.algorithm": "RSA_SHA256",
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
|
@ -131,10 +131,10 @@
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml_force_name_id_format": "true",
|
"saml_force_name_id_format": "true",
|
||||||
"saml_name_id_format": "email",
|
"saml_name_id_format": "email",
|
||||||
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-email",
|
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-sig-email/",
|
||||||
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-email",
|
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-sig-email/",
|
||||||
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-email",
|
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-sig-email/",
|
||||||
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig-email",
|
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-sig-email/",
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.signature.algorithm": "RSA_SHA256",
|
"saml.signature.algorithm": "RSA_SHA256",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
|
@ -148,8 +148,8 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"protocol": "saml",
|
"protocol": "saml",
|
||||||
"fullScopeAllowed": true,
|
"fullScopeAllowed": true,
|
||||||
"baseUrl": "http://localhost:8081/bad-realm-sales-post-sig",
|
"baseUrl": "http://localhost:8081/bad-realm-sales-post-sig/",
|
||||||
"adminUrl": "http://localhost:8081/bad-realm-sales-post-sig",
|
"adminUrl": "http://localhost:8081/bad-realm-sales-post-sig/",
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8081/bad-realm-sales-post-sig/*"
|
"http://localhost:8081/bad-realm-sales-post-sig/*"
|
||||||
],
|
],
|
||||||
|
@ -166,8 +166,8 @@
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"protocol": "saml",
|
"protocol": "saml",
|
||||||
"fullScopeAllowed": true,
|
"fullScopeAllowed": true,
|
||||||
"baseUrl": "http://localhost:8081/bad-client-sales-post-sig",
|
"baseUrl": "http://localhost:8081/bad-client-sales-post-sig/",
|
||||||
"adminUrl": "http://localhost:8081/bad-client-sales-post-sig",
|
"adminUrl": "http://localhost:8081/bad-client-sales-post-sig/",
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8081/bad-client-sales-post-sig/*"
|
"http://localhost:8081/bad-client-sales-post-sig/*"
|
||||||
],
|
],
|
||||||
|
@ -189,10 +189,10 @@
|
||||||
"http://localhost:8081/sales-post-enc/*"
|
"http://localhost:8081/sales-post-enc/*"
|
||||||
],
|
],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-enc",
|
"saml_assertion_consumer_url_post": "http://localhost:8081/sales-post-enc/",
|
||||||
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-enc",
|
"saml_assertion_consumer_url_redirect": "http://localhost:8081/sales-post-enc/",
|
||||||
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-enc",
|
"saml_single_logout_service_url_post": "http://localhost:8081/sales-post-enc/",
|
||||||
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-enc",
|
"saml_single_logout_service_url_redirect": "http://localhost:8081/sales-post-enc/",
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.signature.algorithm": "RSA_SHA512",
|
"saml.signature.algorithm": "RSA_SHA512",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
|
@ -213,7 +213,7 @@
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8081/employee-sig/*"
|
"http://localhost:8081/employee-sig/*"
|
||||||
],
|
],
|
||||||
"adminUrl": "http://localhost:8081/employee-sig",
|
"adminUrl": "http://localhost:8081/employee-sig/",
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
|
@ -279,15 +279,15 @@
|
||||||
"protocol": "saml",
|
"protocol": "saml",
|
||||||
"fullScopeAllowed": true,
|
"fullScopeAllowed": true,
|
||||||
"frontchannelLogout": true,
|
"frontchannelLogout": true,
|
||||||
"baseUrl": "http://localhost:8081/employee-sig-front",
|
"baseUrl": "http://localhost:8081/employee-sig-front/",
|
||||||
"redirectUris": [
|
"redirectUris": [
|
||||||
"http://localhost:8081/employee-sig-front/*"
|
"http://localhost:8081/employee-sig-front/*"
|
||||||
],
|
],
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"saml_assertion_consumer_url_post": "http://localhost:8081/employee-sig-front",
|
"saml_assertion_consumer_url_post": "http://localhost:8081/employee-sig-front/",
|
||||||
"saml_assertion_consumer_url_redirect": "http://localhost:8081/employee-sig-front",
|
"saml_assertion_consumer_url_redirect": "http://localhost:8081/employee-sig-front/",
|
||||||
"saml_single_logout_service_url_post": "http://localhost:8081/employee-sig-front",
|
"saml_single_logout_service_url_post": "http://localhost:8081/employee-sig-front/",
|
||||||
"saml_single_logout_service_url_redirect": "http://localhost:8081/employee-sig-front",
|
"saml_single_logout_service_url_redirect": "http://localhost:8081/employee-sig-front/",
|
||||||
"saml.server.signature": "true",
|
"saml.server.signature": "true",
|
||||||
"saml.client.signature": "true",
|
"saml.client.signature": "true",
|
||||||
"saml.signature.algorithm": "RSA_SHA1",
|
"saml.signature.algorithm": "RSA_SHA1",
|
||||||
|
|
Loading…
Reference in a new issue