Merge pull request #4261 from hmlnarik/KEYCLOAK-4377-null
KEYCLOAK-4377
This commit is contained in:
commit
ce4506f367
12 changed files with 233 additions and 82 deletions
|
@ -54,7 +54,6 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
|||
import org.keycloak.saml.common.exceptions.ConfigurationException;
|
||||
import org.keycloak.saml.common.exceptions.ProcessingException;
|
||||
import org.keycloak.saml.common.util.DocumentUtil;
|
||||
import org.keycloak.saml.processing.api.saml.v2.sig.SAML2Signature;
|
||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
|
||||
import org.keycloak.saml.processing.core.saml.v2.util.AssertionUtil;
|
||||
|
@ -88,10 +87,13 @@ import java.util.List;
|
|||
import org.keycloak.rotation.HardcodedKeyLocator;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
import org.w3c.dom.Document;
|
||||
import org.keycloak.saml.processing.core.util.XMLEncryptionUtil;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.*;
|
||||
import javax.xml.crypto.dsig.XMLSignature;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -517,6 +519,17 @@ public class SAMLEndpoint {
|
|||
protected class PostBinding extends Binding {
|
||||
@Override
|
||||
protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
|
||||
NodeList nl = documentHolder.getSamlDocument().getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
|
||||
boolean anyElementSigned = (nl != null && nl.getLength() > 0);
|
||||
if ((! anyElementSigned) && (documentHolder.getSamlObject() instanceof ResponseType)) {
|
||||
ResponseType responseType = (ResponseType) documentHolder.getSamlObject();
|
||||
List<ResponseType.RTChoiceType> assertions = responseType.getAssertions();
|
||||
if (! assertions.isEmpty() ) {
|
||||
// Only relax verification if the response is an authnresponse and contains (encrypted/plaintext) assertion.
|
||||
// In that case, signature is validated on assertion element
|
||||
return;
|
||||
}
|
||||
}
|
||||
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKeyLocator());
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,24 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||
|
||||
public static final XmlKeyInfoKeyNameTransformer DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER = XmlKeyInfoKeyNameTransformer.NONE;
|
||||
|
||||
public static final String ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO = "addExtensionsElementWithKeyInfo";
|
||||
public static final String BACKCHANNEL_SUPPORTED = "backchannelSupported";
|
||||
public static final String ENCRYPTION_PUBLIC_KEY = "encryptionPublicKey";
|
||||
public static final String FORCE_AUTHN = "forceAuthn";
|
||||
public static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat";
|
||||
public static final String POST_BINDING_AUTHN_REQUEST = "postBindingAuthnRequest";
|
||||
public static final String POST_BINDING_LOGOUT = "postBindingLogout";
|
||||
public static final String POST_BINDING_RESPONSE = "postBindingResponse";
|
||||
public static final String SIGNATURE_ALGORITHM = "signatureAlgorithm";
|
||||
public static final String SIGNING_CERTIFICATE_KEY = "signingCertificate";
|
||||
public static final String SINGLE_LOGOUT_SERVICE_URL = "singleLogoutServiceUrl";
|
||||
public static final String SINGLE_SIGN_ON_SERVICE_URL = "singleSignOnServiceUrl";
|
||||
public static final String VALIDATE_SIGNATURE = "validateSignature";
|
||||
public static final String WANT_ASSERTIONS_ENCRYPTED = "wantAssertionsEncrypted";
|
||||
public static final String WANT_ASSERTIONS_SIGNED = "wantAssertionsSigned";
|
||||
public static final String WANT_AUTHN_REQUESTS_SIGNED = "wantAuthnRequestsSigned";
|
||||
public static final String XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER = "xmlSigKeyInfoKeyNameTransformer";
|
||||
|
||||
public SAMLIdentityProviderConfig() {
|
||||
}
|
||||
|
||||
|
@ -35,35 +53,35 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||
}
|
||||
|
||||
public String getSingleSignOnServiceUrl() {
|
||||
return getConfig().get("singleSignOnServiceUrl");
|
||||
return getConfig().get(SINGLE_SIGN_ON_SERVICE_URL);
|
||||
}
|
||||
|
||||
public void setSingleSignOnServiceUrl(String singleSignOnServiceUrl) {
|
||||
getConfig().put("singleSignOnServiceUrl", singleSignOnServiceUrl);
|
||||
getConfig().put(SINGLE_SIGN_ON_SERVICE_URL, singleSignOnServiceUrl);
|
||||
}
|
||||
|
||||
public String getSingleLogoutServiceUrl() {
|
||||
return getConfig().get("singleLogoutServiceUrl");
|
||||
return getConfig().get(SINGLE_LOGOUT_SERVICE_URL);
|
||||
}
|
||||
|
||||
public void setSingleLogoutServiceUrl(String singleLogoutServiceUrl) {
|
||||
getConfig().put("singleLogoutServiceUrl", singleLogoutServiceUrl);
|
||||
getConfig().put(SINGLE_LOGOUT_SERVICE_URL, singleLogoutServiceUrl);
|
||||
}
|
||||
|
||||
public boolean isValidateSignature() {
|
||||
return Boolean.valueOf(getConfig().get("validateSignature"));
|
||||
return Boolean.valueOf(getConfig().get(VALIDATE_SIGNATURE));
|
||||
}
|
||||
|
||||
public void setValidateSignature(boolean validateSignature) {
|
||||
getConfig().put("validateSignature", String.valueOf(validateSignature));
|
||||
getConfig().put(VALIDATE_SIGNATURE, String.valueOf(validateSignature));
|
||||
}
|
||||
|
||||
public boolean isForceAuthn() {
|
||||
return Boolean.valueOf(getConfig().get("forceAuthn"));
|
||||
return Boolean.valueOf(getConfig().get(FORCE_AUTHN));
|
||||
}
|
||||
|
||||
public void setForceAuthn(boolean forceAuthn) {
|
||||
getConfig().put("forceAuthn", String.valueOf(forceAuthn));
|
||||
getConfig().put(FORCE_AUTHN, String.valueOf(forceAuthn));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,82 +121,80 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||
return crt.split(",");
|
||||
}
|
||||
|
||||
public static final String SIGNING_CERTIFICATE_KEY = "signingCertificate";
|
||||
|
||||
public String getNameIDPolicyFormat() {
|
||||
return getConfig().get("nameIDPolicyFormat");
|
||||
return getConfig().get(NAME_ID_POLICY_FORMAT);
|
||||
}
|
||||
|
||||
public void setNameIDPolicyFormat(String nameIDPolicyFormat) {
|
||||
getConfig().put("nameIDPolicyFormat", nameIDPolicyFormat);
|
||||
getConfig().put(NAME_ID_POLICY_FORMAT, nameIDPolicyFormat);
|
||||
}
|
||||
|
||||
public boolean isWantAuthnRequestsSigned() {
|
||||
return Boolean.valueOf(getConfig().get("wantAuthnRequestsSigned"));
|
||||
return Boolean.valueOf(getConfig().get(WANT_AUTHN_REQUESTS_SIGNED));
|
||||
}
|
||||
|
||||
public void setWantAuthnRequestsSigned(boolean wantAuthnRequestsSigned) {
|
||||
getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
|
||||
getConfig().put(WANT_AUTHN_REQUESTS_SIGNED, String.valueOf(wantAuthnRequestsSigned));
|
||||
}
|
||||
|
||||
public boolean isWantAssertionsSigned() {
|
||||
return Boolean.valueOf(getConfig().get("wantAssertionsSigned"));
|
||||
return Boolean.valueOf(getConfig().get(WANT_ASSERTIONS_SIGNED));
|
||||
}
|
||||
|
||||
public void setWantAssertionsSigned(boolean wantAssertionsSigned) {
|
||||
getConfig().put("wantAssertionsSigned", String.valueOf(wantAssertionsSigned));
|
||||
getConfig().put(WANT_ASSERTIONS_SIGNED, String.valueOf(wantAssertionsSigned));
|
||||
}
|
||||
|
||||
public boolean isWantAssertionsEncrypted() {
|
||||
return Boolean.valueOf(getConfig().get("wantAssertionsEncrypted"));
|
||||
return Boolean.valueOf(getConfig().get(WANT_ASSERTIONS_ENCRYPTED));
|
||||
}
|
||||
|
||||
public void setWantAssertionsEncrypted(boolean wantAssertionsEncrypted) {
|
||||
getConfig().put("wantAssertionsEncrypted", String.valueOf(wantAssertionsEncrypted));
|
||||
getConfig().put(WANT_ASSERTIONS_ENCRYPTED, String.valueOf(wantAssertionsEncrypted));
|
||||
}
|
||||
|
||||
public boolean isAddExtensionsElementWithKeyInfo() {
|
||||
return Boolean.valueOf(getConfig().get("addExtensionsElementWithKeyInfo"));
|
||||
return Boolean.valueOf(getConfig().get(ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO));
|
||||
}
|
||||
|
||||
public void setAddExtensionsElementWithKeyInfo(boolean addExtensionsElementWithKeyInfo) {
|
||||
getConfig().put("addExtensionsElementWithKeyInfo", String.valueOf(addExtensionsElementWithKeyInfo));
|
||||
getConfig().put(ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO, String.valueOf(addExtensionsElementWithKeyInfo));
|
||||
}
|
||||
|
||||
public String getSignatureAlgorithm() {
|
||||
return getConfig().get("signatureAlgorithm");
|
||||
return getConfig().get(SIGNATURE_ALGORITHM);
|
||||
}
|
||||
|
||||
public void setSignatureAlgorithm(String signatureAlgorithm) {
|
||||
getConfig().put("signatureAlgorithm", signatureAlgorithm);
|
||||
getConfig().put(SIGNATURE_ALGORITHM, signatureAlgorithm);
|
||||
}
|
||||
|
||||
public String getEncryptionPublicKey() {
|
||||
return getConfig().get("encryptionPublicKey");
|
||||
return getConfig().get(ENCRYPTION_PUBLIC_KEY);
|
||||
}
|
||||
|
||||
public void setEncryptionPublicKey(String encryptionPublicKey) {
|
||||
getConfig().put("encryptionPublicKey", encryptionPublicKey);
|
||||
getConfig().put(ENCRYPTION_PUBLIC_KEY, encryptionPublicKey);
|
||||
}
|
||||
|
||||
public boolean isPostBindingAuthnRequest() {
|
||||
return Boolean.valueOf(getConfig().get("postBindingAuthnRequest"));
|
||||
return Boolean.valueOf(getConfig().get(POST_BINDING_AUTHN_REQUEST));
|
||||
}
|
||||
|
||||
public void setPostBindingAuthnRequest(boolean postBindingAuthnRequest) {
|
||||
getConfig().put("postBindingAuthnRequest", String.valueOf(postBindingAuthnRequest));
|
||||
getConfig().put(POST_BINDING_AUTHN_REQUEST, String.valueOf(postBindingAuthnRequest));
|
||||
}
|
||||
|
||||
public boolean isPostBindingResponse() {
|
||||
return Boolean.valueOf(getConfig().get("postBindingResponse"));
|
||||
return Boolean.valueOf(getConfig().get(POST_BINDING_RESPONSE));
|
||||
}
|
||||
|
||||
public void setPostBindingResponse(boolean postBindingResponse) {
|
||||
getConfig().put("postBindingResponse", String.valueOf(postBindingResponse));
|
||||
getConfig().put(POST_BINDING_RESPONSE, String.valueOf(postBindingResponse));
|
||||
}
|
||||
|
||||
public boolean isPostBindingLogout() {
|
||||
String postBindingLogout = getConfig().get("postBindingLogout");
|
||||
String postBindingLogout = getConfig().get(POST_BINDING_LOGOUT);
|
||||
if (postBindingLogout == null) {
|
||||
// To maintain unchanged behavior when adding this field, we set the inital value to equal that
|
||||
// of the binding for the response:
|
||||
|
@ -188,15 +204,15 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||
}
|
||||
|
||||
public void setPostBindingLogout(boolean postBindingLogout) {
|
||||
getConfig().put("postBindingLogout", String.valueOf(postBindingLogout));
|
||||
getConfig().put(POST_BINDING_LOGOUT, String.valueOf(postBindingLogout));
|
||||
}
|
||||
|
||||
public boolean isBackchannelSupported() {
|
||||
return Boolean.valueOf(getConfig().get("backchannelSupported"));
|
||||
return Boolean.valueOf(getConfig().get(BACKCHANNEL_SUPPORTED));
|
||||
}
|
||||
|
||||
public void setBackchannelSupported(boolean backchannel) {
|
||||
getConfig().put("backchannelSupported", String.valueOf(backchannel));
|
||||
getConfig().put(BACKCHANNEL_SUPPORTED, String.valueOf(backchannel));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -204,11 +220,11 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
|||
* @return Configured ransformer of {@link #DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER} if not set.
|
||||
*/
|
||||
public XmlKeyInfoKeyNameTransformer getXmlSigKeyInfoKeyNameTransformer() {
|
||||
return XmlKeyInfoKeyNameTransformer.from(getConfig().get("xmlSigKeyInfoKeyNameTransformer"), DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER);
|
||||
return XmlKeyInfoKeyNameTransformer.from(getConfig().get(XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER), DEFAULT_XML_KEY_INFO_KEY_NAME_TRANSFORMER);
|
||||
}
|
||||
|
||||
public void setXmlSigKeyInfoKeyNameTransformer(XmlKeyInfoKeyNameTransformer xmlSigKeyInfoKeyNameTransformer) {
|
||||
getConfig().put("xmlSigKeyInfoKeyNameTransformer",
|
||||
getConfig().put(XML_SIG_KEY_INFO_KEY_NAME_TRANSFORMER,
|
||||
xmlSigKeyInfoKeyNameTransformer == null
|
||||
? null
|
||||
: xmlSigKeyInfoKeyNameTransformer.name());
|
||||
|
|
|
@ -193,7 +193,7 @@ public class SamlProtocol implements LoginProtocol {
|
|||
if (samlClient.requiresEncryption()) {
|
||||
PublicKey publicKey;
|
||||
try {
|
||||
publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
|
||||
publicKey = SamlProtocolUtils.getEncryptionKey(client);
|
||||
} catch (Exception e) {
|
||||
logger.error("failed", e);
|
||||
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
|
||||
|
@ -457,7 +457,7 @@ public class SamlProtocol implements LoginProtocol {
|
|||
if (samlClient.requiresEncryption()) {
|
||||
PublicKey publicKey = null;
|
||||
try {
|
||||
publicKey = SamlProtocolUtils.getEncryptionValidationKey(client);
|
||||
publicKey = SamlProtocolUtils.getEncryptionKey(client);
|
||||
} catch (Exception e) {
|
||||
logger.error("failed", e);
|
||||
return ErrorPage.error(session, Messages.FAILED_TO_PROCESS_RESPONSE);
|
||||
|
|
|
@ -103,7 +103,7 @@ public class SamlProtocolUtils {
|
|||
* @return Public key for encryption.
|
||||
* @throws VerificationException
|
||||
*/
|
||||
public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException {
|
||||
public static PublicKey getEncryptionKey(ClientModel client) throws VerificationException {
|
||||
return getPublicKey(client, SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.testsuite.util;
|
||||
package org.keycloak.testsuite.updaters;
|
||||
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
|
@ -0,0 +1,55 @@
|
|||
package org.keycloak.testsuite.updaters;
|
||||
|
||||
import org.keycloak.admin.client.resource.IdentityProviderResource;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import java.io.Closeable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class IdentityProviderAttributeUpdater {
|
||||
|
||||
private final Map<String, String> originalAttributes = new HashMap<>();
|
||||
|
||||
private final IdentityProviderResource identityProviderResource;
|
||||
|
||||
private final IdentityProviderRepresentation rep;
|
||||
|
||||
public IdentityProviderAttributeUpdater(IdentityProviderResource identityProviderResource) {
|
||||
this.identityProviderResource = identityProviderResource;
|
||||
this.rep = identityProviderResource.toRepresentation();
|
||||
if (this.rep.getConfig() == null) {
|
||||
this.rep.setConfig(new HashMap<>());
|
||||
}
|
||||
}
|
||||
|
||||
public IdentityProviderAttributeUpdater setAttribute(String name, String value) {
|
||||
if (! originalAttributes.containsKey(name)) {
|
||||
this.originalAttributes.put(name, this.rep.getConfig().put(name, value));
|
||||
} else {
|
||||
this.rep.getConfig().put(name, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public IdentityProviderAttributeUpdater removeAttribute(String name) {
|
||||
if (! originalAttributes.containsKey(name)) {
|
||||
this.originalAttributes.put(name, this.rep.getConfig().put(name, null));
|
||||
} else {
|
||||
this.rep.getConfig().put(name, null);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Closeable update() {
|
||||
identityProviderResource.update(rep);
|
||||
|
||||
return () -> {
|
||||
rep.getConfig().putAll(originalAttributes);
|
||||
identityProviderResource.update(rep);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.testsuite.util;
|
||||
package org.keycloak.testsuite.updaters;
|
||||
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.testsuite.util;
|
||||
package org.keycloak.testsuite.updaters;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.testsuite.adapter.servlet;
|
||||
|
||||
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -14,7 +13,6 @@ import org.keycloak.representations.idm.RealmRepresentation;
|
|||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.Assert;
|
||||
import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.pages.ConsentPage;
|
||||
import org.keycloak.testsuite.util.*;
|
||||
|
||||
|
@ -40,6 +38,8 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.*;
|
|||
|
||||
public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
||||
|
||||
protected IdentityProviderResource identityProviderResource;
|
||||
|
||||
@Before
|
||||
public void beforeBrokerTest() {
|
||||
log.debug("creating user for realm " + bc.providerRealmName());
|
||||
|
@ -61,7 +61,8 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
|||
|
||||
log.debug("adding identity provider to realm " + bc.consumerRealmName());
|
||||
RealmResource realm = adminClient.realm(bc.consumerRealmName());
|
||||
realm.identityProviders().create(bc.setUpIdentityProvider(suiteContext));
|
||||
realm.identityProviders().create(bc.setUpIdentityProvider(suiteContext)).close();
|
||||
identityProviderResource = realm.identityProviders().get(bc.getIDPAlias());
|
||||
|
||||
// addClients
|
||||
List<ClientRepresentation> clients = bc.createProviderClients(suiteContext);
|
||||
|
@ -70,7 +71,7 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
|||
for (ClientRepresentation client : clients) {
|
||||
log.debug("adding client " + client.getName() + " to realm " + bc.providerRealmName());
|
||||
|
||||
providerRealm.clients().create(client);
|
||||
providerRealm.clients().create(client).close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +81,7 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
|||
for (ClientRepresentation client : clients) {
|
||||
log.debug("adding client " + client.getName() + " to realm " + bc.consumerRealmName());
|
||||
|
||||
consumerRealm.clients().create(client);
|
||||
consumerRealm.clients().create(client).close();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,6 +91,12 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
|||
|
||||
@Test
|
||||
public void testLogInAsUserInIDP() {
|
||||
loginUser();
|
||||
|
||||
testSingleLogout();
|
||||
}
|
||||
|
||||
protected void loginUser() {
|
||||
driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
|
||||
|
||||
log.debug("Clicking social " + bc.getIDPAlias());
|
||||
|
@ -98,16 +105,16 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
|||
waitForPage(driver, "log in to");
|
||||
|
||||
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
|
||||
|
||||
log.debug("Logging in");
|
||||
accountLoginPage.login(bc.getUserLogin(), bc.getUserPassword());
|
||||
|
||||
waitForPage(driver, "update account information");
|
||||
|
||||
Assert.assertTrue(updateAccountInformationPage.isCurrent());
|
||||
updateAccountInformationPage.assertCurrent();
|
||||
Assert.assertTrue("We must be on correct realm right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
|
||||
|
||||
log.debug("Updating info on updateAccount page");
|
||||
updateAccountInformationPage.updateAccountInformation(bc.getUserLogin(), bc.getUserEmail(), "Firstname", "Lastname");
|
||||
|
@ -128,9 +135,7 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
|
|||
}
|
||||
|
||||
Assert.assertTrue("There must be user " + bc.getUserLogin() + " in realm " + bc.consumerRealmName(),
|
||||
isUserFound);
|
||||
|
||||
testSingleLogout();
|
||||
isUserFound);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.protocol.ProtocolMapperUtils;
|
||||
import org.keycloak.protocol.saml.SamlConfigAttributes;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
|
||||
import org.keycloak.protocol.saml.mappers.UserAttributeStatementMapper;
|
||||
|
@ -22,6 +23,7 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.keycloak.broker.saml.SAMLIdentityProviderConfig.*;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestConstants.*;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.*;
|
||||
|
||||
|
@ -63,17 +65,17 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
|
|||
|
||||
Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
attributes.put("saml.authnstatement", "true");
|
||||
attributes.put("saml_single_logout_service_url_post",
|
||||
attributes.put(SamlConfigAttributes.SAML_AUTHNSTATEMENT, "true");
|
||||
attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE,
|
||||
getAuthRoot(suiteContext) + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint");
|
||||
attributes.put("saml_assertion_consumer_url_post",
|
||||
attributes.put(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE,
|
||||
getAuthRoot(suiteContext) + "/auth/realms/" + REALM_CONS_NAME + "/broker/" + IDP_SAML_ALIAS + "/endpoint");
|
||||
attributes.put("saml_force_name_id_format", "true");
|
||||
attributes.put("saml_name_id_format", "username");
|
||||
attributes.put("saml.assertion.signature", "false");
|
||||
attributes.put("saml.server.signature", "false");
|
||||
attributes.put("saml.client.signature", "false");
|
||||
attributes.put("saml.encrypt", "false");
|
||||
attributes.put(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, "true");
|
||||
attributes.put(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, "username");
|
||||
attributes.put(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, "false");
|
||||
attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "false");
|
||||
attributes.put(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "false");
|
||||
attributes.put(SamlConfigAttributes.SAML_ENCRYPT, "false");
|
||||
|
||||
client.setAttributes(attributes);
|
||||
|
||||
|
@ -133,15 +135,15 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
|
|||
|
||||
Map<String, String> config = idp.getConfig();
|
||||
|
||||
config.put("singleSignOnServiceUrl", getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
|
||||
config.put("singleLogoutServiceUrl", getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
|
||||
config.put("nameIDPolicyFormat", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
|
||||
config.put("forceAuthn", "true");
|
||||
config.put("postBindingResponse", "true");
|
||||
config.put("postBindingAuthnRequest", "true");
|
||||
config.put("validateSignature", "false");
|
||||
config.put("wantAuthnRequestsSigned", "false");
|
||||
config.put("backchannelSupported", "true");
|
||||
config.put(SINGLE_SIGN_ON_SERVICE_URL, getAuthRoot(suiteContext) + "/auth/realms/" + REALM_PROV_NAME + "/protocol/saml");
|
||||
config.put(SINGLE_LOGOUT_SERVICE_URL, getAuthRoot(suiteContext) + "/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(FORCE_AUTHN, "true");
|
||||
config.put(POST_BINDING_RESPONSE, "true");
|
||||
config.put(POST_BINDING_AUTHN_REQUEST, "true");
|
||||
config.put(VALIDATE_SIGNATURE, "false");
|
||||
config.put(WANT_AUTHN_REQUESTS_SIGNED, "false");
|
||||
config.put(BACKCHANNEL_SUPPORTED, "true");
|
||||
|
||||
return idp;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
package org.keycloak.testsuite.broker;
|
||||
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
import org.keycloak.broker.saml.SAMLIdentityProviderConfig;
|
||||
import org.keycloak.protocol.saml.SamlConfigAttributes;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.arquillian.SuiteContext;
|
||||
|
||||
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||
import org.keycloak.testsuite.updaters.IdentityProviderAttributeUpdater;
|
||||
import java.io.Closeable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestConstants.*;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestTools.encodeUrl;
|
||||
|
||||
public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
|
||||
|
||||
public static class KcSamlSignedBrokerConfiguration extends KcSamlBrokerConfiguration {
|
||||
public class KcSamlSignedBrokerConfiguration extends KcSamlBrokerConfiguration {
|
||||
|
||||
@Override
|
||||
public RealmRepresentation createProviderRealm() {
|
||||
|
@ -39,6 +49,9 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
|
|||
public List<ClientRepresentation> createProviderClients(SuiteContext suiteContext) {
|
||||
List<ClientRepresentation> clientRepresentationList = super.createProviderClients(suiteContext);
|
||||
|
||||
String consumerCert = adminClient.realm(consumerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate();
|
||||
Assert.assertThat(consumerCert, Matchers.notNullValue());
|
||||
|
||||
for (ClientRepresentation client : clientRepresentationList) {
|
||||
client.setClientAuthenticatorType("client-secret");
|
||||
client.setSurrogateAuthRequired(false);
|
||||
|
@ -49,12 +62,11 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
|
|||
client.setAttributes(attributes);
|
||||
}
|
||||
|
||||
attributes.put("saml.assertion.signature", "true");
|
||||
attributes.put("saml.server.signature", "true");
|
||||
attributes.put("saml.client.signature", "true");
|
||||
attributes.put("saml.signature.algorithm", "RSA_SHA256");
|
||||
attributes.put("saml.signing.private.key", IDP_SAML_SIGN_KEY);
|
||||
attributes.put("saml.signing.certificate", IDP_SAML_SIGN_CERT);
|
||||
attributes.put(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, "true");
|
||||
attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "true");
|
||||
attributes.put(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "true");
|
||||
attributes.put(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, "RSA_SHA256");
|
||||
attributes.put(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, consumerCert);
|
||||
}
|
||||
|
||||
return clientRepresentationList;
|
||||
|
@ -64,11 +76,15 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
|
|||
public IdentityProviderRepresentation setUpIdentityProvider(SuiteContext suiteContext) {
|
||||
IdentityProviderRepresentation result = super.setUpIdentityProvider(suiteContext);
|
||||
|
||||
String providerCert = adminClient.realm(providerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate();
|
||||
Assert.assertThat(providerCert, Matchers.notNullValue());
|
||||
|
||||
Map<String, String> config = result.getConfig();
|
||||
|
||||
config.put("validateSignature", "true");
|
||||
config.put("wantAuthnRequestsSigned", "true");
|
||||
config.put("signingCertificate", IDP_SAML_SIGN_CERT);
|
||||
config.put(SAMLIdentityProviderConfig.VALIDATE_SIGNATURE, "true");
|
||||
config.put(SAMLIdentityProviderConfig.WANT_ASSERTIONS_SIGNED, "true");
|
||||
config.put(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED, "true");
|
||||
config.put(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY, providerCert);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -76,7 +92,50 @@ public class KcSamlSignedBrokerTest extends KcSamlBrokerTest {
|
|||
|
||||
@Override
|
||||
protected BrokerConfiguration getBrokerConfiguration() {
|
||||
return KcSamlSignedBrokerConfiguration.INSTANCE;
|
||||
return new KcSamlSignedBrokerConfiguration();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSignedEncryptedAssertions() throws Exception {
|
||||
ClientRepresentation client = adminClient.realm(bc.providerRealmName())
|
||||
.clients()
|
||||
.findByClientId(bc.getIDPClientIdInProviderRealm(suiteContext))
|
||||
.get(0);
|
||||
|
||||
final ClientResource clientResource = realmsResouce().realm(bc.providerRealmName()).clients().get(client.getId());
|
||||
Assert.assertThat(clientResource, Matchers.notNullValue());
|
||||
|
||||
String providerCert = adminClient.realm(bc.providerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate();
|
||||
Assert.assertThat(providerCert, Matchers.notNullValue());
|
||||
|
||||
String consumerCert = adminClient.realm(bc.consumerRealmName()).keys().getKeyMetadata().getKeys().get(0).getCertificate();
|
||||
Assert.assertThat(consumerCert, Matchers.notNullValue());
|
||||
|
||||
try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource)
|
||||
.setAttribute(SAMLIdentityProviderConfig.VALIDATE_SIGNATURE, "true")
|
||||
.setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_SIGNED, "true")
|
||||
.setAttribute(SAMLIdentityProviderConfig.WANT_ASSERTIONS_ENCRYPTED, "true")
|
||||
.setAttribute(SAMLIdentityProviderConfig.WANT_AUTHN_REQUESTS_SIGNED, "false")
|
||||
.setAttribute(SAMLIdentityProviderConfig.SIGNING_CERTIFICATE_KEY, providerCert)
|
||||
.update();
|
||||
Closeable clientUpdater = new ClientAttributeUpdater(clientResource)
|
||||
.setAttribute(SamlConfigAttributes.SAML_ENCRYPT, "true")
|
||||
.setAttribute(SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, consumerCert)
|
||||
.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, "false") // only sign assertions
|
||||
.setAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, "true")
|
||||
.setAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, "false")
|
||||
.update())
|
||||
{
|
||||
// Login should pass because assertion is signed.
|
||||
loginUser();
|
||||
|
||||
// Logout should fail because logout response is not signed.
|
||||
driver.navigate().to(BrokerTestTools.getAuthRoot(suiteContext)
|
||||
+ "/auth/realms/" + bc.providerRealmName()
|
||||
+ "/protocol/" + "openid-connect"
|
||||
+ "/logout?redirect_uri=" + encodeUrl(getAccountUrl(bc.providerRealmName())));
|
||||
|
||||
errorPage.assertCurrent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue