broker token exchange refactor

This commit is contained in:
Bill Burke 2015-04-23 11:26:44 -04:00
parent f9b099d49d
commit 1f4df58e6c
43 changed files with 305 additions and 586 deletions

View file

@ -129,8 +129,9 @@ public class OIDCIdentityProvider extends AbstractOAuth2IdentityProvider<OIDCIde
@Override
public Response keycloakInitiatedBrowserLogout(UserSessionModel userSession, UriInfo uriInfo, RealmModel realm) {
if (getConfig().getLogoutUrl() == null || getConfig().getLogoutUrl().trim().equals("")) return null;
String sessionId = userSession.getId();
UriBuilder logoutUri = UriBuilder.fromUri(getConfig().getLogoutUrl())
.queryParam("state", userSession.getId());
.queryParam("state", sessionId);
String idToken = userSession.getNote(FEDERATED_ID_TOKEN);
if (idToken != null) logoutUri.queryParam("id_token_hint", idToken);
String redirect = RealmsResource.brokerUrl(uriInfo)

View file

@ -7,6 +7,8 @@ import org.keycloak.broker.provider.BrokeredIdentityContext;
import org.keycloak.broker.provider.IdentityBrokerException;
import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.dom.saml.v2.assertion.AssertionType;
import org.keycloak.dom.saml.v2.assertion.AttributeStatementType;
import org.keycloak.dom.saml.v2.assertion.AttributeType;
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
import org.keycloak.dom.saml.v2.assertion.EncryptedAssertionType;
import org.keycloak.dom.saml.v2.assertion.NameIDType;
@ -36,6 +38,7 @@ import org.keycloak.saml.common.util.StaxParserUtil;
import org.keycloak.saml.processing.api.saml.v2.response.SAML2Response;
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
import org.keycloak.saml.processing.core.util.JAXPValidationUtil;
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
import org.keycloak.saml.processing.core.util.XMLSignatureUtil;
@ -295,6 +298,19 @@ public class SAMLEndpoint {
break;
}
}
if (assertion.getAttributeStatements() != null ) {
for (AttributeStatementType attrStatement : assertion.getAttributeStatements()) {
for (AttributeStatementType.ASTChoiceType choice : attrStatement.getAttributes()) {
AttributeType attribute = choice.getAttribute();
if (X500SAMLProfileConstants.EMAIL.getFriendlyName().equals(attribute.getFriendlyName())
|| X500SAMLProfileConstants.EMAIL.get().equals(attribute.getName())) {
if (!attribute.getAttributeValue().isEmpty()) identity.setEmail(attribute.getAttributeValue().get(0).toString());
}
}
}
}
String brokerUserId = config.getAlias() + "." + subjectNameID.getValue();
identity.setBrokerUserId(brokerUserId);
identity.setIdpConfig(config);

View file

@ -1,9 +1,10 @@
package org.keycloak.migration;
import java.util.List;
import org.keycloak.provider.Provider;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import java.util.List;
/**
* Various common utils needed for migration from older version to newer
*

View file

@ -116,10 +116,6 @@ public interface ClientModel extends RoleContainerModel {
void setNotBefore(int notBefore);
void updateIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders);
List<ClientIdentityProviderMappingModel> getIdentityProviders();
boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId);
Set<ProtocolMapperModel> getProtocolMappers();
ProtocolMapperModel addProtocolMapper(ProtocolMapperModel model);
void removeProtocolMapper(ProtocolMapperModel mapping);

View file

@ -8,6 +8,7 @@ public interface Constants {
String ADMIN_CONSOLE_CLIENT_ID = "security-admin-console";
String ACCOUNT_MANAGEMENT_CLIENT_ID = "account";
String BROKER_SERVICE_CLIENT_ID = "broker";
String INSTALLED_APP_URN = "urn:ietf:wg:oauth:2.0:oob";
String INSTALLED_APP_URL = "http://localhost";

View file

@ -1,5 +1,7 @@
package org.keycloak.models;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@ -8,8 +10,6 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/

View file

@ -1,7 +1,5 @@
package org.keycloak.models.entities;
import org.keycloak.models.ProtocolMapperModel;
import java.util.Map;
/**

View file

@ -1,7 +1,6 @@
package org.keycloak.models.utils;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderMapperModel;
@ -14,8 +13,6 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.ClientIdentityProviderMappingRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
@ -261,10 +258,6 @@ public class ModelToRepresentation {
rep.setRegisteredNodes(new HashMap<>(clientModel.getRegisteredNodes()));
}
if (!clientModel.getIdentityProviders().isEmpty()) {
rep.setIdentityProviders(toRepresentation(clientModel.getIdentityProviders()));
}
if (!clientModel.getProtocolMappers().isEmpty()) {
List<ProtocolMapperRepresentation> mappings = new LinkedList<>();
for (ProtocolMapperModel model : clientModel.getProtocolMappers()) {
@ -276,21 +269,6 @@ public class ModelToRepresentation {
return rep;
}
private static List<ClientIdentityProviderMappingRepresentation> toRepresentation(List<ClientIdentityProviderMappingModel> identityProviders) {
ArrayList<ClientIdentityProviderMappingRepresentation> representations = new ArrayList<ClientIdentityProviderMappingRepresentation>();
for (ClientIdentityProviderMappingModel model : identityProviders) {
ClientIdentityProviderMappingRepresentation representation = new ClientIdentityProviderMappingRepresentation();
representation.setId(model.getIdentityProvider());
representation.setRetrieveToken(model.isRetrieveToken());
representations.add(representation);
}
return representations;
}
public static UserFederationProviderRepresentation toRepresentation(UserFederationProviderModel model) {
UserFederationProviderRepresentation rep = new UserFederationProviderRepresentation();
rep.setId(model.getId());

View file

@ -6,7 +6,6 @@ import org.keycloak.enums.SslRequired;
import org.keycloak.migration.MigrationProvider;
import org.keycloak.models.BrowserSecurityHeaders;
import org.keycloak.models.ClaimMask;
import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderMapperModel;
@ -22,7 +21,6 @@ import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.ApplicationRepresentation;
import org.keycloak.representations.idm.ClaimRepresentation;
import org.keycloak.representations.idm.ClientIdentityProviderMappingRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.FederatedIdentityRepresentation;
@ -39,7 +37,6 @@ import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.util.UriUtils;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@ -610,8 +607,6 @@ public class RepresentationToModel {
}
}
client.updateIdentityProviders(toModel(resourceRep.getIdentityProviders(), realm));
return client;
}
@ -660,7 +655,6 @@ public class RepresentationToModel {
}
}
updateClientIdentityProviders(rep.getIdentityProviders(), resource);
}
public static long getClaimsMask(ClaimRepresentation rep) {
@ -887,37 +881,4 @@ public class RepresentationToModel {
return model;
}
private static List<ClientIdentityProviderMappingModel> toModel(List<ClientIdentityProviderMappingRepresentation> repIdentityProviders, RealmModel realm) {
List<ClientIdentityProviderMappingModel> result = new ArrayList<ClientIdentityProviderMappingModel>();
if (repIdentityProviders != null) {
for (ClientIdentityProviderMappingRepresentation rep : repIdentityProviders) {
ClientIdentityProviderMappingModel identityProviderMapping = new ClientIdentityProviderMappingModel();
identityProviderMapping.setIdentityProvider(rep.getId());
identityProviderMapping.setRetrieveToken(rep.isRetrieveToken());
result.add(identityProviderMapping);
}
}
return result;
}
private static void updateClientIdentityProviders(List<ClientIdentityProviderMappingRepresentation> identityProviders, ClientModel resource) {
if (identityProviders != null) {
List<ClientIdentityProviderMappingModel> result = new ArrayList<ClientIdentityProviderMappingModel>();
for (ClientIdentityProviderMappingRepresentation mappingRepresentation : identityProviders) {
ClientIdentityProviderMappingModel identityProviderMapping = new ClientIdentityProviderMappingModel();
identityProviderMapping.setIdentityProvider(mappingRepresentation.getId());
identityProviderMapping.setRetrieveToken(mappingRepresentation.isRetrieveToken());
result.add(identityProviderMapping);
}
resource.updateIdentityProviders(result);
}
}
}

View file

@ -1,13 +1,13 @@
package org.keycloak.models.utils.reflection;
import org.keycloak.util.reflections.Reflections;
import java.beans.Introspector;
import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import org.keycloak.util.reflections.Reflections;
/**
* A bean property based on the value represented by a getter/setter method pair
*/

