merge
This commit is contained in:
commit
cc0eb47814
692 files changed
+6300
-961
No files matched your search
@@ -63,6 +63,11 @@
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-ldap-federation</artifactId>
|
||||
|
||||
+2
-2
@@ -20,6 +20,7 @@ package org.keycloak.authentication.authenticators.browser;
|
||||
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.utils.RoleUtils;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import java.util.List;
|
||||
@@ -30,7 +31,6 @@ import static org.keycloak.authentication.authenticators.browser.ConditionalOtpF
|
||||
import static org.keycloak.authentication.authenticators.browser.ConditionalOtpFormAuthenticator.OtpDecision.SHOW_OTP;
|
||||
import static org.keycloak.authentication.authenticators.browser.ConditionalOtpFormAuthenticator.OtpDecision.SKIP_OTP;
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.getRoleFromString;
|
||||
import static org.keycloak.models.utils.KeycloakModelUtils.hasRole;
|
||||
|
||||
/**
|
||||
* An {@link OTPFormAuthenticator} that can conditionally require OTP authentication.
|
||||
@@ -264,6 +264,6 @@ public class ConditionalOtpFormAuthenticator extends OTPFormAuthenticator {
|
||||
RoleModel role = getRoleFromString(context.getRealm(), roleName);
|
||||
UserModel user = context.getUser();
|
||||
|
||||
return hasRole(user.getRoleMappings(), role);
|
||||
return RoleUtils.hasRole(user.getRoleMappings(), role);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -142,7 +142,7 @@ public class ScriptBasedAuthenticator implements Authenticator {
|
||||
|
||||
RealmModel realm = context.getRealm();
|
||||
|
||||
ScriptingProvider scripting = context.getSession().scripting();
|
||||
ScriptingProvider scripting = context.getSession().getProvider(ScriptingProvider.class);
|
||||
|
||||
//TODO lookup script by scriptId instead of creating it every time
|
||||
ScriptModel script = scripting.createScript(realm.getId(), ScriptModel.TEXT_JAVASCRIPT, scriptName, scriptCode, scriptDescription);
|
||||
|
||||
@@ -73,9 +73,13 @@ import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.IOException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Key;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import org.keycloak.rotation.HardcodedKeyLocator;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -174,14 +178,20 @@ public class SAMLEndpoint {
|
||||
protected abstract void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException;
|
||||
protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest);
|
||||
protected abstract SAMLDocumentHolder extractResponseDocument(String response);
|
||||
protected PublicKey getIDPKey() {
|
||||
X509Certificate certificate = null;
|
||||
try {
|
||||
certificate = XMLSignatureUtil.getX509CertificateFromKeyInfoString(config.getSigningCertificate().replaceAll("\\s", ""));
|
||||
} catch (ProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
protected KeyLocator getIDPKeyLocator() {
|
||||
List<Key> keys = new LinkedList<>();
|
||||
|
||||
for (String signingCertificate : config.getSigningCertificates()) {
|
||||
try {
|
||||
X509Certificate cert = XMLSignatureUtil.getX509CertificateFromKeyInfoString(signingCertificate.replaceAll("\\s", ""));
|
||||
keys.add(cert.getPublicKey());
|
||||
} catch (ProcessingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return certificate.getPublicKey();
|
||||
|
||||
return new HardcodedKeyLocator(keys);
|
||||
}
|
||||
|
||||
public Response execute(String samlRequest, String samlResponse, String relayState) {
|
||||
@@ -265,14 +275,18 @@ public class SAMLEndpoint {
|
||||
builder.issuer(issuerURL);
|
||||
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
|
||||
.relayState(relayState);
|
||||
boolean postBinding = config.isPostBindingResponse();
|
||||
if (config.isWantAuthnRequestsSigned()) {
|
||||
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
|
||||
binding.signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate())
|
||||
binding.signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate())
|
||||
.signatureAlgorithm(provider.getSignatureAlgorithm())
|
||||
.signDocument();
|
||||
if (! postBinding && config.isAddExtensionsElementWithKeyInfo()) { // Only include extension if REDIRECT binding and signing whole SAML protocol message
|
||||
builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (config.isPostBindingResponse()) {
|
||||
if (postBinding) {
|
||||
return binding.postBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
|
||||
} else {
|
||||
return binding.redirectBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl());
|
||||
@@ -418,7 +432,7 @@ public class SAMLEndpoint {
|
||||
protected class PostBinding extends Binding {
|
||||
@Override
|
||||
protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
|
||||
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKey());
|
||||
SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKeyLocator());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -440,8 +454,8 @@ public class SAMLEndpoint {
|
||||
protected class RedirectBinding extends Binding {
|
||||
@Override
|
||||
protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException {
|
||||
PublicKey publicKey = getIDPKey();
|
||||
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo, key);
|
||||
KeyLocator locator = getIDPKeyLocator();
|
||||
SamlProtocolUtils.verifyRedirectSignature(documentHolder, locator, uriInfo, key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -50,8 +50,11 @@ import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.keys.KeyMetadata;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
|
||||
/**
|
||||
* @author Pedro Igor
|
||||
@@ -97,18 +100,22 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||
.nameIdPolicy(SAML2NameIDPolicyBuilder.format(nameIDPolicyFormat));
|
||||
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder()
|
||||
.relayState(request.getState());
|
||||
boolean postBinding = getConfig().isPostBindingAuthnRequest();
|
||||
|
||||
if (getConfig().isWantAuthnRequestsSigned()) {
|
||||
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
|
||||
|
||||
KeyPair keypair = new KeyPair(keys.getPublicKey(), keys.getPrivateKey());
|
||||
|
||||
binding.signWith(keypair);
|
||||
binding.signWith(keys.getKid(), keypair);
|
||||
binding.signatureAlgorithm(getSignatureAlgorithm());
|
||||
binding.signDocument();
|
||||
if (! postBinding && getConfig().isAddExtensionsElementWithKeyInfo()) { // Only include extension if REDIRECT binding and signing whole SAML protocol message
|
||||
authnRequestBuilder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
|
||||
}
|
||||
}
|
||||
|
||||
if (getConfig().isPostBindingAuthnRequest()) {
|
||||
if (postBinding) {
|
||||
return binding.postBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
|
||||
} else {
|
||||
return binding.redirectBinding(authnRequestBuilder.toDocument()).request(destinationUrl);
|
||||
@@ -198,7 +205,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||
.relayState(userSession.getId());
|
||||
if (getConfig().isWantAuthnRequestsSigned()) {
|
||||
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
|
||||
binding.signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate())
|
||||
binding.signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate())
|
||||
.signatureAlgorithm(getSignatureAlgorithm())
|
||||
.signDocument();
|
||||
}
|
||||
@@ -225,11 +232,27 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
||||
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
|
||||
String entityId = getEntityId(uriInfo, realm);
|
||||
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
|
||||
String certificatePem = PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate());
|
||||
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, entityId, nameIDPolicyFormat, certificatePem);
|
||||
|
||||
StringBuilder keysString = new StringBuilder();
|
||||
Set<KeyMetadata> keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list
|
||||
? (int) (o2.getProviderPriority() - o1.getProviderPriority())
|
||||
: (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1));
|
||||
keys.addAll(session.keys().getKeys(realm, false));
|
||||
for (KeyMetadata key : keys) {
|
||||
addKeyInfo(keysString, key, KeyTypes.SIGNING.value());
|
||||
}
|
||||
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, entityId, nameIDPolicyFormat, keysString.toString());
|
||||
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
|
||||
}
|
||||
|
||||
private static void addKeyInfo(StringBuilder target, KeyMetadata key, String purpose) {
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
target.append(SPMetadataDescriptor.xmlKeyInfo(" ", key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, true));
|
||||
}
|
||||
|
||||
public SignatureAlgorithm getSignatureAlgorithm() {
|
||||
String alg = getConfig().getSignatureAlgorithm();
|
||||
if (alg != null) {
|
||||
|
||||
@@ -62,14 +62,45 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
||||
getConfig().put("forceAuthn", String.valueOf(forceAuthn));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Prefer {@link #getSigningCertificates()}}
|
||||
* @param signingCertificate
|
||||
*/
|
||||
public String getSigningCertificate() {
|
||||
return getConfig().get("signingCertificate");
|
||||
return getConfig().get(SIGNING_CERTIFICATE_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Prefer {@link #addSigningCertificate(String)}}
|
||||
* @param signingCertificate
|
||||
*/
|
||||
public void setSigningCertificate(String signingCertificate) {
|
||||
getConfig().put("signingCertificate", signingCertificate);
|
||||
getConfig().put(SIGNING_CERTIFICATE_KEY, signingCertificate);
|
||||
}
|
||||
|
||||
public void addSigningCertificate(String signingCertificate) {
|
||||
String crt = getConfig().get(SIGNING_CERTIFICATE_KEY);
|
||||
if (crt == null || crt.isEmpty()) {
|
||||
getConfig().put(SIGNING_CERTIFICATE_KEY, signingCertificate);
|
||||
} else {
|
||||
// Note that "," is not coding character per PEM format specification:
|
||||
// see https://tools.ietf.org/html/rfc1421, section 4.3.2.4 Step 4: Printable Encoding
|
||||
getConfig().put(SIGNING_CERTIFICATE_KEY, crt + "," + signingCertificate);
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getSigningCertificates() {
|
||||
String crt = getConfig().get(SIGNING_CERTIFICATE_KEY);
|
||||
if (crt == null || crt.isEmpty()) {
|
||||
return new String[] { };
|
||||
}
|
||||
// Note that "," is not coding character per PEM format specification:
|
||||
// see https://tools.ietf.org/html/rfc1421, section 4.3.2.4 Step 4: Printable Encoding
|
||||
return crt.split(",");
|
||||
}
|
||||
|
||||
public static final String SIGNING_CERTIFICATE_KEY = "signingCertificate";
|
||||
|
||||
public String getNameIDPolicyFormat() {
|
||||
return getConfig().get("nameIDPolicyFormat");
|
||||
}
|
||||
@@ -86,6 +117,14 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
|
||||
getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
|
||||
}
|
||||
|
||||
public boolean isAddExtensionsElementWithKeyInfo() {
|
||||
return Boolean.valueOf(getConfig().get("addExtensionsElementWithKeyInfo"));
|
||||
}
|
||||
|
||||
public void setAddExtensionsElementWithKeyInfo(boolean addExtensionsElementWithKeyInfo) {
|
||||
getConfig().put("addExtensionsElementWithKeyInfo", String.valueOf(addExtensionsElementWithKeyInfo));
|
||||
}
|
||||
|
||||
public String getSignatureAlgorithm() {
|
||||
return getConfig().get("signatureAlgorithm");
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||
samlIdentityProviderConfig.setSingleLogoutServiceUrl(singleLogoutServiceUrl);
|
||||
samlIdentityProviderConfig.setSingleSignOnServiceUrl(singleSignOnServiceUrl);
|
||||
samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned());
|
||||
samlIdentityProviderConfig.setAddExtensionsElementWithKeyInfo(false);
|
||||
samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned());
|
||||
samlIdentityProviderConfig.setPostBindingResponse(postBinding);
|
||||
samlIdentityProviderConfig.setPostBindingAuthnRequest(postBinding);
|
||||
@@ -121,7 +122,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||
Element x509KeyInfo = DocumentUtil.getChildElement(keyInfo, new QName("dsig", "X509Certificate"));
|
||||
|
||||
if (KeyTypes.SIGNING.equals(keyDescriptorType.getUse())) {
|
||||
samlIdentityProviderConfig.setSigningCertificate(x509KeyInfo.getTextContent());
|
||||
samlIdentityProviderConfig.addSigningCertificate(x509KeyInfo.getTextContent());
|
||||
} else if (KeyTypes.ENCRYPTION.equals(keyDescriptorType.getUse())) {
|
||||
samlIdentityProviderConfig.setEncryptionPublicKey(x509KeyInfo.getTextContent());
|
||||
} else if (keyDescriptorType.getUse() == null) {
|
||||
@@ -131,8 +132,8 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
|
||||
}
|
||||
|
||||
if (defaultCertificate != null) {
|
||||
if (samlIdentityProviderConfig.getSigningCertificate() == null) {
|
||||
samlIdentityProviderConfig.setSigningCertificate(defaultCertificate);
|
||||
if (samlIdentityProviderConfig.getSigningCertificates().length == 0) {
|
||||
samlIdentityProviderConfig.addSigningCertificate(defaultCertificate);
|
||||
}
|
||||
|
||||
if (samlIdentityProviderConfig.getEncryptionPublicKey() == null) {
|
||||
|
||||
@@ -140,7 +140,7 @@ public class PasswordCredentialProvider implements CredentialProvider, Credentia
|
||||
PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, policy.getHashAlgorithm());
|
||||
if (hash == null) {
|
||||
logger.warnv("Realm PasswordPolicy PasswordHashProvider {0} not found", policy.getHashAlgorithm());
|
||||
return session.getProvider(PasswordHashProvider.class, HashAlgorithmPasswordPolicyProviderFactory.DEFAULT_VALUE);
|
||||
return session.getProvider(PasswordHashProvider.class, PasswordPolicy.HASH_ALGORITHM_DEFAULT);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,8 @@ public class ImportUtils {
|
||||
}
|
||||
}
|
||||
|
||||
RealmImporter realmManager = session.getContext().getRealmManager();
|
||||
RealmManager realmManager = new RealmManager(session);
|
||||
realmManager.setContextPath(session.getContext().getContextPath());
|
||||
realmManager.importRealm(rep);
|
||||
|
||||
if (System.getProperty(ExportImportConfig.ACTION) != null) {
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.keycloak.models.RealmModel;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -24,11 +24,17 @@ import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
@@ -61,8 +67,18 @@ public class JavaKeystoreKeyProvider extends AbstractRsaKeyProvider {
|
||||
String kid = KeyUtils.createKeyId(keyPair.getPublic());
|
||||
|
||||
return new Keys(kid, keyPair, certificate);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to load keys", e);
|
||||
} catch (KeyStoreException kse) {
|
||||
throw new RuntimeException("KeyStore error on server. " + kse.getMessage(), kse);
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
throw new RuntimeException("File not found on server. " + fnfe.getMessage(), fnfe);
|
||||
} catch (IOException ioe) {
|
||||
throw new RuntimeException("IO error on server. " + ioe.getMessage(), ioe);
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
throw new RuntimeException("Algorithm not available on server. " + nsae.getMessage(), nsae);
|
||||
} catch (CertificateException ce) {
|
||||
throw new RuntimeException("Certificate error on server. " + ce.getMessage(), ce);
|
||||
} catch (UnrecoverableKeyException uke) {
|
||||
throw new RuntimeException("Keystore on server can not be recovered. " + uke.getMessage(), uke);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.keycloak.provider.ConfigurationValidationHelper;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
|
||||
import java.util.List;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE;
|
||||
|
||||
@@ -34,6 +35,7 @@ import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE;
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactory {
|
||||
private static final Logger logger = Logger.getLogger(JavaKeystoreKeyProviderFactory.class);
|
||||
|
||||
public static final String ID = "java-keystore";
|
||||
|
||||
@@ -77,7 +79,8 @@ public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactor
|
||||
new JavaKeystoreKeyProvider(session.getContext().getRealm(), model)
|
||||
.loadKeys(session.getContext().getRealm(), model);
|
||||
} catch (Throwable t) {
|
||||
throw new ComponentValidationException("Failed to load keys", t);
|
||||
logger.error("Failed to load keys.", t);
|
||||
throw new ComponentValidationException("Failed to load keys. " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.models.utils.RepresentationToModel;
|
||||
import org.keycloak.representations.idm.PartialImportRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.services.managers.UserManager;
|
||||
import org.keycloak.models.UserManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@@ -42,8 +42,9 @@ public class RedirectUtils {
|
||||
}
|
||||
|
||||
public static String verifyRedirectUri(UriInfo uriInfo, String redirectUri, RealmModel realm, ClientModel client) {
|
||||
Set<String> validRedirects = client.getRedirectUris();
|
||||
return verifyRedirectUri(uriInfo, client.getRootUrl(), redirectUri, realm, validRedirects);
|
||||
if (client != null)
|
||||
return verifyRedirectUri(uriInfo, client.getRootUrl(), redirectUri, realm, client.getRedirectUris());
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Set<String> resolveValidRedirects(UriInfo uriInfo, String rootUrl, Set<String> validRedirects) {
|
||||
|
||||
+2
-1
@@ -101,6 +101,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
|
||||
app.setFullScopeAllowed(true);
|
||||
app.setProtocol(SamlProtocol.LOGIN_PROTOCOL);
|
||||
attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); // default to true
|
||||
attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT, SamlProtocol.ATTRIBUTE_FALSE_VALUE); // default to false
|
||||
attributes.put(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.toString());
|
||||
attributes.put(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
|
||||
SPSSODescriptorType spDescriptorType = CoreConfigUtil.getSPDescriptor(entity);
|
||||
@@ -110,7 +111,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
|
||||
String logoutPost = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
|
||||
if (logoutPost != null) attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, logoutPost);
|
||||
String logoutRedirect = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get());
|
||||
if (logoutPost != null) attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, logoutRedirect);
|
||||
if (logoutRedirect != null) attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, logoutRedirect);
|
||||
|
||||
String assertionConsumerServicePostBinding = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get());
|
||||
if (assertionConsumerServicePostBinding != null) {
|
||||
|
||||
@@ -23,6 +23,8 @@ import org.keycloak.saml.SignatureAlgorithm;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
|
||||
/**
|
||||
* Configuration of a SAML-enabled client.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@@ -116,7 +118,14 @@ public class SamlClient extends ClientConfigResolver {
|
||||
|
||||
public void setRequiresRealmSignature(boolean val) {
|
||||
client.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val));
|
||||
}
|
||||
|
||||
public boolean addExtensionsElementWithKeyInfo() {
|
||||
return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT));
|
||||
}
|
||||
|
||||
public void setAddExtensionsElementWithKeyInfo(boolean val) {
|
||||
client.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT, Boolean.toString(val));
|
||||
}
|
||||
|
||||
public boolean forcePostBinding() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.keycloak.protocol.saml;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.saml.SignatureAlgorithm;
|
||||
|
||||
@@ -89,7 +90,14 @@ public class SamlClientTemplate {
|
||||
|
||||
public void setRequiresRealmSignature(boolean val) {
|
||||
clientTemplate.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val));
|
||||
}
|
||||
|
||||
public boolean addExtensionsElementWithKeyInfo() {
|
||||
return Objects.equals("true", clientTemplate.getAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT));
|
||||
}
|
||||
|
||||
public void setAddExtensionsElementWithKeyInfo(boolean val) {
|
||||
clientTemplate.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT, Boolean.toString(val));
|
||||
}
|
||||
|
||||
public boolean forcePostBinding() {
|
||||
|
||||
@@ -31,6 +31,7 @@ public interface SamlConfigAttributes {
|
||||
String SAML_AUTHNSTATEMENT = "saml.authnstatement";
|
||||
String SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE = "saml_force_name_id_format";
|
||||
String SAML_SERVER_SIGNATURE = "saml.server.signature";
|
||||
String SAML_SERVER_SIGNATURE_KEYINFO_EXT = "saml.server.signature.keyinfo.ext";
|
||||
String SAML_FORCE_POST_BINDING = "saml.force.post.binding";
|
||||
String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature";
|
||||
String SAML_ENCRYPT = "saml.encrypt";
|
||||
|
||||
@@ -74,8 +74,10 @@ import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -98,6 +100,7 @@ public class SamlProtocol implements LoginProtocol {
|
||||
public static final String SAML_REDIRECT_BINDING = "get";
|
||||
public static final String SAML_REQUEST_ID = "SAML_REQUEST_ID";
|
||||
public static final String SAML_LOGOUT_BINDING = "saml.logout.binding";
|
||||
public static final String SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO = "saml.logout.addExtensionsElementWithKeyInfo";
|
||||
public static final String SAML_LOGOUT_REQUEST_ID = "SAML_LOGOUT_REQUEST_ID";
|
||||
public static final String SAML_LOGOUT_RELAY_STATE = "SAML_LOGOUT_RELAY_STATE";
|
||||
public static final String SAML_LOGOUT_CANONICALIZATION = "SAML_LOGOUT_CANONICALIZATION";
|
||||
@@ -373,7 +376,15 @@ public class SamlProtocol implements LoginProtocol {
|
||||
}
|
||||
|
||||
Document samlDocument = null;
|
||||
KeyManager keyManager = session.keys();
|
||||
KeyManager.ActiveKey keys = keyManager.getActiveKey(realm);
|
||||
boolean postBinding = isPostBinding(clientSession);
|
||||
|
||||
try {
|
||||
if ((! postBinding) && samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) {
|
||||
builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
|
||||
}
|
||||
|
||||
ResponseType samlModel = builder.buildModel();
|
||||
final AttributeStatementType attributeStatement = populateAttributeStatements(attributeStatementMappers, session, userSession, clientSession);
|
||||
populateRoles(roleListMapper, session, userSession, clientSession, attributeStatement);
|
||||
@@ -394,22 +405,19 @@ public class SamlProtocol implements LoginProtocol {
|
||||
JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder();
|
||||
bindingBuilder.relayState(relayState);
|
||||
|
||||
KeyManager keyManager = session.keys();
|
||||
KeyManager.ActiveKey keys = keyManager.getActiveKey(realm);
|
||||
|
||||
if (samlClient.requiresRealmSignature()) {
|
||||
String canonicalization = samlClient.getCanonicalizationMethod();
|
||||
if (canonicalization != null) {
|
||||
bindingBuilder.canonicalizationMethod(canonicalization);
|
||||
}
|
||||
bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
|
||||
bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
|
||||
}
|
||||
if (samlClient.requiresAssertionSignature()) {
|
||||
String canonicalization = samlClient.getCanonicalizationMethod();
|
||||
if (canonicalization != null) {
|
||||
bindingBuilder.canonicalizationMethod(canonicalization);
|
||||
}
|
||||
bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signAssertions();
|
||||
bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signAssertions();
|
||||
}
|
||||
if (samlClient.requiresEncryption()) {
|
||||
PublicKey publicKey = null;
|
||||
@@ -496,12 +504,17 @@ public class SamlProtocol implements LoginProtocol {
|
||||
if (isLogoutPostBindingForClient(clientSession)) {
|
||||
String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING);
|
||||
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
|
||||
// This is POST binding, hence KeyID is included in dsig:KeyInfo/dsig:KeyName, no need to add <samlp:Extensions> element
|
||||
JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient);
|
||||
return binding.postBinding(logoutBuilder.buildDocument()).request(bindingUri);
|
||||
} else {
|
||||
logger.debug("frontchannel redirect binding");
|
||||
String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING);
|
||||
SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client);
|
||||
if (samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) {
|
||||
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
|
||||
logoutBuilder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
|
||||
}
|
||||
JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient);
|
||||
return binding.redirectBinding(logoutBuilder.buildDocument()).request(bindingUri);
|
||||
}
|
||||
@@ -534,6 +547,7 @@ public class SamlProtocol implements LoginProtocol {
|
||||
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
|
||||
binding.relayState(logoutRelayState);
|
||||
String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM);
|
||||
boolean postBinding = isLogoutPostBindingForInitiator(userSession);
|
||||
if (signingAlgorithm != null) {
|
||||
SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm);
|
||||
String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION);
|
||||
@@ -541,7 +555,11 @@ public class SamlProtocol implements LoginProtocol {
|
||||
binding.canonicalizationMethod(canonicalization);
|
||||
}
|
||||
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
|
||||
binding.signatureAlgorithm(algorithm).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
|
||||
binding.signatureAlgorithm(algorithm).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
|
||||
boolean addExtension = (! postBinding) && Objects.equals("true", userSession.getNote(SamlProtocol.SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO));
|
||||
if (addExtension) { // Only include extension if REDIRECT binding and signing whole SAML protocol message
|
||||
builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -577,6 +595,7 @@ public class SamlProtocol implements LoginProtocol {
|
||||
String logoutRequestString = null;
|
||||
try {
|
||||
JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient);
|
||||
// This is POST binding, hence KeyID is included in dsig:KeyInfo/dsig:KeyName, no need to add <samlp:Extensions> element
|
||||
logoutRequestString = binding.postBinding(logoutBuilder.buildDocument()).encoded();
|
||||
} catch (Exception e) {
|
||||
logger.warn("failed to send saml logout", e);
|
||||
@@ -639,7 +658,7 @@ public class SamlProtocol implements LoginProtocol {
|
||||
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder();
|
||||
if (samlClient.requiresRealmSignature()) {
|
||||
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
|
||||
binding.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
|
||||
binding.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package org.keycloak.protocol.saml;
|
||||
|
||||
import java.security.Key;
|
||||
import org.keycloak.common.VerificationException;
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.models.ClientModel;
|
||||
@@ -33,6 +34,15 @@ import javax.ws.rs.core.UriInfo;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.cert.Certificate;
|
||||
import org.keycloak.dom.saml.v2.SAML2Object;
|
||||
import org.keycloak.dom.saml.v2.protocol.ExtensionsType;
|
||||
import org.keycloak.dom.saml.v2.protocol.RequestAbstractType;
|
||||
import org.keycloak.dom.saml.v2.protocol.StatusResponseType;
|
||||
import org.keycloak.rotation.HardcodedKeyLocator;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -40,20 +50,36 @@ import java.security.cert.Certificate;
|
||||
*/
|
||||
public class SamlProtocolUtils {
|
||||
|
||||
|
||||
/**
|
||||
* Verifies a signature of the given SAML document using settings for the given client.
|
||||
* Throws an exception if the client signature is expected to be present as per the client
|
||||
* settings and it is invalid, otherwise returns back to the caller.
|
||||
*
|
||||
* @param client
|
||||
* @param document
|
||||
* @throws VerificationException
|
||||
*/
|
||||
public static void verifyDocumentSignature(ClientModel client, Document document) throws VerificationException {
|
||||
SamlClient samlClient = new SamlClient(client);
|
||||
if (!samlClient.requiresClientSignature()) {
|
||||
return;
|
||||
}
|
||||
PublicKey publicKey = getSignatureValidationKey(client);
|
||||
verifyDocumentSignature(document, publicKey);
|
||||
verifyDocumentSignature(document, new HardcodedKeyLocator(publicKey));
|
||||
}
|
||||
|
||||
public static void verifyDocumentSignature(Document document, PublicKey publicKey) throws VerificationException {
|
||||
/**
|
||||
* Verifies a signature of the given SAML document using keys obtained from the given key locator.
|
||||
* Throws an exception if the client signature is invalid, otherwise returns back to the caller.
|
||||
*
|
||||
* @param document
|
||||
* @param keyLocator
|
||||
* @throws VerificationException
|
||||
*/
|
||||
public static void verifyDocumentSignature(Document document, KeyLocator keyLocator) throws VerificationException {
|
||||
SAML2Signature saml2Signature = new SAML2Signature();
|
||||
try {
|
||||
if (!saml2Signature.validate(document, publicKey)) {
|
||||
if (!saml2Signature.validate(document, keyLocator)) {
|
||||
throw new VerificationException("Invalid signature on document");
|
||||
}
|
||||
} catch (ProcessingException e) {
|
||||
@@ -61,10 +87,22 @@ public class SamlProtocolUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns public part of SAML signing key from the client settings.
|
||||
* @param client
|
||||
* @return Public key for signature validation.
|
||||
* @throws VerificationException
|
||||
*/
|
||||
public static PublicKey getSignatureValidationKey(ClientModel client) throws VerificationException {
|
||||
return getPublicKey(new SamlClient(client).getClientSigningCertificate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns public part of SAML encryption key from the client settings.
|
||||
* @param client
|
||||
* @return Public key for encryption.
|
||||
* @throws VerificationException
|
||||
*/
|
||||
public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException {
|
||||
return getPublicKey(client, SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
|
||||
}
|
||||
@@ -85,7 +123,7 @@ public class SamlProtocolUtils {
|
||||
return cert.getPublicKey();
|
||||
}
|
||||
|
||||
public static void verifyRedirectSignature(PublicKey publicKey, UriInfo uriInformation, String paramKey) throws VerificationException {
|
||||
public static void verifyRedirectSignature(SAMLDocumentHolder documentHolder, KeyLocator locator, UriInfo uriInformation, String paramKey) throws VerificationException {
|
||||
MultivaluedMap<String, String> encodedParams = uriInformation.getQueryParameters(false);
|
||||
String request = encodedParams.getFirst(paramKey);
|
||||
String algorithm = encodedParams.getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY);
|
||||
@@ -96,10 +134,11 @@ public class SamlProtocolUtils {
|
||||
if (algorithm == null) throw new VerificationException("SigAlg was null");
|
||||
if (signature == null) throw new VerificationException("Signature was null");
|
||||
|
||||
String keyId = getMessageSigningKeyId(documentHolder.getSamlObject());
|
||||
|
||||
// Shibboleth doesn't sign the document for redirect binding.
|
||||
// todo maybe a flag?
|
||||
|
||||
|
||||
UriBuilder builder = UriBuilder.fromPath("/")
|
||||
.queryParam(paramKey, request);
|
||||
if (encodedParams.containsKey(GeneralConstants.RELAY_STATE)) {
|
||||
@@ -113,8 +152,13 @@ public class SamlProtocolUtils {
|
||||
|
||||
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm);
|
||||
Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg
|
||||
validator.initVerify(publicKey);
|
||||
validator.update(rawQuery.getBytes("UTF-8"));
|
||||
Key key = locator.getKey(keyId);
|
||||
if (key instanceof PublicKey) {
|
||||
validator.initVerify((PublicKey) key);
|
||||
validator.update(rawQuery.getBytes("UTF-8"));
|
||||
} else {
|
||||
throw new VerificationException("Invalid key locator for signature verification");
|
||||
}
|
||||
if (!validator.verify(decodedSignature)) {
|
||||
throw new VerificationException("Invalid query param signature");
|
||||
}
|
||||
@@ -123,5 +167,32 @@ public class SamlProtocolUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static String getMessageSigningKeyId(SAML2Object doc) {
|
||||
final ExtensionsType extensions;
|
||||
if (doc instanceof RequestAbstractType) {
|
||||
extensions = ((RequestAbstractType) doc).getExtensions();
|
||||
} else if (doc instanceof StatusResponseType) {
|
||||
extensions = ((StatusResponseType) doc).getExtensions();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (extensions == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (Object ext : extensions.getAny()) {
|
||||
if (! (ext instanceof Element)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String res = KeycloakKeySamlExtensionGenerator.getMessageSigningKeyIdFromElement((Element) ext);
|
||||
|
||||
if (res != null) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,11 @@ public class SamlRepresentationAttributes {
|
||||
public String getSamlServerSignature() {
|
||||
if (getAttributes() == null) return null;
|
||||
return getAttributes().get(SamlConfigAttributes.SAML_SERVER_SIGNATURE);
|
||||
}
|
||||
|
||||
public String getAddExtensionsElementWithKeyInfo() {
|
||||
if (getAttributes() == null) return null;
|
||||
return getAttributes().get(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT);
|
||||
}
|
||||
|
||||
public String getForcePostBinding() {
|
||||
|
||||
@@ -74,6 +74,17 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import org.keycloak.common.util.StringPropertyReplacer;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.keys.KeyMetadata;
|
||||
import org.keycloak.rotation.HardcodedKeyLocator;
|
||||
import org.keycloak.rotation.KeyLocator;
|
||||
import org.keycloak.saml.SPMetadataDescriptor;
|
||||
import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator;
|
||||
|
||||
/**
|
||||
* Resource class for the oauth/openid connect token service
|
||||
@@ -336,6 +347,8 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||
String logoutBinding = getBindingType();
|
||||
if ("true".equals(samlClient.forcePostBinding()))
|
||||
logoutBinding = SamlProtocol.SAML_POST_BINDING;
|
||||
boolean postBinding = Objects.equals(SamlProtocol.SAML_POST_BINDING, logoutBinding);
|
||||
|
||||
String bindingUri = SamlProtocol.getLogoutServiceUrl(uriInfo, client, logoutBinding);
|
||||
UserSessionModel userSession = authResult.getSession();
|
||||
userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING_URI, bindingUri);
|
||||
@@ -347,6 +360,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||
userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState);
|
||||
userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID());
|
||||
userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding);
|
||||
userSession.setNote(SamlProtocol.SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO, Boolean.toString((! postBinding) && samlClient.addExtensionsElementWithKeyInfo()));
|
||||
userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, samlClient.getCanonicalizationMethod());
|
||||
userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL);
|
||||
// remove client from logout requests
|
||||
@@ -397,14 +411,17 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||
builder.destination(logoutBindingUri);
|
||||
builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString());
|
||||
JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder().relayState(logoutRelayState);
|
||||
boolean postBinding = SamlProtocol.SAML_POST_BINDING.equals(logoutBinding);
|
||||
if (samlClient.requiresRealmSignature()) {
|
||||
SignatureAlgorithm algorithm = samlClient.getSignatureAlgorithm();
|
||||
KeyManager.ActiveKey keys = session.keys().getActiveKey(realm);
|
||||
binding.signatureAlgorithm(algorithm).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
|
||||
|
||||
binding.signatureAlgorithm(algorithm).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument();
|
||||
if (! postBinding && samlClient.addExtensionsElementWithKeyInfo()) { // Only include extension if REDIRECT binding and signing whole SAML protocol message
|
||||
builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid()));
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (SamlProtocol.SAML_POST_BINDING.equals(logoutBinding)) {
|
||||
if (postBinding) {
|
||||
return binding.postBinding(builder.buildDocument()).response(logoutBindingUri);
|
||||
} else {
|
||||
return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri);
|
||||
@@ -466,7 +483,8 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||
return;
|
||||
}
|
||||
PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client);
|
||||
SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo, GeneralConstants.SAML_REQUEST_KEY);
|
||||
KeyLocator clientKeyLocator = new HardcodedKeyLocator(publicKey);
|
||||
SamlProtocolUtils.verifyRedirectSignature(documentHolder, clientKeyLocator, uriInfo, GeneralConstants.SAML_REQUEST_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -541,12 +559,30 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||
public static String getIDPMetadataDescriptor(UriInfo uriInfo, KeycloakSession session, RealmModel realm) throws IOException {
|
||||
InputStream is = SamlService.class.getResourceAsStream("/idp-metadata-template.xml");
|
||||
String template = StreamUtil.readString(is);
|
||||
template = template.replace("${idp.entityID}", RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString());
|
||||
template = template.replace("${idp.sso.HTTP-POST}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString());
|
||||
template = template.replace("${idp.sso.HTTP-Redirect}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString());
|
||||
template = template.replace("${idp.sls.HTTP-POST}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString());
|
||||
template = template.replace("${idp.signing.certificate}", PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate()));
|
||||
return template;
|
||||
Properties props = new Properties();
|
||||
props.put("idp.entityID", RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString());
|
||||
props.put("idp.sso.HTTP-POST", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString());
|
||||
props.put("idp.sso.HTTP-Redirect", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString());
|
||||
props.put("idp.sls.HTTP-POST", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString());
|
||||
StringBuilder keysString = new StringBuilder();
|
||||
Set<KeyMetadata> keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list
|
||||
? (int) (o2.getProviderPriority() - o1.getProviderPriority())
|
||||
: (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1));
|
||||
keys.addAll(session.keys().getKeys(realm, false));
|
||||
for (KeyMetadata key : keys) {
|
||||
addKeyInfo(keysString, key, KeyTypes.SIGNING.value());
|
||||
}
|
||||
props.put("idp.signing.certificates", keysString.toString());
|
||||
return StringPropertyReplacer.replaceProperties(template, props);
|
||||
}
|
||||
|
||||
private static void addKeyInfo(StringBuilder target, KeyMetadata key, String purpose) {
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
target.append(SPMetadataDescriptor.xmlKeyInfo(" ",
|
||||
key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, false));
|
||||
}
|
||||
|
||||
@GET
|
||||
|
||||
+3
-13
@@ -18,7 +18,6 @@
|
||||
package org.keycloak.protocol.saml.installation;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
@@ -42,14 +41,14 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide
|
||||
@Override
|
||||
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
|
||||
SamlClient samlClient = new SamlClient(client);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("<keycloak-saml-adapter>\n");
|
||||
baseXml(session, realm, client, baseUri, samlClient, buffer);
|
||||
buffer.append("</keycloak-saml-adapter>\n");
|
||||
return Response.ok(buffer.toString(), MediaType.TEXT_PLAIN_TYPE).build();
|
||||
}
|
||||
|
||||
public static void baseXml(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, SamlClient samlClient, StringBuffer buffer) {
|
||||
public static void baseXml(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, SamlClient samlClient, StringBuilder buffer) {
|
||||
buffer.append(" <SP entityID=\"").append(client.getClientId()).append("\"\n");
|
||||
buffer.append(" sslPolicy=\"").append(realm.getSslRequired().name()).append("\"\n");
|
||||
buffer.append(" logoutPage=\"SPECIFY YOUR LOGOUT PAGE!\">\n");
|
||||
@@ -113,15 +112,6 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide
|
||||
buffer.append(" postBindingUrl=\"").append(bindingUrl).append("\"\n");
|
||||
buffer.append(" redirectBindingUrl=\"").append(bindingUrl).append("\"");
|
||||
buffer.append("/>\n");
|
||||
if (samlClient.requiresRealmSignature()) {
|
||||
buffer.append(" <Keys>\n");
|
||||
buffer.append(" <Key signing=\"true\">\n");
|
||||
buffer.append(" <CertificatePem>\n");
|
||||
buffer.append(" ").append(PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate())).append("\n");
|
||||
buffer.append(" </CertificatePem>\n");
|
||||
buffer.append(" </Key>\n");
|
||||
buffer.append(" </Keys>\n");
|
||||
}
|
||||
buffer.append(" </IDP>\n");
|
||||
buffer.append(" </SP>\n");
|
||||
}
|
||||
@@ -138,7 +128,7 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "Keycloak SAML adapter configuration file. Put this in WEB-INF directory if your WAR.";
|
||||
return "Keycloak SAML adapter configuration file. Put this in WEB-INF directory of your WAR.";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ public class KeycloakSamlSubsystemInstallation implements ClientInstallationProv
|
||||
@Override
|
||||
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
|
||||
SamlClient samlClient = new SamlClient(client);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("<secure-deployment name=\"YOUR-WAR.war\">\n");
|
||||
KeycloakSamlClientInstallation.baseXml(session, realm, client, baseUri, samlClient, buffer);
|
||||
buffer.append("</secure-deployment>\n");
|
||||
|
||||
+53
-36
@@ -32,6 +32,11 @@ import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.net.URI;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
import org.keycloak.keys.KeyMetadata;
|
||||
import org.keycloak.saml.SPMetadataDescriptor;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -41,49 +46,61 @@ public class SamlIDPDescriptorClientInstallation implements ClientInstallationPr
|
||||
public static String getIDPDescriptorForClient(KeycloakSession session, RealmModel realm, ClientModel client, URI serverBaseUri) {
|
||||
SamlClient samlClient = new SamlClient(client);
|
||||
String idpEntityId = RealmsResource.realmBaseUrl(UriBuilder.fromUri(serverBaseUri)).build(realm.getName()).toString();
|
||||
String idp = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<EntityDescriptor entityID=\"" + idpEntityId + "\"\n" +
|
||||
" xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n" +
|
||||
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
|
||||
" <IDPSSODescriptor WantAuthnRequestsSigned=\"" + Boolean.toString(samlClient.requiresClientSignature()) + "\"\n" +
|
||||
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
+ "<EntityDescriptor entityID=\"").append(idpEntityId).append("\"\n"
|
||||
+ " xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n"
|
||||
+ " xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\"\n"
|
||||
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
|
||||
+ " <IDPSSODescriptor WantAuthnRequestsSigned=\"")
|
||||
.append(samlClient.requiresClientSignature())
|
||||
.append("\"\n"
|
||||
+ " protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n");
|
||||
if (samlClient.forceNameIDFormat() && samlClient.getNameIDFormat() != null) {
|
||||
idp += " <NameIDFormat>" + samlClient.getNameIDFormat() + "</NameIDFormat>\n";
|
||||
sb.append(" <NameIDFormat>").append(samlClient.getNameIDFormat()).append("</NameIDFormat>\n");
|
||||
} else {
|
||||
idp += " <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>\n" +
|
||||
" <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>\n" +
|
||||
" <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>\n" +
|
||||
" <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>\n";
|
||||
sb.append(" <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>\n"
|
||||
+ " <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>\n"
|
||||
+ " <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>\n"
|
||||
+ " <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>\n");
|
||||
}
|
||||
String bindUrl = RealmsResource.protocolUrl(UriBuilder.fromUri(serverBaseUri)).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString();
|
||||
idp += "\n" +
|
||||
" <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n" +
|
||||
" Location=\"" + bindUrl + "\" />\n";
|
||||
if (!samlClient.forcePostBinding()) {
|
||||
idp += " <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n" +
|
||||
" Location=\"" + bindUrl + "\" />\n";
|
||||
sb.append("\n"
|
||||
+ " <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n"
|
||||
+ " Location=\"").append(bindUrl).append("\" />\n");
|
||||
if (! samlClient.forcePostBinding()) {
|
||||
sb.append(" <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n"
|
||||
+ " Location=\"").append(bindUrl).append("\" />\n");
|
||||
|
||||
}
|
||||
idp += " <SingleLogoutService\n" +
|
||||
" Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n" +
|
||||
" Location=\"" + bindUrl + "\" />\n";
|
||||
if (!samlClient.forcePostBinding()) {
|
||||
idp += " <SingleLogoutService\n" +
|
||||
" Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n" +
|
||||
" Location=\"" + bindUrl + "\" />\n";
|
||||
sb.append(" <SingleLogoutService\n"
|
||||
+ " Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n"
|
||||
+ " Location=\"").append(bindUrl).append("\" />\n");
|
||||
if (! samlClient.forcePostBinding()) {
|
||||
sb.append(" <SingleLogoutService\n"
|
||||
+ " Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n"
|
||||
+ " Location=\"").append(bindUrl).append("\" />\n");
|
||||
}
|
||||
idp += " <KeyDescriptor use=\"signing\">\n" +
|
||||
" <dsig:KeyInfo xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">\n" +
|
||||
" <dsig:X509Data>\n" +
|
||||
" <dsig:X509Certificate>\n" +
|
||||
" " + PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate()) + "\n" +
|
||||
" </dsig:X509Certificate>\n" +
|
||||
" </dsig:X509Data>\n" +
|
||||
" </dsig:KeyInfo>\n" +
|
||||
" </KeyDescriptor>\n" +
|
||||
" </IDPSSODescriptor>\n" +
|
||||
"</EntityDescriptor>\n";
|
||||
return idp;
|
||||
|
||||
Set<KeyMetadata> keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list
|
||||
? (int) (o2.getProviderPriority() - o1.getProviderPriority())
|
||||
: (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1));
|
||||
keys.addAll(session.keys().getKeys(realm, false));
|
||||
for (KeyMetadata key : keys) {
|
||||
addKeyInfo(sb, key, KeyTypes.SIGNING.value());
|
||||
}
|
||||
|
||||
sb.append(" </IDPSSODescriptor>\n"
|
||||
+ "</EntityDescriptor>\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void addKeyInfo(StringBuilder target, KeyMetadata key, String purpose) {
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
target.append(SPMetadataDescriptor.xmlKeyInfo(" ", key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-1
@@ -31,6 +31,7 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.net.URI;
|
||||
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
@@ -45,7 +46,8 @@ public class SamlSPDescriptorClientInstallation implements ClientInstallationPro
|
||||
if (logoutUrl == null) logoutUrl = client.getManagementUrl();
|
||||
String nameIdFormat = samlClient.getNameIDFormat();
|
||||
if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
|
||||
return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl, samlClient.requiresClientSignature(), client.getClientId(), nameIdFormat, samlClient.getClientSigningCertificate());
|
||||
String spCertificate = SPMetadataDescriptor.xmlKeyInfo(" ", null, samlClient.getClientSigningCertificate(), KeyTypes.SIGNING.value(), true);
|
||||
return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl, samlClient.requiresClientSignature(), client.getClientId(), nameIdFormat, spCertificate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -110,13 +110,6 @@ public class DefaultKeycloakContext implements KeycloakContext {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmImporter getRealmManager() {
|
||||
RealmManager manager = new RealmManager(session);
|
||||
manager.setContextPath(getContextPath());
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale resolveLocale(UserModel user) {
|
||||
return LocaleHelper.getLocale(session, realm, user);
|
||||
|
||||
@@ -59,7 +59,6 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||
private UserProvider userModel;
|
||||
private UserStorageManager userStorageManager;
|
||||
private UserCredentialStoreManager userCredentialStorageManager;
|
||||
private ScriptingProvider scriptingProvider;
|
||||
private UserSessionProvider sessionProvider;
|
||||
private UserFederationManager federationManager;
|
||||
private UserFederatedStorageProvider userFederatedStorageProvider;
|
||||
@@ -275,14 +274,4 @@ public class DefaultKeycloakSession implements KeycloakSession {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptingProvider scripting() {
|
||||
|
||||
if (scriptingProvider == null) {
|
||||
scriptingProvider = getProvider(ScriptingProvider.class);
|
||||
}
|
||||
|
||||
return scriptingProvider;
|
||||
}
|
||||
}
|
||||
@@ -436,7 +436,11 @@ public class AuthenticationManager {
|
||||
// refresh the cookies!
|
||||
createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection);
|
||||
if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN);
|
||||
if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getLoginUsername(), uriInfo, clientConnection);
|
||||
if (userSession.isRememberMe()) {
|
||||
createRememberMeCookie(realm, userSession.getLoginUsername(), uriInfo, clientConnection);
|
||||
} else {
|
||||
expireRememberMeCookie(realm, uriInfo, clientConnection);
|
||||
}
|
||||
|
||||
// Update userSession note with authTime. But just if flag SSO_AUTH is not set
|
||||
if (!isSSOAuthentication(clientSession)) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserManager;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionProvider;
|
||||
import org.keycloak.models.session.UserSessionPersisterProvider;
|
||||
|
||||
@@ -66,7 +66,7 @@ import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.BruteForceProtector;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.managers.UserManager;
|
||||
import org.keycloak.models.UserManager;
|
||||
import org.keycloak.services.managers.UserSessionManager;
|
||||
import org.keycloak.services.resources.AccountService;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
|
||||
@@ -16,22 +16,12 @@
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<EntitiesDescriptor Name="urn:keycloak"
|
||||
xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
<EntitiesDescriptor Name="urn:keycloak" xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
|
||||
<EntityDescriptor entityID="${idp.entityID}">
|
||||
<IDPSSODescriptor WantAuthnRequestsSigned="true"
|
||||
protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
|
||||
<KeyDescriptor use="signing">
|
||||
<dsig:KeyInfo>
|
||||
<dsig:X509Data>
|
||||
<dsig:X509Certificate>
|
||||
${idp.signing.certificate}
|
||||
</dsig:X509Certificate>
|
||||
</dsig:X509Data>
|
||||
</dsig:KeyInfo>
|
||||
</KeyDescriptor>
|
||||
${idp.signing.certificates}
|
||||
<SingleLogoutService
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
Location="${idp.sls.HTTP-POST}" />
|
||||
|
||||
Reference in new issue
Block a user