Merge pull request #4261 from hmlnarik/KEYCLOAK-4377-null

KEYCLOAK-4377
This commit is contained in:
Stian Thorgersen 2017-06-28 08:21:20 +02:00 committed by GitHub
commit ce4506f367
12 changed files with 233 additions and 82 deletions

View file

@ -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());
}

View file

@ -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());

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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);
};
}
}

View file

@ -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;

View file

@ -15,7 +15,7 @@
* limitations under the License.
*/
package org.keycloak.testsuite.util;
package org.keycloak.testsuite.updaters;
import java.io.Closeable;

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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();
}
}
}