View file

@ -1,7 +1,5 @@
package org.keycloak.provider;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.List;
/**

View file

@ -1,8 +1,5 @@
package org.keycloak.provider;
import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $

View file

@ -1,12 +1,12 @@
package org.keycloak.models;
import static org.junit.Assert.fail;
import java.util.regex.PatternSyntaxException;
import org.junit.Assert;
import org.junit.Test;
import java.util.regex.PatternSyntaxException;
import static org.junit.Assert.fail;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/

View file

@ -16,34 +16,33 @@
*/
package org.keycloak.models.file;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.file.adapter.UserAdapter;
import org.keycloak.connections.file.FileConnectionProvider;
import org.keycloak.connections.file.InMemoryModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.models.entities.FederatedIdentityEntity;
import org.keycloak.models.entities.UserEntity;
import org.keycloak.models.file.adapter.UserAdapter;
import org.keycloak.models.utils.CredentialValidation;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.keycloak.connections.file.FileConnectionProvider;
import org.keycloak.connections.file.InMemoryModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.entities.FederatedIdentityEntity;
import org.keycloak.models.entities.UserEntity;
import org.keycloak.models.utils.CredentialValidation;
/**
* UserProvider for JSON persistence.

View file

@ -16,12 +16,18 @@
*/
package org.keycloak.models.file.adapter;
import org.keycloak.connections.file.InMemoryModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.entities.ClientEntity;
import org.keycloak.models.entities.ProtocolMapperEntity;
import org.keycloak.models.entities.RoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.ArrayList;
import java.util.Collections;
@ -30,14 +36,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.keycloak.connections.file.InMemoryModel;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.UserModel;
import org.keycloak.models.entities.ClientEntity;
import org.keycloak.models.entities.ClientIdentityProviderMappingEntity;
import org.keycloak.models.entities.ProtocolMapperEntity;
import org.keycloak.models.entities.RoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
/**
* ApplicationModel used for JSON persistence.
@ -362,48 +360,6 @@ public class ClientAdapter implements ClientModel {
return mapping;
}
@Override
public void updateIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
List<ClientIdentityProviderMappingEntity> stored = new ArrayList<ClientIdentityProviderMappingEntity>();
for (ClientIdentityProviderMappingModel model : identityProviders) {
ClientIdentityProviderMappingEntity entity = new ClientIdentityProviderMappingEntity();
entity.setId(model.getIdentityProvider());
entity.setRetrieveToken(model.isRetrieveToken());
stored.add(entity);
}
entity.setIdentityProviders(stored);
}
@Override
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
List<ClientIdentityProviderMappingModel> models = new ArrayList<>();
for (ClientIdentityProviderMappingEntity e : entity.getIdentityProviders()) {
ClientIdentityProviderMappingModel model = new ClientIdentityProviderMappingModel();
model.setIdentityProvider(e.getId());
model.setRetrieveToken(e.isRetrieveToken());
models.add(model);
}
return models;
}
@Override
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
for (ClientIdentityProviderMappingEntity identityProviderMappingModel : entity.getIdentityProviders()) {
if (identityProviderMappingModel.getId().equals(providerId)) {
return identityProviderMappingModel.isRetrieveToken();
}
}
return false;
}
@Override
public String getClientId() {
return entity.getClientId();

View file

@ -16,18 +16,18 @@
*/
package org.keycloak.models.file.adapter;
import java.util.ArrayList;
import java.util.Collections;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.entities.RoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.entities.RoleEntity;
/**
* RoleModel for JSON persistence.

View file

@ -16,11 +16,10 @@
*/
package org.keycloak.models.file.adapter;
import org.keycloak.connections.file.InMemoryModel;
import org.keycloak.models.ClientModel;
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
import org.keycloak.models.GrantedConsentModel;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
@ -28,7 +27,11 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.entities.CredentialEntity;
import org.keycloak.models.entities.FederatedIdentityEntity;
import org.keycloak.models.entities.RoleEntity;
import org.keycloak.models.entities.UserEntity;
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import org.keycloak.util.Time;
import java.util.ArrayList;
import java.util.Collections;
@ -39,12 +42,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.keycloak.connections.file.InMemoryModel;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.entities.FederatedIdentityEntity;
import org.keycloak.models.entities.RoleEntity;
import org.keycloak.models.entities.UserEntity;
import org.keycloak.util.Time;
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
/**
* UserModel for JSON persistence.

View file

@ -1,7 +1,6 @@
package org.keycloak.models.cache;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
@ -249,24 +248,6 @@ public class ClientAdapter implements ClientModel {
return copy;
}
@Override
public void updateIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
getDelegateForUpdate();
updated.updateIdentityProviders(identityProviders);
}
@Override
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
if (updated != null) return updated.getIdentityProviders();
return cached.getIdentityProviders();
}
@Override
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
if (updated != null) return updated.isAllowedRetrieveTokenFromIdentityProvider(providerId);
return cached.isAllowedRetrieveTokenFromIdentityProvider(providerId);
}
@Override
public Set<ProtocolMapperModel> getProtocolMappers() {
if (updated != null) return updated.getProtocolMappers();

View file

@ -2,12 +2,12 @@ package org.keycloak.models.cache;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;

View file

@ -2,11 +2,11 @@ package org.keycloak.models.cache;
import org.keycloak.models.ClientModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;

View file

@ -1,14 +1,12 @@
package org.keycloak.models.cache.entities;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.RoleModel;
import org.keycloak.models.cache.RealmCache;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@ -37,7 +35,6 @@ public class CachedClient {
private int notBefore;
private Set<String> scope = new HashSet<String>();
private Set<String> webOrigins = new HashSet<String>();
private List<ClientIdentityProviderMappingModel> identityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
private Set<ProtocolMapperModel> protocolMappers = new HashSet<ProtocolMapperModel>();
private boolean surrogateAuthRequired;
private String managementUrl;
@ -67,7 +64,6 @@ public class CachedClient {
for (RoleModel role : model.getScopeMappings()) {
scope.add(role.getId());
}
this.identityProviders = model.getIdentityProviders();
for (ProtocolMapperModel mapper : model.getProtocolMappers()) {
this.protocolMappers.add(mapper);
}
@ -145,34 +141,10 @@ public class CachedClient {
return frontchannelLogout;
}
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
return this.identityProviders;
}
public boolean hasIdentityProvider(String providerId) {
for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
if (model.getIdentityProvider().equals(providerId)) {
return true;
}
}
return false;
}
public Set<ProtocolMapperModel> getProtocolMappers() {
return protocolMappers;
}
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
if (model.getIdentityProvider().equals(providerId)) {
return model.isRetrieveToken();
}
}
return false;
}
public boolean isSurrogateAuthRequired() {
return surrogateAuthRequired;
}

View file

@ -1,15 +1,12 @@
package org.keycloak.models.jpa;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleContainerModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.jpa.entities.ClientEntity;
import org.keycloak.models.jpa.entities.ClientIdentityProviderMappingEntity;
import org.keycloak.models.jpa.entities.IdentityProviderEntity;
import org.keycloak.models.jpa.entities.ProtocolMapperEntity;
import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.models.jpa.entities.ScopeMappingEntity;
@ -263,88 +260,6 @@ public class ClientAdapter implements ClientModel {
return copy;
}
@Override
public void updateIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
Collection<ClientIdentityProviderMappingEntity> entities = entity.getIdentityProviders();
Set<String> already = new HashSet<>();
List<ClientIdentityProviderMappingEntity> remove = new ArrayList<>();
for (ClientIdentityProviderMappingEntity entity : entities) {
IdentityProviderEntity identityProvider = entity.getIdentityProvider();
boolean toRemove = true;
for (ClientIdentityProviderMappingModel model : identityProviders) {
if (model.getIdentityProvider().equals(identityProvider.getAlias())) {
toRemove = false;
break;
}
}
if (toRemove) {
remove.add(entity);
} else {
already.add(entity.getIdentityProvider().getAlias());
}
}
for (ClientIdentityProviderMappingEntity entity : remove) {
entities.remove(entity);
em.remove(entity);
}
em.flush();
for (ClientIdentityProviderMappingModel model : identityProviders) {
ClientIdentityProviderMappingEntity mappingEntity = null;
if (!already.contains(model.getIdentityProvider())) {
mappingEntity = new ClientIdentityProviderMappingEntity();
entities.add(mappingEntity);
} else {
for (ClientIdentityProviderMappingEntity entity : entities) {
if (entity.getIdentityProvider().getAlias().equals(model.getIdentityProvider())) {
mappingEntity = entity;
break;
}
}
}
TypedQuery<IdentityProviderEntity> query = em.createNamedQuery("findIdentityProviderByAlias", IdentityProviderEntity.class).setParameter("alias", model.getIdentityProvider());
IdentityProviderEntity identityProviderEntity = query.getSingleResult();
mappingEntity.setIdentityProvider(identityProviderEntity);
mappingEntity.setClient(this.entity);
mappingEntity.setRetrieveToken(model.isRetrieveToken());
em.persist(mappingEntity);
}
em.flush();
}
@Override
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
List<ClientIdentityProviderMappingModel> models = new ArrayList<ClientIdentityProviderMappingModel>();
for (ClientIdentityProviderMappingEntity entity : this.entity.getIdentityProviders()) {
ClientIdentityProviderMappingModel model = new ClientIdentityProviderMappingModel();
model.setIdentityProvider(entity.getIdentityProvider().getAlias());
model.setRetrieveToken(entity.isRetrieveToken());
models.add(model);
}
return models;
}
@Override
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
for (ClientIdentityProviderMappingModel model : getIdentityProviders()) {
if (model.getIdentityProvider().equals(providerId)) {
return model.isRetrieveToken();
}
}
return false;
}
public static boolean contains(String str, String[] array) {
for (String s : array) {
if (str.equals(s)) return true;

View file

@ -25,12 +25,10 @@ import org.keycloak.util.Time;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;

View file

@ -1,25 +1,19 @@
package org.keycloak.models.jpa.entities;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import java.util.ArrayList;
import java.util.Collection;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>

View file

@ -1,7 +1,5 @@
package org.keycloak.models.jpa.entities;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@ -12,6 +10,7 @@ import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import java.io.Serializable;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>

View file

@ -1,7 +1,5 @@
package org.keycloak.models.jpa.entities;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
@ -12,6 +10,7 @@ import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import java.io.Serializable;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>

View file

@ -4,12 +4,10 @@ import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.entities.ClientIdentityProviderMappingEntity;
import org.keycloak.models.entities.ProtocolMapperEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoClientEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
@ -396,49 +394,6 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
}
@Override
public void updateIdentityProviders(List<ClientIdentityProviderMappingModel> identityProviders) {
List<ClientIdentityProviderMappingEntity> stored = new ArrayList<ClientIdentityProviderMappingEntity>();
for (ClientIdentityProviderMappingModel model : identityProviders) {
ClientIdentityProviderMappingEntity entity = new ClientIdentityProviderMappingEntity();
entity.setId(model.getIdentityProvider());
entity.setRetrieveToken(model.isRetrieveToken());
stored.add(entity);
}
getMongoEntity().setIdentityProviders(stored);
updateMongoEntity();
}
@Override
public List<ClientIdentityProviderMappingModel> getIdentityProviders() {
List<ClientIdentityProviderMappingModel> models = new ArrayList<ClientIdentityProviderMappingModel>();
for (ClientIdentityProviderMappingEntity entity : getMongoEntity().getIdentityProviders()) {
ClientIdentityProviderMappingModel model = new ClientIdentityProviderMappingModel();
model.setIdentityProvider(entity.getId());
model.setRetrieveToken(entity.isRetrieveToken());
models.add(model);
}
return models;
}
@Override
public boolean isAllowedRetrieveTokenFromIdentityProvider(String providerId) {
for (ClientIdentityProviderMappingEntity identityProviderMappingModel : getMongoEntity().getIdentityProviders()) {
if (identityProviderMappingModel.getId().equals(providerId)) {
return identityProviderMappingModel.isRetrieveToken();
}
}
return false;
}
@Override
public boolean isSurrogateAuthRequired() {
return getMongoEntity().isSurrogateAuthRequired();

View file

@ -1,7 +1,5 @@
package org.keycloak.models.mongo.keycloak.adapters;
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ClientModel;
import org.keycloak.models.GrantedConsentModel;
@ -22,13 +20,14 @@ import org.keycloak.util.Time;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
/**
* Wrapper around UserData object, which will persist wrapped object after each set operation (compatibility with picketlink based idm)
*

View file

@ -21,6 +21,7 @@ import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.timer.TimerProvider;
import java.util.Collections;
@ -84,6 +85,7 @@ public class RealmManager {
setupMasterAdminManagement(realm);
setupRealmAdminManagement(realm);
setupAccountManagement(realm);
setupBrokerService(realm);
setupAdminConsole(realm);
return realm;
@ -214,6 +216,19 @@ public class RealmManager {
}
}
public void setupBrokerService(RealmModel realm) {
ClientModel client = realm.getClientNameMap().get(Constants.BROKER_SERVICE_CLIENT_ID);
if (client == null) {
client = new ClientManager(this).createClient(realm, Constants.BROKER_SERVICE_CLIENT_ID);
client.setEnabled(true);
client.setFullScopeAllowed(false);
for (String role : IdentityBrokerService.ROLES) {
client.addRole(role).setDescription("${role_"+role+"}");
}
}
}
public RealmModel importRealm(RealmRepresentation rep) {
String id = rep.getId();
if (id == null) {
@ -228,6 +243,7 @@ public class RealmManager {
setupMasterAdminManagement(realm);
if (!hasRealmAdminManagementClient(rep)) setupRealmAdminManagement(realm);
if (!hasAccountManagementClient(rep)) setupAccountManagement(realm);
if (!hasBrokerClient(rep)) setupBrokerService(realm);
if (!hasAdminConsoleClient(rep)) setupAdminConsole(realm);
RepresentationToModel.importRealm(session, rep, realm);
@ -260,6 +276,15 @@ public class RealmManager {
}
return false;
}
private boolean hasBrokerClient(RealmRepresentation rep) {
if (rep.getClients() == null) return false;
for (ClientRepresentation clientRep : rep.getClients()) {
if (clientRep.getClientId().equals(Constants.BROKER_SERVICE_CLIENT_ID)) {
return true;
}
}
return false;
}
private boolean hasAdminConsoleClient(RealmRepresentation rep) {
if (rep.getClients() == null) return false;

View file

@ -34,17 +34,20 @@ import org.keycloak.events.EventType;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.AccessToken;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.AuthenticationManager.AuthResult;
@ -92,6 +95,8 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
private static final Logger LOGGER = Logger.getLogger(IdentityBrokerService.class);
public static final String BROKER_PROVIDER_ID = "BROKER_PROVIDER_ID";
public static final String READ_TOKEN_ROLE = "READ_TOKEN";
public static final String[] ROLES = {READ_TOKEN_ROLE};
private final RealmModel realmModel;
@ -185,7 +190,8 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
AuthResult authResult = authManager.authenticateBearerToken(this.session, this.realmModel, this.uriInfo, this.clientConnection, this.request.getHttpHeaders());
if (authResult != null) {
String audience = authResult.getToken().getAudience();
AccessToken token = authResult.getToken();
String audience = token.getAudience();
ClientModel clientModel = this.realmModel.getClientByClientId(audience);
if (clientModel == null) {
@ -194,16 +200,16 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
session.getContext().setClient(clientModel);
if (!clientModel.isAllowedRetrieveTokenFromIdentityProvider(providerId)) {
return corsResponse(badRequest("Client [" + audience + "] not authorized to retrieve tokens from identity provider [" + providerId + "]."), clientModel);
}
ClientModel brokerClient = realmModel.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID);
if (brokerClient == null) {
return corsResponse(forbidden("Realm has not migrated to support the broker token exchange service"), clientModel);
}
Map<String, AccessToken.Access> resourceAccess = token.getResourceAccess();
AccessToken.Access brokerRoles = resourceAccess == null ? null : resourceAccess.get(Constants.BROKER_SERVICE_CLIENT_ID);
if (brokerRoles == null || !brokerRoles.isUserInRole(READ_TOKEN_ROLE)) {
return corsResponse(forbidden("Client [" + audience + "] not authorized to retrieve tokens from identity provider [" + providerId + "]."), clientModel);
if (clientModel.isConsentRequired()) {
return corsResponse(session.getProvider(LoginFormsProvider.class)
.setClientSessionCode(authManager.extractAuthorizationHeaderToken(this.request.getHttpHeaders()))
.setAccessRequest("Your information from " + providerId + " identity provider.")
.setActionUri(this.uriInfo.getRequestUri())
.createOAuthGrant(null), clientModel);
}
IdentityProvider identityProvider = getIdentityProvider(session, realmModel, providerId);
@ -232,18 +238,6 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
}
}
@POST
@Path("{provider_id}/token")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response consentTokenRetrieval(@PathParam("provider_id") String providerId,
MultivaluedMap<String, String> formData) {
if (formData.containsKey("cancel")) {
return redirectToErrorPage(Messages.PERMISSION_NOT_APPROVED);
}
return getToken(providerId, true);
}
public Response authenticated(BrokeredIdentityContext context) {
ClientSessionCode clientCode = null;
IdentityProviderModel identityProviderConfig = context.getIdpConfig();
@ -445,6 +439,11 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
return ErrorResponse.error(message, Status.BAD_REQUEST);
}
private Response forbidden(String message) {
fireErrorEvent(message);
return ErrorResponse.error(message, Status.FORBIDDEN);
}
public static IdentityProvider getIdentityProvider(KeycloakSession session, RealmModel realm, String alias) {
IdentityProviderModel identityProviderModel = realm.getIdentityProviderByAlias(alias);
@ -534,6 +533,13 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
federatedUser.setFirstName(updatedIdentity.getFirstName());
federatedUser.setLastName(updatedIdentity.getLastName());
if (updatedIdentity.getIdpConfig().isStoreToken()) {
RoleModel readTokenRole = realmModel.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(READ_TOKEN_ROLE);
federatedUser.grantRole(readTokenRole);
}
this.session.users().addFederatedIdentity(this.realmModel, federatedUser, federatedIdentityModel);
updatedIdentity.getIdp().importNewUser(session, realmModel, federatedUser, updatedIdentity);

View file

@ -84,8 +84,6 @@ public class IdentityProviderResource {
public Response delete() {
this.auth.requireManage();
removeClientIdentityProviders(this.realm.getClients(), this.identityProviderModel);
this.realm.removeIdentityProviderByAlias(this.identityProviderModel.getAlias());
return Response.noContent().build();
@ -109,7 +107,6 @@ public class IdentityProviderResource {
// Admin changed the ID (alias) of identity provider. We must update all clients and users
logger.debug("Changing providerId in all clients and linked users. oldProviderId=" + oldProviderId + ", newProviderId=" + newProviderId);
updateClientsAfterProviderAliasChange(this.realm.getClients(), oldProviderId, newProviderId);
updateUsersAfterProviderAliasChange(this.session.users().getUsers(this.realm), oldProviderId, newProviderId);
}
@ -131,25 +128,6 @@ public class IdentityProviderResource {
return null;
}
private void updateClientsAfterProviderAliasChange(List<ClientModel> clients, String oldProviderId, String newProviderId) {
for (ClientModel client : clients) {
List<ClientIdentityProviderMappingModel> clientIdentityProviders = client.getIdentityProviders();
boolean found = true;
for (ClientIdentityProviderMappingModel mappingModel : clientIdentityProviders) {
if (mappingModel.getIdentityProvider().equals(oldProviderId)) {
mappingModel.setIdentityProvider(newProviderId);
found = true;
break;
}
}
if (found) {
client.updateIdentityProviders(clientIdentityProviders);
}
}
}
private void updateUsersAfterProviderAliasChange(List<UserModel> users, String oldProviderId, String newProviderId) {
for (UserModel user : users) {
FederatedIdentityModel federatedIdentity = this.session.users().getFederatedIdentity(user, oldProviderId, this.realm);
@ -285,18 +263,5 @@ public class IdentityProviderResource {
realm.removeIdentityProviderMapper(model);
}
private void removeClientIdentityProviders(List<ClientModel> clients, IdentityProviderModel identityProvider) {
for (ClientModel clientModel : clients) {
List<ClientIdentityProviderMappingModel> identityProviders = clientModel.getIdentityProviders();
for (ClientIdentityProviderMappingModel providerMappingModel : new ArrayList<>(identityProviders)) {
if (providerMappingModel.getIdentityProvider().equals(identityProvider.getAlias())) {
identityProviders.remove(providerMappingModel);
clientModel.updateIdentityProviders(identityProviders);
break;
}
}
}
}
}

View file

@ -42,7 +42,7 @@ public class ClientTest extends AbstractClientTest {
@Test
public void getClients() {
assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console");
assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console", "broker");
}
@Test
@ -52,7 +52,7 @@ public class ClientTest extends AbstractClientTest {
rep.setEnabled(true);
realm.clients().create(rep);
assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console", "my-app");
assertNames(realm.clients().findAll(), "account", "realm-management", "security-admin-console", "broker", "my-app");
}
@Test

View file

@ -32,14 +32,17 @@ import org.junit.Test;
import org.keycloak.OAuth2Constants;
import org.keycloak.models.ClientIdentityProviderMappingModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.representations.IDToken;
import org.keycloak.services.Urls;
import org.keycloak.services.resources.IdentityBrokerService;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
@ -243,6 +246,7 @@ public abstract class AbstractIdentityProviderTest {
FederatedIdentityModel federatedIdentityModel = federatedIdentities.iterator().next();
assertEquals(getProviderId(), federatedIdentityModel.getIdentityProvider());
revokeGrant();
driver.navigate().to("http://localhost:8081/test-app/logout");
driver.navigate().to("http://localhost:8081/test-app");
@ -285,27 +289,12 @@ public abstract class AbstractIdentityProviderTest {
RealmModel realm = getRealm();
ClientModel clientModel = realm.getClientByClientId("test-app");
// This client doesn't have any specific identity providers settings
ClientModel client2 = realm.getClientByClientId("test-app");
assertEquals(0, client2.getIdentityProviders().size());
// Provider button is available on login page
this.driver.navigate().to("http://localhost:8081/test-app/");
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
loginPage.findSocialButton(getProviderId());
// Add identityProvider to client model
List<ClientIdentityProviderMappingModel> appIdentityProviders = new ArrayList<ClientIdentityProviderMappingModel>();
ClientIdentityProviderMappingModel mapping = new ClientIdentityProviderMappingModel();
mapping.setIdentityProvider(getProviderId());
mapping.setRetrieveToken(true);
appIdentityProviders.add(mapping);
clientModel.updateIdentityProviders(appIdentityProviders);
// Provider button still available on login page
this.driver.navigate().to("http://localhost:8081/test-app/");
loginPage.findSocialButton(getProviderId());
}
}
@Test
public void testUserAlreadyExistsWhenUpdatingProfile() {
@ -411,12 +400,16 @@ public abstract class AbstractIdentityProviderTest {
revokeGrant();
// Logout from account management
String pageSource = driver.getPageSource();
accountFederatedIdentityPage.logout();
assertTrue(driver.getTitle().equals("Log in to realm-with-broker"));
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
// Try to login. Previous link is not valid anymore, so now it should try to register new user
this.loginPage.clickSocial(identityProviderModel.getAlias());
this.loginPage.login("test-user", "password");
doAfterProviderAuthentication();
String current = driver.getCurrentUrl();
this.updateProfilePage.assertCurrent();
}
@ -429,6 +422,52 @@ public abstract class AbstractIdentityProviderTest {
driver.findElement(By.className("model-oidc-idp"));
}
protected void configureClientRetrieveToken(String clientId) {
RealmModel realm = getRealm();
RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(IdentityBrokerService.READ_TOKEN_ROLE);
ClientModel client = realm.getClientByClientId(clientId);
if (!client.hasScope(readTokenRole)) client.addScopeMapping(readTokenRole);
brokerServerRule.stopSession(session, true);
session = brokerServerRule.startSession();
}
protected void configureUserRetrieveToken(String username) {
RealmModel realm = getRealm();
UserModel user = session.users().getUserByUsername(username, realm);
RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(IdentityBrokerService.READ_TOKEN_ROLE);
if (user != null && !user.hasRole(readTokenRole)) {
user.grantRole(readTokenRole);
}
brokerServerRule.stopSession(session, true);
session = brokerServerRule.startSession();
}
protected void unconfigureClientRetrieveToken(String clientId) {
RealmModel realm = getRealm();
RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(IdentityBrokerService.READ_TOKEN_ROLE);
ClientModel client = realm.getClientByClientId(clientId);
if (client.hasScope(readTokenRole)) client.deleteScopeMapping(readTokenRole);
brokerServerRule.stopSession(session, true);
session = brokerServerRule.startSession();
}
protected void unconfigureUserRetrieveToken(String username) {
RealmModel realm = getRealm();
UserModel user = session.users().getUserByUsername(username, realm);
RoleModel readTokenRole = realm.getClientByClientId(Constants.BROKER_SERVICE_CLIENT_ID).getRole(IdentityBrokerService.READ_TOKEN_ROLE);
if (user != null && user.hasRole(readTokenRole)) {
user.deleteRoleMapping(readTokenRole);
}
brokerServerRule.stopSession(session, true);
session = brokerServerRule.startSession();
}
@Test
public void testTokenStorageAndRetrievalByApplication() {
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
@ -448,8 +487,6 @@ public abstract class AbstractIdentityProviderTest {
assertNotNull(identityModel.getToken());
configureRetrieveToken(realm.getClientByClientId("test-app"), getProviderId(), false);
UserSessionStatus userSessionStatus = retrieveSessionStatus();
String accessToken = userSessionStatus.getAccessTokenString();
URI tokenEndpointUrl = Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), realm.getName());
@ -463,112 +500,43 @@ public abstract class AbstractIdentityProviderTest {
Client client = ClientBuilder.newBuilder().register(authFilter).build();
WebTarget tokenEndpoint = client.target(tokenEndpointUrl);
Response response = tokenEndpoint.request().get();
assertEquals(Status.OK.getStatusCode(), response.getStatus());
assertNotNull(response.readEntity(String.class));
revokeGrant();
assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
configureRetrieveToken(getRealm().getClientByClientId("test-app"), getProviderId(), true);
driver.navigate().to("http://localhost:8081/test-app/logout");
String currentUrl = this.driver.getCurrentUrl();
System.out.println("after logout currentUrl: " + currentUrl);
assertTrue(currentUrl.startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
client = ClientBuilder.newBuilder().register(authFilter).build();
unconfigureUserRetrieveToken(getProviderId() + ".test-user");
loginIDP("test-user");
//authenticateWithIdentityProvider(identityProviderModel, "test-user");
assertEquals("http://localhost:8081/test-app", driver.getCurrentUrl());
userSessionStatus = retrieveSessionStatus();
accessToken = userSessionStatus.getAccessTokenString();
final String authHeader2 = "Bearer " + accessToken;
ClientRequestFilter authFilter2 = new ClientRequestFilter() {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader2);
}
};
client = ClientBuilder.newBuilder().register(authFilter2).build();
tokenEndpoint = client.target(tokenEndpointUrl);
response = tokenEndpoint.request().get();
assertEquals(Status.OK.getStatusCode(), response.getStatus());
assertNotNull(response.readEntity(String.class));
assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
revokeGrant();
driver.navigate().to("http://localhost:8081/test-app/logout");
driver.navigate().to("http://localhost:8081/test-app");
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
}
@Test
public void testTokenStorageAndRetrievalByOAuthClient() {
IdentityProviderModel identityProviderModel = getIdentityProviderModel();
identityProviderModel.setStoreToken(true);
identityProviderModel.setUpdateProfileFirstLogin(false);
driver.navigate().to("http://localhost:8081/test-app");
// choose the identity provider
this.loginPage.clickSocial(getProviderId());
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
// log in to identity provider
this.loginPage.login("test-user", "password");
doAfterProviderAuthentication();
changePasswordPage.realm("realm-with-broker");
changePasswordPage.open();
changePasswordPage.changePassword("password", "password");
driver.navigate().to("http://localhost:8081/test-app/logout");
oauth.realm("realm-with-broker");
oauth.redirectUri("http://localhost:8081/third-party");
oauth.clientId("third-party");
oauth.doLoginGrant("test-user@localhost", "password");
grantPage.assertCurrent();
grantPage.accept();
assertTrue(oauth.getCurrentQuery().containsKey(OAuth2Constants.CODE));
ClientModel clientModel = getRealm().getClientByClientId("third-party");
assertEquals(0, clientModel.getIdentityProviders().size());
configureRetrieveToken(clientModel, getProviderId(), true);
AccessTokenResponse accessToken = oauth.doAccessTokenRequest(oauth.getCurrentQuery().get(OAuth2Constants.CODE), "password");
doTokenRequest(accessToken.getAccessToken());
}
public void doTokenRequest(String token) {
try {
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(Urls.identityProviderRetrieveToken(BASE_URI, getProviderId(), getRealm().getName()));
get.setHeader("Authorization", "Bearer " + token);
HttpResponse response = client.execute(get);
assertEquals(200, response.getStatusLine().getStatusCode());
assertNotNull(IOUtils.toString(response.getEntity().getContent()));
} catch (Exception e) {
fail(e.getMessage());
}
}
private void configureRetrieveToken(ClientModel clientModel, String providerId, boolean retrieveToken) {
List<ClientIdentityProviderMappingModel> providerMappingModels = clientModel.getIdentityProviders();
ClientIdentityProviderMappingModel providerMappingModel = null;
// Check if provider is already linked with this client
for (ClientIdentityProviderMappingModel current : providerMappingModels) {
if (current.getIdentityProvider().equals(providerId)) {
providerMappingModel = current;
break;
}
}
// Link provider with client if not linked yet
if (providerMappingModel == null) {
providerMappingModel = new ClientIdentityProviderMappingModel();
providerMappingModel.setIdentityProvider(providerId);
providerMappingModels.add(providerMappingModel);
}
providerMappingModel.setRetrieveToken(retrieveToken);
clientModel.updateIdentityProviders(providerMappingModels);
brokerServerRule.stopSession(session, true);
session = brokerServerRule.startSession();
}
protected abstract void doAssertTokenRetrieval(String pageSource);
private UserModel assertSuccessfulAuthentication(IdentityProviderModel identityProviderModel, String username, String expectedEmail) {
@ -605,19 +573,8 @@ public abstract class AbstractIdentityProviderTest {
}
private void authenticateWithIdentityProvider(IdentityProviderModel identityProviderModel, String username) {
driver.navigate().to("http://localhost:8081/test-app");
loginIDP(username);
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
// choose the identity provider
this.loginPage.clickSocial(getProviderId());
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8082/auth/"));
System.out.println(this.driver.getCurrentUrl());
// log in to identity provider
this.loginPage.login(username, "password");
doAfterProviderAuthentication();
if (identityProviderModel.isUpdateProfileFirstLogin()) {
String userEmail = "new@email.com";
@ -630,6 +587,22 @@ public abstract class AbstractIdentityProviderTest {
}
}
private void loginIDP(String username) {
driver.navigate().to("http://localhost:8081/test-app");
assertTrue(this.driver.getCurrentUrl().startsWith("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/auth"));
// choose the identity provider
this.loginPage.clickSocial(getProviderId());
String currentUrl = this.driver.getCurrentUrl();
assertTrue(currentUrl.startsWith("http://localhost:8082/auth/"));
System.out.println(this.driver.getCurrentUrl());
// log in to identity provider
this.loginPage.login(username, "password");
doAfterProviderAuthentication();
}
protected UserModel getFederatedUser() {
UserSessionStatus userSessionStatus = retrieveSessionStatus();
IDToken idToken = userSessionStatus.getIdToken();

View file

@ -118,32 +118,6 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes
this.realmManager.removeRealm(realm);
}
@Test
public void testApplicationIdentityProviders() throws Exception {
RealmModel realm = installTestRealm();
ClientModel client = realm.getClientByClientId("test-app-with-allowed-providers");
List<ClientIdentityProviderMappingModel> identityProviders = client.getIdentityProviders();
assertEquals(1, identityProviders.size());
ClientIdentityProviderMappingModel identityProviderMappingModel = identityProviders.get(0);
assertEquals("kc-oidc-idp", identityProviderMappingModel.getIdentityProvider());
assertEquals(false, identityProviderMappingModel.isRetrieveToken());
identityProviders.remove(identityProviderMappingModel);
client.updateIdentityProviders(identityProviders);
client = realm.getClientById(client.getId());
identityProviders = client.getIdentityProviders();
assertEquals(0, identityProviders.size());
this.realmManager.removeRealm(realm);
}
private void assertIdentityProviderConfig(List<IdentityProviderModel> identityProviders) {
assertFalse(identityProviders.isEmpty());

View file

@ -75,8 +75,8 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
@Override
protected void doAfterProviderAuthentication() {
// grant access to broker-app
grantPage.assertCurrent();
grantPage.accept();
//grantPage.assertCurrent();
//grantPage.accept();
}
@Override
@ -120,4 +120,14 @@ public class OIDCKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername_emailNotProvided() {
super.testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername_emailNotProvided();
}
@Test
public void testTokenStorageAndRetrievalByApplication() {
super.testTokenStorageAndRetrievalByApplication();
}
@Test
public void testAccountManagementLinkIdentity() {
super.testAccountManagementLinkIdentity();
}
}

View file

@ -67,8 +67,8 @@ public class SAMLKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
@Override
protected void doAssertFederatedUserNoEmail(UserModel federatedUser) {
assertEquals("kc-saml-idp-basic.", federatedUser.getUsername());
assertEquals("", federatedUser.getEmail());
assertEquals("kc-saml-idp-basic.test-user-noemail", federatedUser.getUsername());
//assertEquals("", federatedUser.getEmail());
assertEquals(null, federatedUser.getFirstName());
assertEquals(null, federatedUser.getLastName());
}
@ -94,15 +94,24 @@ public class SAMLKeyCloakServerBrokerBasicTest extends AbstractIdentityProviderT
super.testSuccessfulAuthenticationWithoutUpdateProfile();
}
@Override
@Test
public void testTokenStorageAndRetrievalByOAuthClient() {
super.testTokenStorageAndRetrievalByOAuthClient();
}
@Override
@Test
public void testSuccessfulAuthentication() {
super.testSuccessfulAuthentication();
}
@Test
public void testAccountManagementLinkIdentity() {
super.testAccountManagementLinkIdentity();
}
@Test
public void testTokenStorageAndRetrievalByApplication() {
super.testTokenStorageAndRetrievalByApplication();
}
@Test
public void testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername() {
super.testSuccessfulAuthenticationWithoutUpdateProfile_newUser_emailAsUsername();
}
}

View file

@ -67,8 +67,8 @@ public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractIdentityP
@Override
protected void doAssertFederatedUserNoEmail(UserModel federatedUser) {
assertEquals("kc-saml-signed-idp.", federatedUser.getUsername());
assertEquals("", federatedUser.getEmail());
assertEquals("kc-saml-signed-idp.test-user-noemail", federatedUser.getUsername());
//assertEquals("", federatedUser.getEmail());
assertEquals(null, federatedUser.getFirstName());
assertEquals(null, federatedUser.getLastName());
}
@ -93,4 +93,8 @@ public class SAMLKeyCloakServerBrokerWithSignatureTest extends AbstractIdentityP
super.testSuccessfulAuthentication();
}
@Test
public void testTokenStorageAndRetrievalByApplication() {
super.testTokenStorageAndRetrievalByApplication();
}
}

View file

@ -27,6 +27,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
@ -39,8 +40,11 @@ public class UserSessionStatusServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (req.getRequestURI().toString().endsWith("logout")) {
resp.setStatus(200);
req.logout();
String redirect = UriBuilder.fromUri("http://localhost:8081/auth/realms/realm-with-broker/protocol/openid-connect/logout")
.queryParam("redirect_uri", "http://localhost:8081/test-app").build().toString();
resp.sendRedirect(redirect);
//resp.setStatus(200);
//req.logout();
return;
}

View file

@ -81,7 +81,7 @@ public class ImportTest extends AbstractModelTest {
Assert.assertEquals(0, session.users().getFederatedIdentities(user, realm).size());
List<ClientModel> resources = realm.getClients();
Assert.assertEquals(6, resources.size());
Assert.assertEquals(7, resources.size());
// Test applications imported
ClientModel application = realm.getClientByClientId("Application");
@ -92,7 +92,7 @@ public class ImportTest extends AbstractModelTest {
Assert.assertNotNull(otherApp);
Assert.assertNull(nonExisting);
Map<String, ClientModel> clients = realm.getClientNameMap();
Assert.assertEquals(6, clients.size());
Assert.assertEquals(7, clients.size());
Assert.assertTrue(clients.values().contains(application));
Assert.assertTrue(clients.values().contains(otherApp));
Assert.assertTrue(clients.values().contains(accountApp));

View file

@ -9,11 +9,11 @@
"clients" : [
{
"clientId": "broker-app",
"consentRequired": "true",
"consentRequired": "false",
"enabled": true,
"secret": "secret",
"redirectUris": [
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-oidc-idp/endpoint"
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-oidc-idp/endpoint/*"
],
"protocolMappers": [
{

View file

@ -15,6 +15,10 @@
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-signed-idp/endpoint"
],
"attributes": {
"saml_single_logout_service_url_post": "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-signed-idp/endpoint",
"saml_assertion_consumer_url_post": "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-signed-idp/endpoint",
"saml_force_name_id_format": "true",
"saml_name_id_format": "username",
"saml.assertion.signature": "true",
"saml.server.signature": "true",
"saml.signature.algorithm": "RSA_SHA256",
@ -46,6 +50,18 @@
"attribute.name": "mobile",
"attribute.nameformat": "Basic"
}
},
{
"name": "email",
"protocol": "saml",
"protocolMapper": "saml-user-property-mapper",
"consentRequired": false,
"config": {
"user.attribute": "email",
"attribute.name": "urn:oid:1.2.840.113549.1.9.1",
"attribute.nameformat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
"friendly.name": "email"
}
}
]

View file

@ -15,7 +15,12 @@
"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint"
],
"attributes": {
"saml.authnstatement": "true"
"saml.authnstatement": "true",
"saml_single_logout_service_url_post": "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint",
"saml_assertion_consumer_url_post": "http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint",
"saml_force_name_id_format": "true",
"saml_name_id_format": "username"
},
"protocolMappers": [
{
@ -40,6 +45,18 @@
"attribute.name": "mobile",
"attribute.nameformat": "Basic"
}
},
{
"name": "email",
"protocol": "saml",
"protocolMapper": "saml-user-property-mapper",
"consentRequired": false,
"config": {
"user.attribute": "email",
"attribute.name": "urn:oid:1.2.840.113549.1.9.1",
"attribute.nameformat": "urn:oasis:names:tc:SAML:2.0:attrname-format:uri",
"friendly.name": "email"
}
}
]

View file

@ -112,6 +112,7 @@
"updateProfileFirstLogin" : "true",
"config": {
"singleSignOnServiceUrl": "http://localhost:8082/auth/realms/realm-with-saml-signed-idp/protocol/saml",
"singleLogoutServiceUrl": "http://localhost:8082/auth/realms/realm-with-saml-signed-idp/protocol/saml",
"nameIDPolicyFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"signingCertificate": "MIIDdzCCAl+gAwIBAgIEbySuqTANBgkqhkiG9w0BAQsFADBsMRAwDgYDVQQGEwdVbmtub3duMRAwDgYDVQQIEwdVbmtub3duMRAwDgYDVQQHEwdVbmtub3duMRAwDgYDVQQKEwdVbmtub3duMRAwDgYDVQQLEwdVbmtub3duMRAwDgYDVQQDEwdVbmtub3duMB4XDTE1MDEyODIyMTYyMFoXDTE3MTAyNDIyMTYyMFowbDEQMA4GA1UEBhMHVW5rbm93bjEQMA4GA1UECBMHVW5rbm93bjEQMA4GA1UEBxMHVW5rbm93bjEQMA4GA1UEChMHVW5rbm93bjEQMA4GA1UECxMHVW5rbm93bjEQMA4GA1UEAxMHVW5rbm93bjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAII/K9NNvXi9IySl7+l2zY/kKrGTtuR4WdCI0xLW/Jn4dLY7v1/HOnV4CC4ecFOzhdNFPtJkmEhP/q62CpmOYOKApXk3tfmm2rwEz9bWprVxgFGKnbrWlz61Z/cjLAlhD3IUj2ZRBquYgSXQPsYfXo1JmSWF5pZ9uh1FVqu9f4wvRqY20ZhUN+39F+1iaBsoqsrbXypCn1HgZkW1/9D9GZug1c3vB4wg1TwZZWRNGtxwoEhdK6dPrNcZ+6PdanVilWrbQFbBjY4wz8/7IMBzssoQ7Usmo8F1Piv0FGfaVeJqBrcAvbiBMpk8pT+27u6p8VyIX6LhGvnxIwM07NByeSUCAwEAAaMhMB8wHQYDVR0OBBYEFFlcNuTYwI9W0tQ224K1gFJlMam0MA0GCSqGSIb3DQEBCwUAA4IBAQB5snl1KWOJALtAjLqD0mLPg1iElmZP82Lq1htLBt3XagwzU9CaeVeCQ7lTp+DXWzPa9nCLhsC3QyrV3/+oqNli8C6NpeqI8FqN2yQW/QMWN1m5jWDbmrWwtQzRUn/rh5KEb5m3zPB+tOC6e/2bV3QeQebxeW7lVMD0tSCviUg1MQf1l2gzuXQo60411YwqrXwk6GMkDOhFDQKDlMchO3oRbQkGbcP8UeiKAXjMeHfzbiBr+cWz8NYZEtxUEDYDjTpKrYCSMJBXpmgVJCZ00BswbksxJwaGqGMPpUKmCV671pf3m8nq3xyiHMDGuGwtbU+GE8kVx85menmp8+964nin",
"wantAuthnRequestsSigned": true,
@ -128,6 +129,7 @@
"updateProfileFirstLogin" : "true",
"config": {
"singleSignOnServiceUrl": "http://localhost:8082/auth/realms/realm-with-saml-idp-basic/protocol/saml",
"singleLogoutServiceUrl": "http://localhost:8082/auth/realms/realm-with-saml-idp-basic/protocol/saml",
"nameIDPolicyFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
"forceAuthn": true,
"postBindingResponse": true,
@ -155,6 +157,7 @@
"providerId" : "keycloak-oidc",
"enabled": true,
"updateProfileFirstLogin" : "false",
"storeToken" : "true",
"config": {
"clientId": "broker-app",
"clientSecret": "secret",
@ -162,6 +165,7 @@
"authorizationUrl": "http://localhost:8082/auth/realms/realm-with-oidc-identity-provider/tokens/login",
"tokenUrl": "http://localhost:8082/auth/realms/realm-with-oidc-identity-provider/protocol/openid-connect/token",
"userInfoUrl": "http://localhost:8082/auth/realms/realm-with-oidc-identity-provider/protocol/openid-connect/userinfo",
"logoutUrl": "http://localhost:8082/auth/realms/realm-with-oidc-identity-provider/tokens/logout",
"defaultScope": "email profile"
}
}
@ -248,8 +252,8 @@
"name": "test-app",
"enabled": true,
"publicClient": true,
"adminUrl": "http://localhost:8081/auth",
"baseUrl": "http://localhost:8081/auth",
"adminUrl": "http://localhost:8081/test-app",
"baseUrl": "http://localhost:8081/test-app",
"redirectUris": [
"/test-app/*"
],