KEYCLOAK-13950 SAML2 Identity Provider - Send Subject in SAML requests
This commit is contained in:
parent
344003264a
commit
e82fe7d9e3
19 changed files with 210 additions and 26 deletions
|
@ -17,7 +17,9 @@
|
||||||
package org.keycloak.saml;
|
package org.keycloak.saml;
|
||||||
|
|
||||||
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
import org.keycloak.dom.saml.v2.assertion.NameIDType;
|
||||||
|
import org.keycloak.dom.saml.v2.assertion.SubjectType;
|
||||||
import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
|
import org.keycloak.dom.saml.v2.protocol.AuthnRequestType;
|
||||||
|
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||||
import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
|
import org.keycloak.saml.processing.api.saml.v2.request.SAML2Request;
|
||||||
import org.keycloak.saml.processing.core.saml.v2.common.IDGenerator;
|
import org.keycloak.saml.processing.core.saml.v2.common.IDGenerator;
|
||||||
import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
|
import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil;
|
||||||
|
@ -26,7 +28,6 @@ import org.w3c.dom.Document;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author pedroigor
|
* @author pedroigor
|
||||||
|
@ -88,6 +89,25 @@ public class SAML2AuthnRequestBuilder implements SamlProtocolExtensionsAwareBuil
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SAML2AuthnRequestBuilder subject(String subject) {
|
||||||
|
String sanitizedSubject = subject != null ? subject.trim() : null;
|
||||||
|
if (sanitizedSubject != null && !sanitizedSubject.isEmpty()) {
|
||||||
|
this.authnRequestType.setSubject(createSubject(sanitizedSubject));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SubjectType createSubject(String value) {
|
||||||
|
NameIDType nameId = new NameIDType();
|
||||||
|
nameId.setValue(value);
|
||||||
|
nameId.setFormat(this.authnRequestType.getNameIDPolicy() != null ? this.authnRequestType.getNameIDPolicy().getFormat() : null);
|
||||||
|
SubjectType subject = new SubjectType();
|
||||||
|
SubjectType.STSubType subType = new SubjectType.STSubType();
|
||||||
|
subType.addBaseID(nameId);
|
||||||
|
subject.setSubType(subType);
|
||||||
|
return subject;
|
||||||
|
}
|
||||||
|
|
||||||
public Document toDocument() {
|
public Document toDocument() {
|
||||||
try {
|
try {
|
||||||
AuthnRequestType authnRequestType = createAuthnRequest();
|
AuthnRequestType authnRequestType = createAuthnRequest();
|
||||||
|
|
|
@ -81,10 +81,12 @@ public class BaseWriter {
|
||||||
*
|
*
|
||||||
* @throws org.keycloak.saml.common.exceptions.ProcessingException
|
* @throws org.keycloak.saml.common.exceptions.ProcessingException
|
||||||
*/
|
*/
|
||||||
public void write(NameIDType nameIDType, QName tag) throws ProcessingException {
|
public void write(NameIDType nameIDType, QName tag, boolean writeNamespace) throws ProcessingException {
|
||||||
StaxUtil.writeStartElement(writer, tag.getPrefix(), tag.getLocalPart(), tag.getNamespaceURI());
|
StaxUtil.writeStartElement(writer, tag.getPrefix(), tag.getLocalPart(), tag.getNamespaceURI());
|
||||||
|
|
||||||
StaxUtil.writeNameSpace(writer, ASSERTION_PREFIX, ASSERTION_NSURI.get());
|
if (writeNamespace) {
|
||||||
|
StaxUtil.writeNameSpace(writer, ASSERTION_PREFIX, ASSERTION_NSURI.get());
|
||||||
|
}
|
||||||
|
|
||||||
URI format = nameIDType.getFormat();
|
URI format = nameIDType.getFormat();
|
||||||
if (format != null) {
|
if (format != null) {
|
||||||
|
@ -115,6 +117,13 @@ public class BaseWriter {
|
||||||
StaxUtil.flush(writer);
|
StaxUtil.flush(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write {@code NameIDType} to stream without writing a namespace
|
||||||
|
*/
|
||||||
|
public void write(NameIDType nameIDType, QName tag) throws ProcessingException {
|
||||||
|
this.write(nameIDType, tag, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write an {@code AttributeType} to stream
|
* Write an {@code AttributeType} to stream
|
||||||
*
|
*
|
||||||
|
|
|
@ -64,6 +64,7 @@ public class SAMLRequestWriter extends BaseWriter {
|
||||||
public void write(AuthnRequestType request) throws ProcessingException {
|
public void write(AuthnRequestType request) throws ProcessingException {
|
||||||
StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.AUTHN_REQUEST.get(), PROTOCOL_NSURI.get());
|
StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.AUTHN_REQUEST.get(), PROTOCOL_NSURI.get());
|
||||||
StaxUtil.writeNameSpace(writer, PROTOCOL_PREFIX, PROTOCOL_NSURI.get());
|
StaxUtil.writeNameSpace(writer, PROTOCOL_PREFIX, PROTOCOL_NSURI.get());
|
||||||
|
StaxUtil.writeNameSpace(writer, ASSERTION_PREFIX, ASSERTION_NSURI.get());
|
||||||
StaxUtil.writeDefaultNameSpace(writer, ASSERTION_NSURI.get());
|
StaxUtil.writeDefaultNameSpace(writer, ASSERTION_NSURI.get());
|
||||||
|
|
||||||
// Attributes
|
// Attributes
|
||||||
|
@ -119,7 +120,12 @@ public class SAMLRequestWriter extends BaseWriter {
|
||||||
|
|
||||||
NameIDType issuer = request.getIssuer();
|
NameIDType issuer = request.getIssuer();
|
||||||
if (issuer != null) {
|
if (issuer != null) {
|
||||||
write(issuer, new QName(ASSERTION_NSURI.get(), JBossSAMLConstants.ISSUER.get(), ASSERTION_PREFIX));
|
write(issuer, new QName(ASSERTION_NSURI.get(), JBossSAMLConstants.ISSUER.get(), ASSERTION_PREFIX), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubjectType subject = request.getSubject();
|
||||||
|
if (subject != null) {
|
||||||
|
write(subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
Element sig = request.getSignature();
|
Element sig = request.getSignature();
|
||||||
|
@ -157,6 +163,7 @@ public class SAMLRequestWriter extends BaseWriter {
|
||||||
StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.LOGOUT_REQUEST.get(), PROTOCOL_NSURI.get());
|
StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.LOGOUT_REQUEST.get(), PROTOCOL_NSURI.get());
|
||||||
|
|
||||||
StaxUtil.writeNameSpace(writer, PROTOCOL_PREFIX, PROTOCOL_NSURI.get());
|
StaxUtil.writeNameSpace(writer, PROTOCOL_PREFIX, PROTOCOL_NSURI.get());
|
||||||
|
StaxUtil.writeNameSpace(writer, ASSERTION_PREFIX, ASSERTION_NSURI.get());
|
||||||
StaxUtil.writeDefaultNameSpace(writer, ASSERTION_NSURI.get());
|
StaxUtil.writeDefaultNameSpace(writer, ASSERTION_NSURI.get());
|
||||||
|
|
||||||
// Attributes
|
// Attributes
|
||||||
|
|
|
@ -162,12 +162,13 @@ public class SAMLResponseWriter extends BaseWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
StaxUtil.writeNameSpace(writer, PROTOCOL_PREFIX, JBossSAMLURIConstants.PROTOCOL_NSURI.get());
|
StaxUtil.writeNameSpace(writer, PROTOCOL_PREFIX, JBossSAMLURIConstants.PROTOCOL_NSURI.get());
|
||||||
|
StaxUtil.writeNameSpace(writer, ASSERTION_PREFIX, JBossSAMLURIConstants.ASSERTION_NSURI.get());
|
||||||
StaxUtil.writeDefaultNameSpace(writer, JBossSAMLURIConstants.ASSERTION_NSURI.get());
|
StaxUtil.writeDefaultNameSpace(writer, JBossSAMLURIConstants.ASSERTION_NSURI.get());
|
||||||
|
|
||||||
writeBaseAttributes(response);
|
writeBaseAttributes(response);
|
||||||
|
|
||||||
NameIDType issuer = response.getIssuer();
|
NameIDType issuer = response.getIssuer();
|
||||||
write(issuer, new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ISSUER.get(), ASSERTION_PREFIX));
|
write(issuer, new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ISSUER.get()));
|
||||||
|
|
||||||
Element sig = response.getSignature();
|
Element sig = response.getSignature();
|
||||||
if (sig != null) {
|
if (sig != null) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.util.Map;
|
||||||
public class IdentityProviderModel implements Serializable {
|
public class IdentityProviderModel implements Serializable {
|
||||||
|
|
||||||
public static final String ALLOWED_CLOCK_SKEW = "allowedClockSkew";
|
public static final String ALLOWED_CLOCK_SKEW = "allowedClockSkew";
|
||||||
|
public static final String LOGIN_HINT = "loginHint";
|
||||||
|
|
||||||
public static final String SYNC_MODE = "syncMode";
|
public static final String SYNC_MODE = "syncMode";
|
||||||
|
|
||||||
|
@ -218,4 +219,12 @@ public class IdentityProviderModel implements Serializable {
|
||||||
public void setSyncMode(IdentityProviderSyncMode syncMode) {
|
public void setSyncMode(IdentityProviderSyncMode syncMode) {
|
||||||
getConfig().put(SYNC_MODE, syncMode.toString());
|
getConfig().put(SYNC_MODE, syncMode.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLoginHint() {
|
||||||
|
return Boolean.valueOf(getConfig().get(LOGIN_HINT));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoginHint(boolean loginHint) {
|
||||||
|
getConfig().put(LOGIN_HINT, String.valueOf(loginHint));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,14 +92,6 @@ public class OAuth2IdentityProviderConfig extends IdentityProviderModel {
|
||||||
public void setDefaultScope(String defaultScope) {
|
public void setDefaultScope(String defaultScope) {
|
||||||
getConfig().put("defaultScope", defaultScope);
|
getConfig().put("defaultScope", defaultScope);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLoginHint() {
|
|
||||||
return Boolean.valueOf(getConfig().get("loginHint"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLoginHint(boolean loginHint) {
|
|
||||||
getConfig().put("loginHint", String.valueOf(loginHint));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isJWTAuthentication() {
|
public boolean isJWTAuthentication() {
|
||||||
if (getClientAuthMethod().equals(OIDCLoginProtocol.CLIENT_SECRET_JWT)
|
if (getClientAuthMethod().equals(OIDCLoginProtocol.CLIENT_SECRET_JWT)
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.dom.saml.v2.protocol.ResponseType;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.keys.RsaKeyMetadata;
|
import org.keycloak.keys.RsaKeyMetadata;
|
||||||
import org.keycloak.models.*;
|
import org.keycloak.models.*;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
|
import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
|
||||||
import org.keycloak.protocol.saml.SamlSessionUtils;
|
import org.keycloak.protocol.saml.SamlSessionUtils;
|
||||||
import org.keycloak.protocol.saml.preprocessor.SamlAuthenticationPreprocessor;
|
import org.keycloak.protocol.saml.preprocessor.SamlAuthenticationPreprocessor;
|
||||||
|
@ -63,6 +64,7 @@ import java.util.TreeSet;
|
||||||
*/
|
*/
|
||||||
public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityProviderConfig> {
|
public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityProviderConfig> {
|
||||||
protected static final Logger logger = Logger.getLogger(SAMLIdentityProvider.class);
|
protected static final Logger logger = Logger.getLogger(SAMLIdentityProvider.class);
|
||||||
|
|
||||||
private final DestinationValidator destinationValidator;
|
private final DestinationValidator destinationValidator;
|
||||||
public SAMLIdentityProvider(KeycloakSession session, SAMLIdentityProviderConfig config, DestinationValidator destinationValidator) {
|
public SAMLIdentityProvider(KeycloakSession session, SAMLIdentityProviderConfig config, DestinationValidator destinationValidator) {
|
||||||
super(session, config);
|
super(session, config);
|
||||||
|
@ -95,13 +97,15 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||||
protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
|
protocolBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String loginHint = getConfig().isLoginHint() ? request.getAuthenticationSession().getClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM) : null;
|
||||||
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
|
SAML2AuthnRequestBuilder authnRequestBuilder = new SAML2AuthnRequestBuilder()
|
||||||
.assertionConsumerUrl(assertionConsumerServiceUrl)
|
.assertionConsumerUrl(assertionConsumerServiceUrl)
|
||||||
.destination(destinationUrl)
|
.destination(destinationUrl)
|
||||||
.issuer(issuerURL)
|
.issuer(issuerURL)
|
||||||
.forceAuthn(getConfig().isForceAuthn())
|
.forceAuthn(getConfig().isForceAuthn())
|
||||||
.protocolBinding(protocolBinding)
|
.protocolBinding(protocolBinding)
|
||||||
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
|
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat))
|
||||||
|
.subject(loginHint);
|
||||||
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(session)
|
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(session)
|
||||||
.relayState(request.getState().getEncoded());
|
.relayState(request.getState().getEncoded());
|
||||||
boolean postBinding = getConfig().isPostBindingAuthnRequest();
|
boolean postBinding = getConfig().isPostBindingAuthnRequest();
|
||||||
|
|
|
@ -124,6 +124,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||||
samlIdentityProviderConfig.setPostBindingResponse(postBindingResponse);
|
samlIdentityProviderConfig.setPostBindingResponse(postBindingResponse);
|
||||||
samlIdentityProviderConfig.setPostBindingAuthnRequest(postBindingResponse);
|
samlIdentityProviderConfig.setPostBindingAuthnRequest(postBindingResponse);
|
||||||
samlIdentityProviderConfig.setPostBindingLogout(postBindingLogout);
|
samlIdentityProviderConfig.setPostBindingLogout(postBindingLogout);
|
||||||
|
samlIdentityProviderConfig.setLoginHint(false);
|
||||||
|
|
||||||
List<String> nameIdFormatList = idpDescriptor.getNameIDFormat();
|
List<String> nameIdFormatList = idpDescriptor.getNameIDFormat();
|
||||||
if (nameIdFormatList != null && !nameIdFormatList.isEmpty())
|
if (nameIdFormatList != null && !nameIdFormatList.isEmpty())
|
||||||
|
|
|
@ -353,8 +353,9 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
public Response performPostLogin(@PathParam("provider_id") String providerId,
|
public Response performPostLogin(@PathParam("provider_id") String providerId,
|
||||||
@QueryParam(LoginActionsService.SESSION_CODE) String code,
|
@QueryParam(LoginActionsService.SESSION_CODE) String code,
|
||||||
@QueryParam("client_id") String clientId,
|
@QueryParam("client_id") String clientId,
|
||||||
@QueryParam(Constants.TAB_ID) String tabId) {
|
@QueryParam(Constants.TAB_ID) String tabId,
|
||||||
return performLogin(providerId, code, clientId, tabId);
|
@QueryParam(OIDCLoginProtocol.LOGIN_HINT_PARAM) String loginHint) {
|
||||||
|
return performLogin(providerId, code, clientId, tabId, loginHint);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
@GET
|
||||||
|
@ -363,7 +364,8 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
public Response performLogin(@PathParam("provider_id") String providerId,
|
public Response performLogin(@PathParam("provider_id") String providerId,
|
||||||
@QueryParam(LoginActionsService.SESSION_CODE) String code,
|
@QueryParam(LoginActionsService.SESSION_CODE) String code,
|
||||||
@QueryParam("client_id") String clientId,
|
@QueryParam("client_id") String clientId,
|
||||||
@QueryParam(Constants.TAB_ID) String tabId) {
|
@QueryParam(Constants.TAB_ID) String tabId,
|
||||||
|
@QueryParam(OIDCLoginProtocol.LOGIN_HINT_PARAM) String loginHint) {
|
||||||
this.event.detail(Details.IDENTITY_PROVIDER, providerId);
|
this.event.detail(Details.IDENTITY_PROVIDER, providerId);
|
||||||
|
|
||||||
if (isDebugEnabled()) {
|
if (isDebugEnabled()) {
|
||||||
|
@ -376,15 +378,18 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
||||||
return parsedCode.response;
|
return parsedCode.response;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientSessionCode clientSessionCode = parsedCode.clientSessionCode;
|
ClientSessionCode<AuthenticationSessionModel> clientSessionCode = parsedCode.clientSessionCode;
|
||||||
IdentityProviderModel identityProviderModel = realmModel.getIdentityProviderByAlias(providerId);
|
IdentityProviderModel identityProviderModel = realmModel.getIdentityProviderByAlias(providerId);
|
||||||
if (identityProviderModel == null) {
|
if (identityProviderModel == null) {
|
||||||
throw new IdentityBrokerException("Identity Provider [" + providerId + "] not found.");
|
throw new IdentityBrokerException("Identity Provider [" + providerId + "] not found.");
|
||||||
}
|
}
|
||||||
if (identityProviderModel.isLinkOnly()) {
|
if (identityProviderModel.isLinkOnly()) {
|
||||||
throw new IdentityBrokerException("Identity Provider [" + providerId + "] is not allowed to perform a login.");
|
throw new IdentityBrokerException("Identity Provider [" + providerId + "] is not allowed to perform a login.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if (clientSessionCode != null && clientSessionCode.getClientSession() != null && loginHint != null) {
|
||||||
|
clientSessionCode.getClientSession().setClientNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, loginHint);
|
||||||
|
}
|
||||||
|
|
||||||
IdentityProviderFactory providerFactory = getIdentityProviderFactory(session, identityProviderModel);
|
IdentityProviderFactory providerFactory = getIdentityProviderFactory(session, identityProviderModel);
|
||||||
|
|
||||||
IdentityProvider identityProvider = providerFactory.create(session, identityProviderModel);
|
IdentityProvider identityProvider = providerFactory.create(session, identityProviderModel);
|
||||||
|
|
|
@ -36,11 +36,11 @@ import static org.junit.Assert.assertThat;
|
||||||
*/
|
*/
|
||||||
public class SAMLDataMarshallerTest {
|
public class SAMLDataMarshallerTest {
|
||||||
|
|
||||||
private static final String TEST_RESPONSE = "<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_4804cf50-cd96-4b92-823e-89adaa0c78ba\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.920Z\" Destination=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\" InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></samlp:Status><saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">manager</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>";
|
private static final String TEST_RESPONSE = "<samlp:Response xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_4804cf50-cd96-4b92-823e-89adaa0c78ba\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.920Z\" Destination=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\" InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\"><saml:Issuer>http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></samlp:Status><saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer>http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">manager</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion></samlp:Response>";
|
||||||
|
|
||||||
private static final String TEST_ASSERTION = "<saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">manager</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion>";
|
private static final String TEST_ASSERTION = "<saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer>http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">manager</saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion>";
|
||||||
|
|
||||||
private static final String TEST_ASSERTION_WITH_NAME_ID = "<saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\">http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue><saml:NameID xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\">b2c6275838784dba219c92f53ea5493c8ef4da09</saml:NameID></saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion>";
|
private static final String TEST_ASSERTION_WITH_NAME_ID = "<saml:Assertion xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"ID_29b196c2-d641-45c8-a423-8ed8e54d4cf9\" Version=\"2.0\" IssueInstant=\"2015-11-06T11:00:33.911Z\"><saml:Issuer>http://localhost:8082/auth/realms/realm-with-saml-idp-basic</saml:Issuer><saml:Subject><saml:NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\">test-user</saml:NameID><saml:SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><saml:SubjectConfirmationData InResponseTo=\"ID_c6b90123-f0bb-4c5c-bf9d-388d5bbe467a\" NotOnOrAfter=\"2015-11-06T11:05:31.911Z\" Recipient=\"http://localhost:8081/auth/realms/realm-with-broker/broker/kc-saml-idp-basic/endpoint\"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore=\"2015-11-06T11:00:31.911Z\" NotOnOrAfter=\"2015-11-06T11:01:31.911Z\"><saml:AudienceRestriction><saml:Audience>http://localhost:8081/auth/realms/realm-with-broker</saml:Audience></saml:AudienceRestriction></saml:Conditions><saml:AuthnStatement AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement><saml:AttributeStatement><saml:Attribute Name=\"mobile\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">617-666-7777</saml:AttributeValue></saml:Attribute><saml:Attribute Name=\"urn:oid:1.2.840.113549.1.9.1\" FriendlyName=\"email\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xsi:type=\"xs:string\">test-user@localhost</saml:AttributeValue></saml:Attribute></saml:AttributeStatement><saml:AttributeStatement><saml:Attribute Name=\"Role\" NameFormat=\"urn:oasis:names:tc:SAML:2.0:attrname-format:basic\"><saml:AttributeValue><saml:NameID Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\">b2c6275838784dba219c92f53ea5493c8ef4da09</saml:NameID></saml:AttributeValue></saml:Attribute></saml:AttributeStatement></saml:Assertion>";
|
||||||
|
|
||||||
private static final String TEST_AUTHN_TYPE = "<saml:AuthnStatement xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement>";
|
private static final String TEST_AUTHN_TYPE = "<saml:AuthnStatement xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" AuthnInstant=\"2015-11-06T11:00:33.923Z\" SessionIndex=\"fa0f4fd3-8a11-44f4-9acb-ee30c5bb8fe5\"><saml:AuthnContext><saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml:AuthnContextClassRef></saml:AuthnContext></saml:AuthnStatement>";
|
||||||
|
|
||||||
|
|
|
@ -888,7 +888,8 @@ public class IdentityProviderTest extends AbstractAdminTest {
|
||||||
"wantAuthnRequestsSigned",
|
"wantAuthnRequestsSigned",
|
||||||
"nameIDPolicyFormat",
|
"nameIDPolicyFormat",
|
||||||
"signingCertificate",
|
"signingCertificate",
|
||||||
"addExtensionsElementWithKeyInfo"
|
"addExtensionsElementWithKeyInfo",
|
||||||
|
"loginHint"
|
||||||
));
|
));
|
||||||
assertThat(config, hasEntry("validateSignature", "true"));
|
assertThat(config, hasEntry("validateSignature", "true"));
|
||||||
assertThat(config, hasEntry("singleLogoutServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml"));
|
assertThat(config, hasEntry("singleLogoutServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml"));
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
package org.keycloak.testsuite.broker;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.openqa.selenium.JavascriptExecutor;
|
||||||
|
import org.openqa.selenium.WebElement;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
|
||||||
|
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test of various scenarios related to the use of login hint
|
||||||
|
*/
|
||||||
|
public abstract class AbstractSamlLoginHintTest extends AbstractInitializedBaseBrokerTest {
|
||||||
|
|
||||||
|
// KEYCLOAK-13950
|
||||||
|
@Test
|
||||||
|
public void testPassLoginHintShouldSendSubjectAndPrefillUsername() {
|
||||||
|
String username = "all-info-set@localhost.com";
|
||||||
|
createUser(bc.providerRealmName(), username, "password");
|
||||||
|
|
||||||
|
driver.navigate().to(getAccountUrl(getConsumerRoot(), bc.consumerRealmName()));
|
||||||
|
log.debug("Clicking social " + bc.getIDPAlias());
|
||||||
|
addLoginHintOnSocialButton(username);
|
||||||
|
loginPage.clickSocial(bc.getIDPAlias());
|
||||||
|
waitForPage(driver, "log in to", true);
|
||||||
|
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||||
|
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||||
|
log.debug("Logging in");
|
||||||
|
|
||||||
|
if (isLoginHintOptionEnabled()) {
|
||||||
|
assertEquals("Username input should contain the SAML subject", loginPage.getUsername(), username);
|
||||||
|
} else {
|
||||||
|
assertEquals("Username input should the SAML subject", loginPage.getUsername(), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KEYCLOAK-13950
|
||||||
|
@Test
|
||||||
|
public void testPassEmptyLoginHintShouldNotSendSubjectAndShouldNotPrefillUsername() {
|
||||||
|
String username = "all-info-set@localhost.com";
|
||||||
|
createUser(bc.providerRealmName(), username, "password", "FirstName");
|
||||||
|
|
||||||
|
driver.navigate().to(getAccountUrl(getConsumerRoot(), bc.consumerRealmName()));
|
||||||
|
log.debug("Clicking social " + bc.getIDPAlias());
|
||||||
|
addLoginHintOnSocialButton("");
|
||||||
|
loginPage.clickSocial(bc.getIDPAlias());
|
||||||
|
waitForPage(driver, "log in to", true);
|
||||||
|
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||||
|
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||||
|
log.debug("Logging in");
|
||||||
|
|
||||||
|
assertEquals("Username input should not contain any username", loginPage.getUsername(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract boolean isLoginHintOptionEnabled();
|
||||||
|
|
||||||
|
protected void addLoginHintOnSocialButton(String hint) {
|
||||||
|
JavascriptExecutor executor = (JavascriptExecutor) driver;
|
||||||
|
WebElement button = loginPage.findSocialButton(bc.getIDPAlias());
|
||||||
|
String url = button.getAttribute("href") + "&"+ OIDCLoginProtocol.LOGIN_HINT_PARAM+"="+hint;
|
||||||
|
executor.executeScript("document.getElementById('"+button.getAttribute("id")+"').setAttribute('href', '"+url+"')");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BrokerConfiguration getBrokerConfiguration() {
|
||||||
|
return new KcSamlBrokerConfiguration(isLoginHintOptionEnabled());
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.admin.client.resource.UsersResource;
|
import org.keycloak.admin.client.resource.UsersResource;
|
||||||
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.IdentityProviderSyncMode;
|
import org.keycloak.models.IdentityProviderSyncMode;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
@ -36,7 +37,7 @@ public class KcOidcBrokerLoginHintTest extends AbstractBrokerTest {
|
||||||
|
|
||||||
Map<String, String> config = idp.getConfig();
|
Map<String, String> config = idp.getConfig();
|
||||||
applyDefaultConfiguration(config, syncMode);
|
applyDefaultConfiguration(config, syncMode);
|
||||||
config.put("loginHint", "true");
|
config.put(IdentityProviderModel.LOGIN_HINT, "true");
|
||||||
return idp;
|
return idp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||||
import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
|
import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.keycloak.admin.client.resource.UsersResource;
|
import org.keycloak.admin.client.resource.UsersResource;
|
||||||
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.IdentityProviderSyncMode;
|
import org.keycloak.models.IdentityProviderSyncMode;
|
||||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
|
@ -31,7 +32,7 @@ public class KcOidcBrokerNoLoginHintTest extends AbstractBrokerTest {
|
||||||
|
|
||||||
Map<String, String> config = idp.getConfig();
|
Map<String, String> config = idp.getConfig();
|
||||||
applyDefaultConfiguration(config, syncMode);
|
applyDefaultConfiguration(config, syncMode);
|
||||||
config.put("loginHint", "false");
|
config.put(IdentityProviderModel.LOGIN_HINT, "false");
|
||||||
return idp;
|
return idp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,16 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
|
||||||
public static final KcSamlBrokerConfiguration INSTANCE = new KcSamlBrokerConfiguration();
|
public static final KcSamlBrokerConfiguration INSTANCE = new KcSamlBrokerConfiguration();
|
||||||
public static final String ATTRIBUTE_TO_MAP_FRIENDLY_NAME = "user-attribute-friendly";
|
public static final String ATTRIBUTE_TO_MAP_FRIENDLY_NAME = "user-attribute-friendly";
|
||||||
|
|
||||||
|
private final boolean loginHint;
|
||||||
|
|
||||||
|
public KcSamlBrokerConfiguration() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KcSamlBrokerConfiguration(boolean loginHint) {
|
||||||
|
this.loginHint = loginHint;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RealmRepresentation createProviderRealm() {
|
public RealmRepresentation createProviderRealm() {
|
||||||
RealmRepresentation realm = new RealmRepresentation();
|
RealmRepresentation realm = new RealmRepresentation();
|
||||||
|
@ -87,6 +97,7 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
|
||||||
attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "false");
|
attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "false");
|
||||||
attributes.put(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "false");
|
attributes.put(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "false");
|
||||||
attributes.put(SamlConfigAttributes.SAML_ENCRYPT, "false");
|
attributes.put(SamlConfigAttributes.SAML_ENCRYPT, "false");
|
||||||
|
attributes.put(IdentityProviderModel.LOGIN_HINT, String.valueOf(loginHint));
|
||||||
|
|
||||||
client.setAttributes(attributes);
|
client.setAttributes(attributes);
|
||||||
|
|
||||||
|
@ -211,6 +222,7 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
|
||||||
config.put(SINGLE_LOGOUT_SERVICE_URL, getProviderRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
|
config.put(SINGLE_LOGOUT_SERVICE_URL, getProviderRoot() + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
|
||||||
config.put(NAME_ID_POLICY_FORMAT, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
|
config.put(NAME_ID_POLICY_FORMAT, "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
|
||||||
config.put(FORCE_AUTHN, "false");
|
config.put(FORCE_AUTHN, "false");
|
||||||
|
config.put(IdentityProviderModel.LOGIN_HINT, String.valueOf(loginHint));
|
||||||
config.put(POST_BINDING_RESPONSE, "true");
|
config.put(POST_BINDING_RESPONSE, "true");
|
||||||
config.put(POST_BINDING_AUTHN_REQUEST, "true");
|
config.put(POST_BINDING_AUTHN_REQUEST, "true");
|
||||||
config.put(VALIDATE_SIGNATURE, "false");
|
config.put(VALIDATE_SIGNATURE, "false");
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.keycloak.testsuite.broker;
|
||||||
|
|
||||||
|
public class KcSamlBrokerLoginHintWithOptionDisabledTest extends AbstractSamlLoginHintTest {
|
||||||
|
@Override
|
||||||
|
boolean isLoginHintOptionEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.keycloak.testsuite.broker;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.keycloak.testsuite.Assert;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
|
||||||
|
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
|
||||||
|
|
||||||
|
public class KcSamlBrokerLoginHintWithOptionEnabledTest extends AbstractSamlLoginHintTest {
|
||||||
|
|
||||||
|
|
||||||
|
// KEYCLOAK-13950
|
||||||
|
@Test
|
||||||
|
public void testPassLoginHintWithXmlCharShouldEncodeIt() {
|
||||||
|
String username = "all-info-set@localhost.com";
|
||||||
|
createUser(bc.providerRealmName(), username, "password", "FirstName");
|
||||||
|
|
||||||
|
driver.navigate().to(getAccountUrl(getConsumerRoot(), bc.consumerRealmName()));
|
||||||
|
log.debug("Clicking social " + bc.getIDPAlias());
|
||||||
|
String fishyLoginHint = "<an-xml-tag>";
|
||||||
|
addLoginHintOnSocialButton(fishyLoginHint);
|
||||||
|
loginPage.clickSocial(bc.getIDPAlias());
|
||||||
|
waitForPage(driver, "log in to", true);
|
||||||
|
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||||
|
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||||
|
log.debug("Logging in");
|
||||||
|
|
||||||
|
assertEquals("Username input should contain the SAML subject", loginPage.getUsername(), fishyLoginHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isLoginHintOptionEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,6 @@ public class KcSamlFirstBrokerLoginTest extends AbstractFirstBrokerLoginTest {
|
||||||
return KcSamlBrokerConfiguration.INSTANCE;
|
return KcSamlBrokerConfiguration.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Override
|
@Override
|
||||||
public void testUpdateProfileIfNotMissingInformation() {
|
public void testUpdateProfileIfNotMissingInformation() {
|
||||||
|
|
|
@ -260,6 +260,13 @@
|
||||||
</div>
|
</div>
|
||||||
<kc-tooltip>{{:: 'validating-x509-certificate.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'validating-x509-certificate.tooltip' | translate}}</kc-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-sm-2 control-label" for="loginHint">{{:: 'saml.loginHint' | translate}}</label>
|
||||||
|
<div class="col-sm-4">
|
||||||
|
<input ng-model="identityProvider.config.loginHint" id="loginHint" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
|
||||||
|
</div>
|
||||||
|
<kc-tooltip>{{:: 'saml.loginHint.tooltip' | translate}}</kc-tooltip>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="allowedClockSkew">{{:: 'allowed-clock-skew' | translate}}</label>
|
<label class="col-md-2 control-label" for="allowedClockSkew">{{:: 'allowed-clock-skew' | translate}}</label>
|
||||||
<div class="col-md-6 time-selector">
|
<div class="col-md-6 time-selector">
|
||||||
|
|
Loading…
Reference in a new issue