KEYCLOAK-10757: Replaying assertion with signature in SAML adapters
This commit is contained in:
parent
1887d3b038
commit
7f54a57271
30 changed files with 430 additions and 84 deletions
|
@ -29,6 +29,7 @@ public class Constants {
|
|||
static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat";
|
||||
static final String LOGOUT_PAGE = "logoutPage";
|
||||
static final String FORCE_AUTHENTICATION = "forceAuthentication";
|
||||
static final String KEEP_DOM_ASSERTION = "keepDOMAssertion";
|
||||
static final String IS_PASSIVE = "isPassive";
|
||||
static final String TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN = "turnOffChangeSessionIdOnLogin";
|
||||
static final String ROLE_ATTRIBUTES = "RoleIdentifiers";
|
||||
|
@ -83,6 +84,7 @@ public class Constants {
|
|||
static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat";
|
||||
static final String LOGOUT_PAGE = "logoutPage";
|
||||
static final String FORCE_AUTHENTICATION = "forceAuthentication";
|
||||
static final String KEEP_DOM_ASSERTION = "keepDOMAssertion";
|
||||
static final String ROLE_IDENTIFIERS = "RoleIdentifiers";
|
||||
static final String SIGNING = "signing";
|
||||
static final String ENCRYPTION = "encryption";
|
||||
|
|
|
@ -60,6 +60,11 @@ public class ServiceProviderDefinition extends SimpleResourceDefinition {
|
|||
.setXmlName(Constants.XML.FORCE_AUTHENTICATION)
|
||||
.build();
|
||||
|
||||
static final SimpleAttributeDefinition KEEP_DOM_ASSERTION =
|
||||
new SimpleAttributeDefinitionBuilder(Constants.Model.KEEP_DOM_ASSERTION, ModelType.BOOLEAN, true)
|
||||
.setXmlName(Constants.XML.KEEP_DOM_ASSERTION)
|
||||
.build();
|
||||
|
||||
static final SimpleAttributeDefinition IS_PASSIVE =
|
||||
new SimpleAttributeDefinitionBuilder(Constants.Model.IS_PASSIVE, ModelType.BOOLEAN, true)
|
||||
.setXmlName(Constants.XML.IS_PASSIVE)
|
||||
|
@ -96,7 +101,7 @@ public class ServiceProviderDefinition extends SimpleResourceDefinition {
|
|||
.build();
|
||||
|
||||
static final SimpleAttributeDefinition[] ATTRIBUTES = {SSL_POLICY, NAME_ID_POLICY_FORMAT, LOGOUT_PAGE, FORCE_AUTHENTICATION,
|
||||
IS_PASSIVE, TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN};
|
||||
IS_PASSIVE, TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN, KEEP_DOM_ASSERTION};
|
||||
static final AttributeDefinition[] ELEMENTS = {PRINCIPAL_NAME_MAPPING_POLICY, PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ROLE_ATTRIBUTES,
|
||||
ROLE_MAPPINGS_PROVIDER_ID, ROLE_MAPPINGS_PROVIDER_CONFIG};
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ keycloak-saml.SP.sslPolicy=SSL Policy to use
|
|||
keycloak-saml.SP.nameIDPolicyFormat=Name ID policy format URN
|
||||
keycloak-saml.SP.logoutPage=URI to a logout page
|
||||
keycloak-saml.SP.forceAuthentication=Redirected unauthenticated request to a login page
|
||||
keycloak-saml.SP.keepDOMAssertion=Attribute to inject the DOM representation of the assertion into the SamlPrincipal (respecting the original syntax)
|
||||
keycloak-saml.SP.isPassive=If user isn't logged in just return with an error. Used to check if a user is already logged in or not
|
||||
keycloak-saml.SP.turnOffChangeSessionIdOnLogin=The session id is changed by default on a successful login. Change this to true if you want to turn this off
|
||||
keycloak-saml.SP.RoleIdentifiers=Role identifiers
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ Copyright 2019 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
@ -84,6 +84,11 @@
|
|||
<xs:documentation>Redirected unauthenticated request to a login page</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="keepDOMAssertion" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Attribute to inject the DOM representation of the assertion into the SamlPrincipal (respecting the original syntax). Default value is false</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="isPassive" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If user isn't logged in just return with an error. Used to check if a user is already logged in or not</xs:documentation>
|
||||
|
|
|
@ -15,12 +15,13 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.2">
|
||||
<secure-deployment name="my-app.war">
|
||||
<SP entityID="http://localhost:8080/sales-post-enc/"
|
||||
sslPolicy="EXTERNAL"
|
||||
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||
logoutPage="/logout.jsp"
|
||||
keepDOMAssertion="false"
|
||||
forceAuthentication="false">
|
||||
|
||||
<Keys>
|
|
@ -28,6 +28,7 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -43,14 +44,20 @@ public class SamlPrincipal implements Serializable, Principal {
|
|||
private String samlSubject;
|
||||
private String nameIDFormat;
|
||||
private AssertionType assertion;
|
||||
private Document assertionDocument;
|
||||
|
||||
public SamlPrincipal(AssertionType assertion, String name, String samlSubject, String nameIDFormat, MultivaluedHashMap<String, String> attributes, MultivaluedHashMap<String, String> friendlyAttributes) {
|
||||
this(assertion, null, name, samlSubject, nameIDFormat, attributes, friendlyAttributes);
|
||||
}
|
||||
|
||||
public SamlPrincipal(AssertionType assertion, Document assertionDocument, String name, String samlSubject, String nameIDFormat, MultivaluedHashMap<String, String> attributes, MultivaluedHashMap<String, String> friendlyAttributes) {
|
||||
this.name = name;
|
||||
this.attributes = attributes;
|
||||
this.friendlyAttributes = friendlyAttributes;
|
||||
this.samlSubject = samlSubject;
|
||||
this.nameIDFormat = nameIDFormat;
|
||||
this.assertion = assertion;
|
||||
this.assertionDocument = assertionDocument;
|
||||
}
|
||||
|
||||
public SamlPrincipal() {
|
||||
|
@ -104,6 +111,16 @@ public class SamlPrincipal implements Serializable, Principal {
|
|||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* The assertion element in DOM format, to respect the original syntax.
|
||||
* It's only available if option <em>keepDOMAssertion</em> is set to true.
|
||||
*
|
||||
* @return The document assertion or null
|
||||
*/
|
||||
public Document getAssertionDocument() {
|
||||
return assertionDocument;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
|
|
|
@ -315,6 +315,7 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
|||
private SignatureAlgorithm signatureAlgorithm;
|
||||
private String signatureCanonicalizationMethod;
|
||||
private boolean autodetectBearerOnly;
|
||||
private boolean keepDOMAssertion;
|
||||
|
||||
@Override
|
||||
public boolean turnOffChangeSessionIdOnLogin() {
|
||||
|
@ -478,4 +479,13 @@ public class DefaultSamlDeployment implements SamlDeployment {
|
|||
public void setAutodetectBearerOnly(boolean autodetectBearerOnly) {
|
||||
this.autodetectBearerOnly = autodetectBearerOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isKeepDOMAssertion() {
|
||||
return keepDOMAssertion;
|
||||
}
|
||||
|
||||
public void setKeepDOMAssertion(Boolean keepDOMAssertion) {
|
||||
this.keepDOMAssertion = keepDOMAssertion != null && keepDOMAssertion;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -183,4 +183,6 @@ public interface SamlDeployment {
|
|||
String getPrincipalAttributeName();
|
||||
boolean isAutodetectBearerOnly();
|
||||
|
||||
boolean isKeepDOMAssertion();
|
||||
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@ public class SP implements Serializable {
|
|||
private RoleMappingsProviderConfig roleMappingsProviderConfig;
|
||||
private IDP idp;
|
||||
private boolean autodetectBearerOnly;
|
||||
private boolean keepDOMAssertion;
|
||||
|
||||
public String getEntityID() {
|
||||
return entityID;
|
||||
|
@ -131,6 +132,14 @@ public class SP implements Serializable {
|
|||
this.turnOffChangeSessionIdOnLogin = turnOffChangeSessionIdOnLogin != null && turnOffChangeSessionIdOnLogin;
|
||||
}
|
||||
|
||||
public boolean isKeepDOMAssertion() {
|
||||
return keepDOMAssertion;
|
||||
}
|
||||
|
||||
public void setKeepDOMAssertion(Boolean keepDOMAssertion) {
|
||||
this.keepDOMAssertion = keepDOMAssertion != null && keepDOMAssertion;
|
||||
}
|
||||
|
||||
public List<Key> getKeys() {
|
||||
return keys;
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ public class DeploymentBuilder {
|
|||
IDP idp = sp.getIdp();
|
||||
deployment.setSignatureCanonicalizationMethod(idp.getSignatureCanonicalizationMethod());
|
||||
deployment.setAutodetectBearerOnly(sp.isAutodetectBearerOnly());
|
||||
deployment.setKeepDOMAssertion(sp.isKeepDOMAssertion());
|
||||
deployment.setSignatureAlgorithm(SignatureAlgorithm.RSA_SHA256);
|
||||
if (idp.getSignatureAlgorithm() != null) {
|
||||
deployment.setSignatureAlgorithm(SignatureAlgorithm.valueOf(idp.getSignatureAlgorithm()));
|
||||
|
|
|
@ -90,6 +90,7 @@ public enum KeycloakSamlAdapterV1QNames implements HasQName {
|
|||
ATTR_VALIDATE_REQUEST_SIGNATURE(null, "validateRequestSignature"),
|
||||
ATTR_VALIDATE_RESPONSE_SIGNATURE(null, "validateResponseSignature"),
|
||||
ATTR_VALUE(null, "value"),
|
||||
ATTR_KEEP_DOM_ASSERTION(null, "keepDOMAssertion"),
|
||||
|
||||
UNKNOWN_ELEMENT("")
|
||||
;
|
||||
|
|
|
@ -53,6 +53,7 @@ public class SpParser extends AbstractKeycloakSamlAdapterV1Parser<SP> {
|
|||
sp.setIsPassive(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_IS_PASSIVE));
|
||||
sp.setAutodetectBearerOnly(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_AUTODETECT_BEARER_ONLY));
|
||||
sp.setTurnOffChangeSessionIdOnLogin(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN));
|
||||
sp.setKeepDOMAssertion(StaxParserUtil.getBooleanAttributeValueRP(element, KeycloakSamlAdapterV1QNames.ATTR_KEEP_DOM_ASSERTION));
|
||||
|
||||
return sp;
|
||||
}
|
||||
|
|
|
@ -375,9 +375,11 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
|||
return AuthOutcome.FAILED;
|
||||
}
|
||||
|
||||
Element assertionElement = null;
|
||||
if (deployment.getIDP().getSingleSignOnService().validateAssertionSignature()) {
|
||||
try {
|
||||
if (!AssertionUtil.isSignatureValid(getAssertionFromResponse(responseHolder), deployment.getIDP().getSignatureValidationKeyLocator())) {
|
||||
assertionElement = getAssertionFromResponse(responseHolder);
|
||||
if (!AssertionUtil.isSignatureValid(assertionElement, deployment.getIDP().getSignatureValidationKeyLocator())) {
|
||||
log.error("Failed to verify saml assertion signature");
|
||||
|
||||
challenge = new AuthChallenge() {
|
||||
|
@ -493,7 +495,13 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
|||
|
||||
URI nameFormat = subjectNameID == null ? null : subjectNameID.getFormat();
|
||||
String nameFormatString = nameFormat == null ? JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get() : nameFormat.toString();
|
||||
final SamlPrincipal principal = new SamlPrincipal(assertion, principalName, principalName, nameFormatString, attributes, friendlyAttributes);
|
||||
if (deployment.isKeepDOMAssertion() && assertionElement == null) {
|
||||
// obtain the assertion from the response to add the DOM document to the principal
|
||||
assertionElement = getAssertionFromResponseNoException(responseHolder);
|
||||
}
|
||||
final SamlPrincipal principal = new SamlPrincipal(assertion,
|
||||
deployment.isKeepDOMAssertion()? getAssertionDocumentFromElement(assertionElement) : null,
|
||||
principalName, principalName, nameFormatString, attributes, friendlyAttributes);
|
||||
final String sessionIndex = authn == null ? null : authn.getSessionIndex();
|
||||
final XMLGregorianCalendar sessionNotOnOrAfter = authn == null ? null : authn.getSessionNotOnOrAfter();
|
||||
SamlSession account = new SamlSession(principal, roles, sessionIndex, sessionNotOnOrAfter);
|
||||
|
@ -534,6 +542,30 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
|
|||
return DocumentUtil.getElement(responseHolder.getSamlDocument(), new QName(JBossSAMLConstants.ASSERTION.get()));
|
||||
}
|
||||
|
||||
private Element getAssertionFromResponseNoException(final SAMLDocumentHolder responseHolder) {
|
||||
try {
|
||||
return getAssertionFromResponse(responseHolder);
|
||||
} catch (ConfigurationException|ProcessingException e) {
|
||||
log.warn("Cannot obtain DOM assertion element", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Document getAssertionDocumentFromElement(final Element assertionElement) {
|
||||
if (assertionElement == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Document assertionDoc = DocumentUtil.createDocument();
|
||||
assertionDoc.adoptNode(assertionElement);
|
||||
assertionDoc.appendChild(assertionElement);
|
||||
return assertionDoc;
|
||||
} catch (ConfigurationException e) {
|
||||
log.warn("Cannot obtain DOM assertion document", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getAttributeValue(Object attrValue) {
|
||||
String value = null;
|
||||
if (attrValue instanceof String) {
|
||||
|
|
|
@ -97,6 +97,11 @@
|
|||
<xs:documentation>SAML clients can request that a user is re-authenticated even if they are already logged in at the IDP. Default value is false.</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="keepDOMAssertion" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Attribute to inject the DOM representation of the assertion into the SamlPrincipal (respecting the original syntax). Default value is false</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="isPassive" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SAML clients can request that a user is never asked to authenticate even if they are not logged in at the IDP. Set this to true if you want this. Do not use together with forceAuthentication as they are opposite. Default value is false.</xs:documentation>
|
||||
|
|
|
@ -86,6 +86,17 @@ public class KeycloakSamlAdapterXMLParserTest {
|
|||
testValidationValid("keycloak-saml-with-role-mappings-provider.xml");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationWithKeepDOMAssertion() throws Exception {
|
||||
testValidationValid("keycloak-saml-keepdomassertion.xml");
|
||||
// check keep dom assertion is TRUE
|
||||
KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-keepdomassertion.xml", KeycloakSamlAdapter.class);
|
||||
assertNotNull(config);
|
||||
assertEquals(1, config.getSps().size());
|
||||
SP sp = config.getSps().get(0);
|
||||
assertTrue(sp.isKeepDOMAssertion());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationKeyInvalid() throws Exception {
|
||||
InputStream schemaIs = KeycloakSamlAdapterV1Parser.class.getResourceAsStream(CURRENT_XSD_LOCATION);
|
||||
|
@ -115,6 +126,7 @@ public class KeycloakSamlAdapterXMLParserTest {
|
|||
assertTrue(sp.isForceAuthentication());
|
||||
assertTrue(sp.isIsPassive());
|
||||
assertFalse(sp.isAutodetectBearerOnly());
|
||||
assertFalse(sp.isKeepDOMAssertion());
|
||||
assertEquals(2, sp.getKeys().size());
|
||||
Key signing = sp.getKeys().get(0);
|
||||
assertTrue(signing.isSigning());
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
<!--
|
||||
~ Copyright 2019 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_9.xsd">
|
||||
<SP entityID="sp"
|
||||
sslPolicy="EXTERNAL"
|
||||
nameIDPolicyFormat="format"
|
||||
forceAuthentication="true"
|
||||
keepDOMAssertion="true"
|
||||
isPassive="true">
|
||||
<Keys>
|
||||
<Key signing="true">
|
||||
<KeyStore file="file" resource="cp" password="pw">
|
||||
<PrivateKey alias="private alias" password="private pw"/>
|
||||
<Certificate alias="cert alias"/>
|
||||
</KeyStore>
|
||||
</Key>
|
||||
<Key encryption="true">
|
||||
<PrivateKeyPem>
|
||||
private pem
|
||||
</PrivateKeyPem>
|
||||
<PublicKeyPem>
|
||||
public pem
|
||||
</PublicKeyPem>
|
||||
</Key>
|
||||
</Keys>
|
||||
<PrincipalNameMapping policy="FROM_ATTRIBUTE" attribute="attribute"/>
|
||||
<RoleIdentifiers>
|
||||
<Attribute name="member"/>
|
||||
</RoleIdentifiers>
|
||||
<IDP entityID="idp"
|
||||
signatureAlgorithm="RSA_SHA256"
|
||||
signatureCanonicalizationMethod="canon"
|
||||
signaturesRequired="true"
|
||||
>
|
||||
<SingleSignOnService signRequest="true"
|
||||
validateResponseSignature="true"
|
||||
requestBinding="POST"
|
||||
bindingUrl="url"
|
||||
/>
|
||||
|
||||
<SingleLogoutService
|
||||
validateRequestSignature="true"
|
||||
validateResponseSignature="true"
|
||||
signRequest="false"
|
||||
signResponse="true"
|
||||
requestBinding="REDIRECT"
|
||||
responseBinding="POST"
|
||||
postBindingUrl="posturl"
|
||||
redirectBindingUrl="redirecturl"
|
||||
/>
|
||||
<Keys>
|
||||
<Key signing="true">
|
||||
<CertificatePem>
|
||||
cert pem
|
||||
</CertificatePem>
|
||||
</Key>
|
||||
</Keys>
|
||||
</IDP>
|
||||
</SP>
|
||||
</keycloak-saml-adapter>
|
|
@ -30,6 +30,7 @@ public class Constants {
|
|||
static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat";
|
||||
static final String LOGOUT_PAGE = "logoutPage";
|
||||
static final String FORCE_AUTHENTICATION = "forceAuthentication";
|
||||
static final String KEEP_DOM_ASSERTION = "keepDOMAssertion";
|
||||
static final String IS_PASSIVE = "isPassive";
|
||||
static final String TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN = "turnOffChangeSessionIdOnLogin";
|
||||
static final String ROLE_ATTRIBUTES = "RoleIdentifiers";
|
||||
|
@ -86,6 +87,7 @@ public class Constants {
|
|||
static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat";
|
||||
static final String LOGOUT_PAGE = "logoutPage";
|
||||
static final String FORCE_AUTHENTICATION = "forceAuthentication";
|
||||
static final String KEEP_DOM_ASSERTION = "keepDOMAssertion";
|
||||
static final String ROLE_IDENTIFIERS = "RoleIdentifiers";
|
||||
static final String SIGNING = "signing";
|
||||
static final String ENCRYPTION = "encryption";
|
||||
|
|
|
@ -62,6 +62,11 @@ public class ServiceProviderDefinition extends SimpleResourceDefinition {
|
|||
.setXmlName(Constants.XML.FORCE_AUTHENTICATION)
|
||||
.build();
|
||||
|
||||
static final SimpleAttributeDefinition KEEP_DOM_ASSERTION =
|
||||
new SimpleAttributeDefinitionBuilder(Constants.Model.KEEP_DOM_ASSERTION, ModelType.BOOLEAN, true)
|
||||
.setXmlName(Constants.XML.KEEP_DOM_ASSERTION)
|
||||
.build();
|
||||
|
||||
static final SimpleAttributeDefinition IS_PASSIVE =
|
||||
new SimpleAttributeDefinitionBuilder(Constants.Model.IS_PASSIVE, ModelType.BOOLEAN, true)
|
||||
.setXmlName(Constants.XML.IS_PASSIVE)
|
||||
|
@ -97,7 +102,7 @@ public class ServiceProviderDefinition extends SimpleResourceDefinition {
|
|||
.build();
|
||||
|
||||
static final SimpleAttributeDefinition[] ATTRIBUTES = {SSL_POLICY, NAME_ID_POLICY_FORMAT, LOGOUT_PAGE, FORCE_AUTHENTICATION,
|
||||
IS_PASSIVE, TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN};
|
||||
IS_PASSIVE, TURN_OFF_CHANGE_SESSSION_ID_ON_LOGIN, KEEP_DOM_ASSERTION};
|
||||
static final AttributeDefinition[] ELEMENTS = {PRINCIPAL_NAME_MAPPING_POLICY, PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ROLE_ATTRIBUTES,
|
||||
ROLE_MAPPINGS_PROVIDER_ID, ROLE_MAPPINGS_PROVIDER_CONFIG};
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ keycloak-saml.SP.sslPolicy=SSL Policy to use
|
|||
keycloak-saml.SP.nameIDPolicyFormat=Name ID policy format URN
|
||||
keycloak-saml.SP.logoutPage=URI to a logout page
|
||||
keycloak-saml.SP.forceAuthentication=Redirected unauthenticated request to a login page
|
||||
keycloak-saml.SP.keepDOMAssertion=Attribute to inject the DOM representation of the assertion into the SamlPrincipal (respecting the original syntax)
|
||||
keycloak-saml.SP.isPassive=If user isn't logged in just return with an error. Used to check if a user is already logged in or not
|
||||
keycloak-saml.SP.turnOffChangeSessionIdOnLogin=The session id is changed by default on a successful login. Change this to true if you want to turn this off
|
||||
keycloak-saml.SP.RoleIdentifiers=Role identifiers
|
||||
|
|
|
@ -84,6 +84,11 @@
|
|||
<xs:documentation>Redirected unauthenticated request to a login page</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="keepDOMAssertion" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Attribute to inject the DOM representation of the assertion into the SamlPrincipal (respecting the original syntax). Default value is false</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="isPassive" type="xs:boolean" use="optional">
|
||||
<xs:annotation>
|
||||
<xs:documentation>If user isn't logged in just return with an error. Used to check if a user is already logged in or not</xs:documentation>
|
||||
|
|
|
@ -1,71 +0,0 @@
|
|||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
|
||||
<secure-deployment name="my-app.war">
|
||||
<SP entityID="http://localhost:8080/sales-post-enc/"
|
||||
sslPolicy="EXTERNAL"
|
||||
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||
logoutPage="/logout.jsp"
|
||||
forceAuthentication="false"
|
||||
isPassive="true"
|
||||
turnOffChangeSessionIdOnLogin="true">
|
||||
|
||||
<Keys>
|
||||
<Key encryption="true" signing="true">
|
||||
<PrivateKeyPem>my_key.pem</PrivateKeyPem>
|
||||
<PublicKeyPem>my_key.pub</PublicKeyPem>
|
||||
<CertificatePem>cert.cer</CertificatePem>
|
||||
<KeyStore resource="/WEB-INF/keystore.jks" password="store123" file="test" alias="test" type="jks">
|
||||
<PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
|
||||
<Certificate alias="http://localhost:8080/sales-post-enc/"/>
|
||||
</KeyStore>
|
||||
</Key>
|
||||
</Keys>
|
||||
<PrincipalNameMapping policy="FROM_NAME_ID" attribute="test"/>
|
||||
<RoleIdentifiers>
|
||||
<Attribute name="Role"/>
|
||||
<Attribute name="Role2"/>
|
||||
</RoleIdentifiers>
|
||||
<IDP entityID="idp" signaturesRequired="true" signatureAlgorithm="test" signatureCanonicalizationMethod="test">
|
||||
<SingleSignOnService signRequest="true"
|
||||
validateResponseSignature="true"
|
||||
validateAssertionSignature="true"
|
||||
requestBinding="POST"
|
||||
responseBinding="POST"
|
||||
bindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"
|
||||
assertionConsumerServiceUrl="acsUrl"/>
|
||||
<SingleLogoutService
|
||||
validateRequestSignature="true"
|
||||
validateResponseSignature="true"
|
||||
signRequest="true"
|
||||
signResponse="true"
|
||||
requestBinding="POST"
|
||||
responseBinding="POST"
|
||||
postBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"
|
||||
redirectBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
|
||||
<Keys>
|
||||
<Key signing="true">
|
||||
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||
<Certificate alias="saml-demo"/>
|
||||
</KeyStore>
|
||||
</Key>
|
||||
</Keys>
|
||||
</IDP>
|
||||
</SP>
|
||||
</secure-deployment>
|
||||
</subsystem>
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
|
||||
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.2">
|
||||
<secure-deployment name="my-app.war">
|
||||
<SP entityID="http://localhost:8080/sales-post-enc/"
|
||||
sslPolicy="EXTERNAL"
|
|
@ -21,6 +21,7 @@
|
|||
sslPolicy="EXTERNAL"
|
||||
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||
logoutPage="/logout.jsp"
|
||||
keepDOMAssertion="false"
|
||||
forceAuthentication="false"
|
||||
isPassive="true"
|
||||
turnOffChangeSessionIdOnLogin="true">
|
||||
|
|
|
@ -58,7 +58,7 @@ public class KeycloakSamlSubsystemInstallation implements ClientInstallationProv
|
|||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Keycloak SAML adapter Wildfly/JBoss subsystem xml. Put this <subsystem xmlns=\"urn:jboss:domain:keycloak-saml:1.1\"> element of your standalone.xml file.";
|
||||
return "Keycloak SAML adapter Wildfly/JBoss subsystem xml. Put this <subsystem xmlns=\"urn:jboss:domain:keycloak-saml:1.2\"> element of your standalone.xml file.";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -39,10 +39,19 @@ import javax.ws.rs.core.MediaType;
|
|||
import javax.ws.rs.core.Response;
|
||||
import javax.xml.datatype.XMLGregorianCalendar;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.security.Principal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -94,6 +103,25 @@ public class SendUsernameServlet {
|
|||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("getAssertionFromDocument")
|
||||
public Response getAssertionFromDocument() throws IOException, TransformerException {
|
||||
sentPrincipal = httpServletRequest.getUserPrincipal();
|
||||
DocumentBuilderFactory domFact = DocumentBuilderFactory.newInstance();
|
||||
Document doc = ((SamlPrincipal) sentPrincipal).getAssertionDocument();
|
||||
String xml = "";
|
||||
if (doc != null) {
|
||||
DOMSource domSource = new DOMSource(doc);
|
||||
StringWriter writer = new StringWriter();
|
||||
StreamResult result = new StreamResult(writer);
|
||||
TransformerFactory tf = TransformerFactory.newInstance();
|
||||
Transformer transformer = tf.newTransformer();
|
||||
transformer.transform(domSource, result);
|
||||
xml = writer.toString();
|
||||
}
|
||||
return Response.ok(xml).header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_TYPE + ";charset=UTF-8").build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{path}")
|
||||
public Response doGetElseWhere(@PathParam("path") String path, @QueryParam("checkRoles") boolean checkRolesFlag) throws IOException {
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2019 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.adapter.page;
|
||||
|
||||
import org.jboss.arquillian.container.test.api.OperateOnDeployment;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* @author rmartinc
|
||||
*/
|
||||
public class EmployeeDomServlet extends SAMLServlet {
|
||||
public static final String DEPLOYMENT_NAME = "employee-dom";
|
||||
|
||||
@ArquillianResource
|
||||
@OperateOnDeployment(DEPLOYMENT_NAME)
|
||||
private URL url;
|
||||
|
||||
@Override
|
||||
public URL getInjectedUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
|
@ -39,10 +39,13 @@ import java.io.Closeable;
|
|||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.StringReader;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -132,6 +135,7 @@ import org.keycloak.saml.common.util.DocumentUtil;
|
|||
import org.keycloak.saml.common.util.XmlKeyInfoKeyNameTransformer;
|
||||
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.util.AssertionUtil;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
import org.keycloak.testsuite.adapter.page.*;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
|
@ -189,6 +193,9 @@ public class SAMLServletAdapterTest extends AbstractSAMLServletAdapterTest {
|
|||
@Page
|
||||
protected Employee2Servlet employee2ServletPage;
|
||||
|
||||
@Page
|
||||
protected EmployeeDomServlet employeeDomServletPage;
|
||||
|
||||
@Page
|
||||
protected EmployeeSigServlet employeeSigServletPage;
|
||||
|
||||
|
@ -307,6 +314,11 @@ public class SAMLServletAdapterTest extends AbstractSAMLServletAdapterTest {
|
|||
return samlServletDeployment(Employee2Servlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
|
||||
}
|
||||
|
||||
@Deployment(name = EmployeeDomServlet.DEPLOYMENT_NAME)
|
||||
protected static WebArchive employeedom() {
|
||||
return samlServletDeployment(EmployeeDomServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
|
||||
}
|
||||
|
||||
@Deployment(name = EmployeeSigServlet.DEPLOYMENT_NAME)
|
||||
protected static WebArchive employeeSig() {
|
||||
return samlServletDeployment(EmployeeSigServlet.DEPLOYMENT_NAME, SendUsernameServlet.class);
|
||||
|
@ -1421,6 +1433,10 @@ public class SAMLServletAdapterTest extends AbstractSAMLServletAdapterTest {
|
|||
waitUntilElement(By.xpath("//body")).text().contains("phone: 617");
|
||||
waitUntilElement(By.xpath("//body")).text().contains("friendlyAttribute phone: null");
|
||||
|
||||
driver.navigate().to(employee2ServletPage.getUriBuilder().clone().path("getAssertionFromDocument").build().toURL());
|
||||
waitForPageToLoad();
|
||||
Assert.assertEquals("", driver.getPageSource());
|
||||
|
||||
employee2ServletPage.logout();
|
||||
checkLoggedOut(employee2ServletPage, testRealmSAMLPostLoginPage);
|
||||
|
||||
|
@ -1483,6 +1499,25 @@ public class SAMLServletAdapterTest extends AbstractSAMLServletAdapterTest {
|
|||
validateXMLWithSchema(driver.getPageSource(), "/adapter-test/keycloak-saml/metadata-schema/saml-schema-metadata-2.0.xsd");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDOMAssertion() throws Exception {
|
||||
assertSuccessfulLogin(employeeDomServletPage, bburkeUser, testRealmSAMLPostLoginPage, "principal=bburke");
|
||||
assertSuccessfullyLoggedIn(employeeDomServletPage, "principal=bburke");
|
||||
|
||||
driver.navigate().to(employeeDomServletPage.getUriBuilder().clone().path("getAssertionFromDocument").build().toURL());
|
||||
waitForPageToLoad();
|
||||
String xml = driver.getPageSource();
|
||||
Assert.assertNotEquals("", xml);
|
||||
Document doc = DocumentUtil.getDocument(new StringReader(xml));
|
||||
String certBase64 = DocumentUtil.getElement(doc, new QName("http://www.w3.org/2000/09/xmldsig#", "X509Certificate")).getTextContent();
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
Certificate cert = cf.generateCertificate(new ByteArrayInputStream(Base64.decode(certBase64)));
|
||||
PublicKey pubkey = cert.getPublicKey();
|
||||
Assert.assertTrue(AssertionUtil.isSignatureValid(doc.getDocumentElement(), pubkey));
|
||||
|
||||
employeeDomServletPage.logout();
|
||||
checkLoggedOut(employeeDomServletPage, testRealmSAMLPostLoginPage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void spMetadataValidation() throws Exception {
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
<!--
|
||||
~ Copyright 2019 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<keycloak-saml-adapter xmlns="urn:keycloak:saml:adapter"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_7.xsd">
|
||||
<SP entityID="http://localhost:8280/employee-dom/"
|
||||
sslPolicy="EXTERNAL"
|
||||
logoutPage="/logout.jsp"
|
||||
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
|
||||
keepDOMAssertion="true"
|
||||
forceAuthentication="false">
|
||||
<Keys>
|
||||
<Key signing="true" >
|
||||
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||
<PrivateKey alias="http://localhost:8080/employee-dom/" password="store123"/>
|
||||
<Certificate alias="http://localhost:8080/employee-dom/"/>
|
||||
</KeyStore>
|
||||
</Key>
|
||||
</Keys>
|
||||
<PrincipalNameMapping policy="FROM_NAME_ID"/>
|
||||
<RoleIdentifiers>
|
||||
<Attribute name="Role"/>
|
||||
</RoleIdentifiers>
|
||||
<IDP entityID="idp">
|
||||
<SingleSignOnService signRequest="true"
|
||||
validateResponseSignature="true"
|
||||
requestBinding="POST"
|
||||
bindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
|
||||
/>
|
||||
|
||||
<SingleLogoutService
|
||||
validateRequestSignature="true"
|
||||
validateResponseSignature="true"
|
||||
signRequest="true"
|
||||
signResponse="true"
|
||||
requestBinding="POST"
|
||||
responseBinding="POST"
|
||||
postBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
|
||||
redirectBindingUrl="http://localhost:8080/auth/realms/demo/protocol/saml"
|
||||
/>
|
||||
<Keys>
|
||||
<Key signing="true">
|
||||
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
|
||||
<Certificate alias="demo"/>
|
||||
</KeyStore>
|
||||
</Key>
|
||||
</Keys>
|
||||
</IDP>
|
||||
</SP>
|
||||
</keycloak-saml-adapter>
|
Binary file not shown.
|
@ -625,6 +625,61 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"clientId": "http://localhost:8280/employee-dom/",
|
||||
"enabled": true,
|
||||
"protocol": "saml",
|
||||
"fullScopeAllowed": true,
|
||||
"baseUrl": "http://localhost:8080/employee-dom",
|
||||
"redirectUris": [
|
||||
"http://localhost:8080/employee-dom/*"
|
||||
],
|
||||
"adminUrl": "http://localhost:8080/employee-dom",
|
||||
"attributes": {
|
||||
"saml.assertion.signature": "true",
|
||||
"saml.server.signature": "true",
|
||||
"saml.client.signature": "true",
|
||||
"saml.signature.algorithm": "RSA_SHA256",
|
||||
"saml.authnstatement": "true",
|
||||
"saml.signing.certificate": "MIIC+zCCAeOgAwIBAgIEcFrChjANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDEyNodHRwOi8vbG9jYWxob3N0OjgwODAvZW1wbG95ZWUtZG9tLzAeFw0xOTA3MDMwOTE1NDlaFw00NjExMTgwOTE1NDlaMC4xLDAqBgNVBAMTI2h0dHA6Ly9sb2NhbGhvc3Q6ODA4MC9lbXBsb3llZS1kb20vMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmEbjaKmKCh2MXTVLMUXdbjKSdmXAOenuE2bDD0AlEaJmnJ5zU2JY6UuFflH3332n2YktaaCyTznwX1Zcf7GH3bm7xhV1HSmlbFpIY17M8QUOIGZEzvKSbT9gjRJSPIdE1JvZuqgzuXpRlRfC4eoH1VgS0Vmu4gwIRFnUUgqc5hW11AQVkGZs7TkEYbVEYneKMbQOKa1OzW+FAb7C13Yn19gSvGr3THE+7FGwxEJM6N6kr4xnxg4VpaXcsW4ijGI3CHPJA06MZ6LzXxCmz+8TOSLo5pV7GKgME9QR1lBSC2Cp0yDtHjqK6QCqApyHhP2xN8qzJhMIhffSSHq4GokhjwIDAQABoyEwHzAdBgNVHQ4EFgQUOVG/h7cr+T6LJ4dQIVALBknwF/AwDQYJKoZIhvcNAQELBQADggEBAI5Y1MPMHPsDRJBQke/+tkRO4PALbsAQtfvYDNmpBGzUNo2xU3n7PNzbWrcqubjLN0nqXloBTaeeHtrFGAejMCS5X8UOGLyXbKBm7hHJs5ZZASrm0FkUzyuJexWCbSAg0p7Z6wWw03dnV/A9LDFwTdGIYsnSzZ59/v3BUH89mavOwVuVJB5O2PysUob3urcv1tmv9eL5jAMc764ID1gLkydcNrmICa+aZ/FojfReyTtwWX0DoPflPvF/Xllp3jLg1HwSlD6fD2wO/MKawgBbE6xrAkg5bF01B25RadJJffx3hEtgxBzlo1EL4Ir+lJmM1vzuTq4c1wDYKku4Y0Qg5o0="
|
||||
},
|
||||
"protocolMappers": [
|
||||
{
|
||||
"name": "email",
|
||||
"protocol": "saml",
|
||||
"protocolMapper": "saml-user-property-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "email",
|
||||
"friendly.name": "email",
|
||||
"attribute.name": "urn:oid:1.2.840.113549.1.9.1",
|
||||
"attribute.nameformat": "URI Reference"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "phone",
|
||||
"protocol": "saml",
|
||||
"protocolMapper": "saml-user-attribute-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"user.attribute": "phone",
|
||||
"attribute.name": "phone",
|
||||
"attribute.nameformat": "Basic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "role-list",
|
||||
"protocol": "saml",
|
||||
"protocolMapper": "saml-role-list-mapper",
|
||||
"consentRequired": false,
|
||||
"config": {
|
||||
"attribute.name": "Role",
|
||||
"attribute.nameformat": "Basic",
|
||||
"single": "false"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"clientId": "http://localhost:8280/employee-sig-front/",
|
||||
"enabled": true,
|
||||
|
|
Loading…
Reference in a new issue