KEYCLOAK-17290 SAML Client - Generate AttributeConsumingService SP metadata section
This commit is contained in:
parent
8260c3c623
commit
6bd7420907
10 changed files with 259 additions and 45 deletions
|
@ -17,27 +17,20 @@
|
|||
|
||||
package org.keycloak.saml;
|
||||
|
||||
import org.keycloak.dom.saml.v2.metadata.EndpointType;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import org.keycloak.saml.common.util.StaxUtil;
|
||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||
|
||||
import org.keycloak.dom.saml.v2.metadata.EndpointType;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.IndexedEndpointType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType;
|
||||
import org.keycloak.saml.processing.core.saml.v2.common.IDGenerator;
|
||||
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLMetadataWriter;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
|
@ -50,25 +43,10 @@ import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_
|
|||
*/
|
||||
public class SPMetadataDescriptor {
|
||||
|
||||
public static String getSPDescriptor(URI binding, URI assertionEndpoint, URI logoutEndpoint,
|
||||
boolean wantAuthnRequestsSigned, boolean wantAssertionsSigned, boolean wantAssertionsEncrypted,
|
||||
String entityId, String nameIDPolicyFormat, List<Element> signingCerts, List<Element> encryptionCerts)
|
||||
throws XMLStreamException, ProcessingException, ParserConfigurationException
|
||||
{
|
||||
return getSPDescriptor(binding, binding, assertionEndpoint, logoutEndpoint, wantAuthnRequestsSigned,
|
||||
wantAssertionsSigned, wantAssertionsEncrypted, entityId, nameIDPolicyFormat, signingCerts,
|
||||
encryptionCerts);
|
||||
}
|
||||
|
||||
public static String getSPDescriptor(URI loginBinding, URI logoutBinding, URI assertionEndpoint, URI logoutEndpoint,
|
||||
public static EntityDescriptorType buildSPdescriptor(URI loginBinding, URI logoutBinding, URI assertionEndpoint, URI logoutEndpoint,
|
||||
boolean wantAuthnRequestsSigned, boolean wantAssertionsSigned, boolean wantAssertionsEncrypted,
|
||||
String entityId, String nameIDPolicyFormat, List<Element> signingCerts, List<Element> encryptionCerts)
|
||||
throws XMLStreamException, ProcessingException, ParserConfigurationException
|
||||
String entityId, String nameIDPolicyFormat, List<Element> signingCerts, List<Element> encryptionCerts)
|
||||
{
|
||||
StringWriter sw = new StringWriter();
|
||||
XMLStreamWriter writer = StaxUtil.getXMLStreamWriter(sw);
|
||||
SAMLMetadataWriter metadataWriter = new SAMLMetadataWriter(writer);
|
||||
|
||||
EntityDescriptorType entityDescriptor = new EntityDescriptorType(entityId);
|
||||
entityDescriptor.setID(IDGenerator.create("ID_"));
|
||||
|
||||
|
@ -104,9 +82,8 @@ public class SPMetadataDescriptor {
|
|||
spSSODescriptor.addAssertionConsumerService(assertionConsumerEndpoint);
|
||||
|
||||
entityDescriptor.addChoiceType(new EntityDescriptorType.EDTChoiceType(Arrays.asList(new EntityDescriptorType.EDTDescriptorChoiceType(spSSODescriptor))));
|
||||
metadataWriter.writeEntityDescriptor(entityDescriptor);
|
||||
|
||||
return sw.toString();
|
||||
return entityDescriptor;
|
||||
}
|
||||
|
||||
public static Element buildKeyInfoElement(String keyName, String pemEncodedCertificate)
|
||||
|
|
|
@ -22,7 +22,9 @@ import org.keycloak.broker.provider.AuthenticationRequest;
|
|||
import org.keycloak.broker.provider.BrokeredIdentityContext;
|
||||
import org.keycloak.broker.provider.IdentityBrokerException;
|
||||
import org.keycloak.broker.provider.IdentityProviderDataMarshaller;
|
||||
import org.keycloak.broker.provider.IdentityProviderMapper;
|
||||
import org.keycloak.broker.provider.util.SimpleHttp;
|
||||
import org.keycloak.broker.saml.mappers.UserAttributeMapper;
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.crypto.Algorithm;
|
||||
import org.keycloak.crypto.KeyStatus;
|
||||
|
@ -31,11 +33,16 @@ import org.keycloak.dom.saml.v2.assertion.AssertionType;
|
|||
import org.keycloak.dom.saml.v2.assertion.AuthnStatementType;
|
||||
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
||||
import org.keycloak.dom.saml.v2.metadata.AttributeConsumingServiceType;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.LocalizedNameType;
|
||||
import org.keycloak.dom.saml.v2.metadata.RequestedAttributeType;
|
||||
import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
|
||||
import org.keycloak.dom.saml.v2.protocol.LogoutRequestType;
|
||||
import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.KeyManager;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -45,6 +52,7 @@ import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
|
|||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
import org.keycloak.protocol.saml.SamlService;
|
||||
import org.keycloak.protocol.saml.SamlSessionUtils;
|
||||
import org.keycloak.protocol.saml.mappers.SamlMetadataDescriptorUpdater;
|
||||
import org.keycloak.protocol.saml.preprocessor.SamlAuthenticationPreprocessor;
|
||||
import org.keycloak.saml.SAML2AuthnRequestBuilder;
|
||||
import org.keycloak.saml.SAML2LogoutRequestBuilder;
|
||||
|
@ -57,8 +65,10 @@ import org.keycloak.saml.common.constants.GeneralConstants;
|
|||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||
import org.keycloak.saml.common.util.DocumentUtil;
|
||||
import org.keycloak.saml.common.util.StaxUtil;
|
||||
import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
|
||||
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
|
||||
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLMetadataWriter;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
import org.keycloak.saml.validators.DestinationValidator;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
|
@ -74,6 +84,10 @@ import javax.ws.rs.core.UriBuilder;
|
|||
import javax.ws.rs.core.UriInfo;
|
||||
import javax.xml.crypto.dsig.CanonicalizationMethod;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.net.URI;
|
||||
import java.security.KeyPair;
|
||||
import java.util.Arrays;
|
||||
|
@ -340,12 +354,13 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||
.path("endpoint")
|
||||
.build();
|
||||
|
||||
|
||||
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
|
||||
boolean wantAssertionsSigned = getConfig().isWantAssertionsSigned();
|
||||
boolean wantAssertionsEncrypted = getConfig().isWantAssertionsEncrypted();
|
||||
String entityId = getEntityId(uriInfo, realm);
|
||||
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
|
||||
int attributeConsumingServiceIndex = getConfig().getAttributeConsumingServiceIndex() != null ? getConfig().getAttributeConsumingServiceIndex(): 1;
|
||||
String attributeConsumingServiceName = getConfig().getAttributeConsumingServiceName();
|
||||
|
||||
List<Element> signingKeys = new LinkedList<>();
|
||||
List<Element> encryptionKeys = new LinkedList<>();
|
||||
|
@ -369,9 +384,56 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||
}
|
||||
});
|
||||
|
||||
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint,
|
||||
wantAuthnRequestsSigned, wantAssertionsSigned, wantAssertionsEncrypted,
|
||||
entityId, nameIDPolicyFormat, signingKeys, encryptionKeys);
|
||||
// Prepare the metadata descriptor model
|
||||
StringWriter sw = new StringWriter();
|
||||
XMLStreamWriter writer = StaxUtil.getXMLStreamWriter(sw);
|
||||
SAMLMetadataWriter metadataWriter = new SAMLMetadataWriter(writer);
|
||||
|
||||
EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPdescriptor(
|
||||
authnBinding, authnBinding, endpoint, endpoint,
|
||||
wantAuthnRequestsSigned, wantAssertionsSigned, wantAssertionsEncrypted,
|
||||
entityId, nameIDPolicyFormat, signingKeys, encryptionKeys);
|
||||
|
||||
// Create the AttributeConsumingService
|
||||
AttributeConsumingServiceType attributeConsumingService = new AttributeConsumingServiceType(attributeConsumingServiceIndex);
|
||||
attributeConsumingService.setIsDefault(true);
|
||||
|
||||
if (attributeConsumingServiceName != null && attributeConsumingServiceName.length() > 0)
|
||||
{
|
||||
String currentLocale = realm.getDefaultLocale() == null ? "en": realm.getDefaultLocale();
|
||||
LocalizedNameType attributeConsumingServiceNameElement = new LocalizedNameType(currentLocale);
|
||||
attributeConsumingServiceNameElement.setValue(attributeConsumingServiceName);
|
||||
attributeConsumingService.addServiceName(attributeConsumingServiceNameElement);
|
||||
}
|
||||
|
||||
// Look for the SP descriptor and add the attribute consuming service
|
||||
for (EntityDescriptorType.EDTChoiceType choiceType: entityDescriptor.getChoiceType()) {
|
||||
List<EntityDescriptorType.EDTDescriptorChoiceType> descriptors = choiceType.getDescriptors();
|
||||
|
||||
if (descriptors != null) {
|
||||
for (EntityDescriptorType.EDTDescriptorChoiceType descriptor: descriptors) {
|
||||
if (descriptor.getSpDescriptor() != null) {
|
||||
descriptor.getSpDescriptor().addAttributeConsumerService(attributeConsumingService);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the attribute mappers
|
||||
realm.getIdentityProviderMappersByAliasStream(getConfig().getAlias())
|
||||
.forEach(mapper -> {
|
||||
IdentityProviderMapper target = (IdentityProviderMapper) session.getKeycloakSessionFactory().getProviderFactory(IdentityProviderMapper.class, mapper.getIdentityProviderMapper());
|
||||
if (target instanceof SamlMetadataDescriptorUpdater)
|
||||
{
|
||||
SamlMetadataDescriptorUpdater metadataAttrProvider = (SamlMetadataDescriptorUpdater)target;
|
||||
metadataAttrProvider.updateMetadata(mapper, entityDescriptor);
|
||||
}
|
||||
});
|
||||
|
||||
// Write the metadata and export it to a string
|
||||
metadataWriter.writeEntityDescriptor(entityDescriptor);
|
||||
|
||||
String descriptor = sw.toString();
|
||||
|
||||
// Metadata signing
|
||||
if (getConfig().isSignSpMetadata())
|
||||
|
|
|
@ -61,6 +61,7 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||
public static final String SIGN_SP_METADATA = "signSpMetadata";
|
||||
public static final String ALLOW_CREATE = "allowCreate";
|
||||
public static final String ATTRIBUTE_CONSUMING_SERVICE_INDEX = "attributeConsumingServiceIndex";
|
||||
public static final String ATTRIBUTE_CONSUMING_SERVICE_NAME = "attributeConsumingServiceName";
|
||||
|
||||
public SAMLIdentityProviderConfig() {
|
||||
}
|
||||
|
@ -370,6 +371,14 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||
}
|
||||
}
|
||||
|
||||
public void setAttributeConsumingServiceName(String attributeConsumingServiceName) {
|
||||
getConfig().put(ATTRIBUTE_CONSUMING_SERVICE_NAME, attributeConsumingServiceName);
|
||||
}
|
||||
|
||||
public String getAttributeConsumingServiceName() {
|
||||
return getConfig().get(ATTRIBUTE_CONSUMING_SERVICE_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(RealmModel realm) {
|
||||
SslRequired sslRequired = realm.getSslRequired();
|
||||
|
|
|
@ -25,11 +25,15 @@ import org.keycloak.common.util.CollectionUtil;
|
|||
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.metadata.AttributeConsumingServiceType;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.RequestedAttributeType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderSyncMode;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.saml.mappers.SamlMetadataDescriptorUpdater;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.saml.common.util.StringUtil;
|
||||
|
||||
|
@ -44,11 +48,13 @@ import java.util.function.Predicate;
|
|||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ATTRIBUTE_FORMAT_BASIC;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class UserAttributeMapper extends AbstractIdentityProviderMapper {
|
||||
public class UserAttributeMapper extends AbstractIdentityProviderMapper implements SamlMetadataDescriptorUpdater {
|
||||
|
||||
public static final String[] COMPATIBLE_PROVIDERS = {SAMLIdentityProviderFactory.PROVIDER_ID};
|
||||
|
||||
|
@ -214,4 +220,31 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
|
|||
return "Import declared saml attribute if it exists in assertion into the specified user property or attribute.";
|
||||
}
|
||||
|
||||
// ISpMetadataAttributeProvider interface
|
||||
@Override
|
||||
public void updateMetadata(IdentityProviderMapperModel mapperModel, EntityDescriptorType entityDescriptor) {
|
||||
RequestedAttributeType requestedAttribute = new RequestedAttributeType(mapperModel.getConfig().get(UserAttributeMapper.ATTRIBUTE_NAME));
|
||||
requestedAttribute.setIsRequired(null);
|
||||
requestedAttribute.setNameFormat(ATTRIBUTE_FORMAT_BASIC.get());
|
||||
|
||||
String attributeFriendlyName = mapperModel.getConfig().get(UserAttributeMapper.ATTRIBUTE_FRIENDLY_NAME);
|
||||
if (attributeFriendlyName != null && attributeFriendlyName.length() > 0)
|
||||
requestedAttribute.setFriendlyName(attributeFriendlyName);
|
||||
|
||||
// Add the requestedAttribute item to any AttributeConsumingServices
|
||||
for (EntityDescriptorType.EDTChoiceType choiceType: entityDescriptor.getChoiceType()) {
|
||||
List<EntityDescriptorType.EDTDescriptorChoiceType> descriptors = choiceType.getDescriptors();
|
||||
|
||||
if (descriptors != null) {
|
||||
for (EntityDescriptorType.EDTDescriptorChoiceType descriptor: descriptors) {
|
||||
if (descriptor.getSpDescriptor() != null && descriptor.getSpDescriptor().getAttributeConsumingService() != null) {
|
||||
for (AttributeConsumingServiceType attributeConsumingService: descriptor.getSpDescriptor().getAttributeConsumingService())
|
||||
{
|
||||
attributeConsumingService.addRequestedAttribute(requestedAttribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ package org.keycloak.protocol.saml.installation;
|
|||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
@ -29,13 +29,17 @@ import org.keycloak.protocol.saml.SamlClient;
|
|||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
import org.keycloak.saml.SPMetadataDescriptor;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import org.keycloak.saml.common.util.StaxUtil;
|
||||
import org.keycloak.saml.processing.core.saml.v2.writers.SAMLMetadataWriter;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.StringWriter;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -90,9 +94,19 @@ public class SamlSPDescriptorClientInstallation implements ClientInstallationPro
|
|||
if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
|
||||
Element spCertificate = SPMetadataDescriptor.buildKeyInfoElement(null, samlClient.getClientSigningCertificate());
|
||||
Element encCertificate = SPMetadataDescriptor.buildKeyInfoElement(null, samlClient.getClientEncryptingCertificate());
|
||||
return SPMetadataDescriptor.getSPDescriptor(loginBinding, logoutBinding, new URI(assertionUrl), new URI(logoutUrl), samlClient.requiresClientSignature(),
|
||||
samlClient.requiresAssertionSignature(), samlClient.requiresEncryption(),
|
||||
client.getClientId(), nameIdFormat, Arrays.asList(spCertificate), Arrays.asList(encCertificate));
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
XMLStreamWriter writer = StaxUtil.getXMLStreamWriter(sw);
|
||||
SAMLMetadataWriter metadataWriter = new SAMLMetadataWriter(writer);
|
||||
|
||||
EntityDescriptorType entityDescriptor = SPMetadataDescriptor.buildSPdescriptor(
|
||||
loginBinding, logoutBinding, new URI(assertionUrl), new URI(logoutUrl),
|
||||
samlClient.requiresClientSignature(), samlClient.requiresAssertionSignature(), samlClient.requiresEncryption(),
|
||||
client.getClientId(), nameIdFormat, Arrays.asList(spCertificate), Arrays.asList(encCertificate));
|
||||
|
||||
metadataWriter.writeEntityDescriptor(entityDescriptor);
|
||||
|
||||
return sw.toString();
|
||||
} catch (Exception ex) {
|
||||
logger.error("Cannot generate SP metadata", ex);
|
||||
return "";
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package org.keycloak.protocol.saml.mappers;
|
||||
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
|
||||
public interface SamlMetadataDescriptorUpdater
|
||||
{
|
||||
void updateMetadata(IdentityProviderMapperModel mapperModel, EntityDescriptorType descriptor);
|
||||
}
|
|
@ -14,7 +14,9 @@ import java.io.Closeable;
|
|||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
|
||||
|
||||
/**
|
||||
|
@ -95,4 +97,4 @@ public final class KcSamlAttributeConsumingServiceIndexTest extends AbstractBrok
|
|||
.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import org.apache.tools.ant.filters.StringInputStream;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
|
||||
import org.keycloak.broker.saml.mappers.UserAttributeMapper;
|
||||
import org.keycloak.dom.saml.v2.metadata.EntityDescriptorType;
|
||||
import org.keycloak.dom.saml.v2.metadata.SPSSODescriptorType;
|
||||
import org.keycloak.models.IdentityProviderMapperModel;
|
||||
import org.keycloak.models.IdentityProviderMapperSyncMode;
|
||||
import org.keycloak.representations.idm.IdentityProviderMapperRepresentation;
|
||||
import org.keycloak.saml.common.exceptions.ParsingException;
|
||||
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
|
||||
import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class KcSamlSpDescriptorTest extends AbstractBrokerTest {
|
||||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return KcSamlBrokerConfiguration.INSTANCE;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttributeConsumingServiceIndexInSpMetadata() throws IOException, ParsingException, URISyntaxException {
|
||||
try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource)
|
||||
.setAttribute(SAMLIdentityProviderConfig.ATTRIBUTE_CONSUMING_SERVICE_INDEX, "15")
|
||||
.update())
|
||||
{
|
||||
|
||||
String spDescriptorString = identityProviderResource.export(null).readEntity(String.class);
|
||||
SAMLParser parser = SAMLParser.getInstance();
|
||||
EntityDescriptorType o = (EntityDescriptorType) parser.parse(new StringInputStream(spDescriptorString));
|
||||
SPSSODescriptorType spDescriptor = o.getChoiceType().get(0).getDescriptors().get(0).getSpDescriptor();
|
||||
|
||||
assertThat(spDescriptor.getAttributeConsumingService().isEmpty(), is(false));
|
||||
assertThat(spDescriptor.getAttributeConsumingService().get(0).getIndex(), is(15));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttributeConsumingServiceNameInSpMetadata() throws IOException, ParsingException, URISyntaxException {
|
||||
try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource)
|
||||
.setAttribute(SAMLIdentityProviderConfig.ATTRIBUTE_CONSUMING_SERVICE_NAME, "My Attribute Set")
|
||||
.update())
|
||||
{
|
||||
|
||||
String spDescriptorString = identityProviderResource.export(null).readEntity(String.class);
|
||||
SAMLParser parser = SAMLParser.getInstance();
|
||||
EntityDescriptorType o = (EntityDescriptorType) parser.parse(new StringInputStream(spDescriptorString));
|
||||
SPSSODescriptorType spDescriptor = o.getChoiceType().get(0).getDescriptors().get(0).getSpDescriptor();
|
||||
|
||||
assertThat(spDescriptor.getAttributeConsumingService().isEmpty(), is(false));
|
||||
assertThat(spDescriptor.getAttributeConsumingService().get(0).getServiceName().get(0).getValue(), is("My Attribute Set"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAttributeConsumingServiceMappersInSpMetadata() throws IOException, ParsingException, URISyntaxException {
|
||||
try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource)
|
||||
.setAttribute(SAMLIdentityProviderConfig.ATTRIBUTE_CONSUMING_SERVICE_INDEX, "12")
|
||||
.update())
|
||||
{
|
||||
IdentityProviderMapperRepresentation attrMapperEmail = new IdentityProviderMapperRepresentation();
|
||||
attrMapperEmail.setName("attribute-mapper-email");
|
||||
attrMapperEmail.setIdentityProviderMapper(UserAttributeMapper.PROVIDER_ID);
|
||||
attrMapperEmail.setConfig(ImmutableMap.<String,String>builder()
|
||||
.put(IdentityProviderMapperModel.SYNC_MODE, IdentityProviderMapperSyncMode.INHERIT.toString())
|
||||
.put(UserAttributeMapper.ATTRIBUTE_NAME, "email_attr_name")
|
||||
.put(UserAttributeMapper.ATTRIBUTE_FRIENDLY_NAME, "email_attr_friendlyname")
|
||||
.put(UserAttributeMapper.USER_ATTRIBUTE, "email")
|
||||
.build());
|
||||
attrMapperEmail.setIdentityProviderAlias(bc.getIDPAlias());
|
||||
|
||||
identityProviderResource.addMapper(attrMapperEmail);
|
||||
|
||||
String spDescriptorString = identityProviderResource.export(null).readEntity(String.class);
|
||||
SAMLParser parser = SAMLParser.getInstance();
|
||||
EntityDescriptorType o = (EntityDescriptorType) parser.parse(new StringInputStream(spDescriptorString));
|
||||
SPSSODescriptorType spDescriptor = o.getChoiceType().get(0).getDescriptors().get(0).getSpDescriptor();
|
||||
|
||||
assertThat(spDescriptor.getAttributeConsumingService().isEmpty(), is(false));
|
||||
assertThat(spDescriptor.getAttributeConsumingService().get(0).getIndex(), is(12));
|
||||
assertThat(spDescriptor.getAttributeConsumingService().get(0).getRequestedAttribute() != null, is(true));
|
||||
assertThat(spDescriptor.getAttributeConsumingService().get(0).getRequestedAttribute().isEmpty(), is(false));
|
||||
assertThat(spDescriptor.getAttributeConsumingService().get(0).getRequestedAttribute().get(0).getName(), is("email_attr_name"));
|
||||
assertThat(spDescriptor.getAttributeConsumingService().get(0).getRequestedAttribute().get(0).getFriendlyName(), is("email_attr_friendlyname"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -730,6 +730,8 @@ identity-provider.saml.protocol-endpoints.saml=SAML 2.0 Service Provider Metadat
|
|||
identity-provider.saml.protocol-endpoints.saml.tooltip=Shows the configuration of the Service Provider endpoint
|
||||
identity-provider.saml.attribute-consuming-service-index=Attribute Consuming Service Index
|
||||
identity-provider.saml.attribute-consuming-service-index.tooltip=Index of the Attribute Consuming Service profile to request during authentication
|
||||
identity-provider.saml.attribute-consuming-service-name=Attribute Consuming Service Name
|
||||
identity-provider.saml.attribute-consuming-service-name.tooltip=Name of the Attribute Consuming Service profile to advertise in the SP metadata
|
||||
saml-config=SAML Config
|
||||
identity-provider.saml-config.tooltip=SAML SP and external IDP configuration.
|
||||
single-signon-service-url=Single Sign-On Service URL
|
||||
|
|
|
@ -309,6 +309,13 @@
|
|||
</div>
|
||||
<kc-tooltip>{{:: 'identity-provider.saml.attribute-consuming-service-index.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="attributeConsumingServiceName">{{:: 'identity-provider.saml.attribute-consuming-service-name' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<input class="form-control" type="text" ng-model="identityProvider.config.attributeConsumingServiceName" id="attributeConsumingServiceName"/>
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'identity-provider.saml.attribute-consuming-service-name.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend collapsed><span class="text">{{:: 'identity-provider.saml.requested-authncontext' | translate}}</span> <kc-tooltip>{{:: 'identity-provider.saml.requested-authncontext.tooltip' | translate}}</kc-tooltip></legend>
|
||||
|
|
Loading…
Reference in a new issue