From dbac1474197102e235b567e3878002cb6b7d33fb Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Tue, 22 Dec 2015 17:50:03 -0500 Subject: [PATCH 1/2] client config refactor --- .../META-INF/jpa-changelog-1.8.0.xml | 35 +++++ .../keycloak/models/ClientTemplateModel.java | 30 +++++ .../models/entities/ClientTemplateEntity.java | 85 +++++++++++- .../infinispan/ClientTemplateAdapter.java | 121 ++++++++++++++++++ .../cache/entities/CachedClientTemplate.java | 54 ++++++++ .../models/jpa/ClientTemplateAdapter.java | 105 +++++++++++++++ .../jpa/entities/ClientTemplateEntity.java | 101 +++++++++++++++ .../adapters/ClientTemplateAdapter.java | 115 +++++++++++++++++ .../EntityDescriptorDescriptionConverter.java | 16 +-- .../keycloak/protocol/saml/SamlClient.java | 73 ++++++----- .../saml/SamlClientRepresentation.java | 17 ++- .../protocol/saml/SamlConfigAttributes.java | 22 ++++ .../keycloak/protocol/saml/SamlProtocol.java | 94 ++++---------- .../protocol/saml/SamlProtocolFactory.java | 2 + .../protocol/saml/SamlProtocolUtils.java | 9 +- .../keycloak/protocol/saml/SamlService.java | 18 +-- .../ecp/SamlEcpProfileProtocolFactory.java | 4 +- 17 files changed, 769 insertions(+), 132 deletions(-) create mode 100755 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java mode change 100644 => 100755 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileProtocolFactory.java diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.8.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.8.0.xml index 068d4e7187..d0893a3830 100755 --- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.8.0.xml +++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.8.0.xml @@ -18,6 +18,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -69,6 +102,8 @@ + + diff --git a/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java b/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java index f3c0f59954..eb3fbc0342 100755 --- a/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java +++ b/model/api/src/main/java/org/keycloak/models/ClientTemplateModel.java @@ -23,5 +23,35 @@ public interface ClientTemplateModel extends ProtocolMapperContainerModel, Scope String getProtocol(); void setProtocol(String protocol); + void setAttribute(String name, String value); + void removeAttribute(String name); + String getAttribute(String name); + Map getAttributes(); + + boolean isFrontchannelLogout(); + void setFrontchannelLogout(boolean flag); + + boolean isBearerOnly(); + void setBearerOnly(boolean only); + + boolean isPublicClient(); + void setPublicClient(boolean flag); + + boolean isConsentRequired(); + void setConsentRequired(boolean consentRequired); + + boolean isStandardFlowEnabled(); + void setStandardFlowEnabled(boolean standardFlowEnabled); + + boolean isImplicitFlowEnabled(); + void setImplicitFlowEnabled(boolean implicitFlowEnabled); + + boolean isDirectAccessGrantsEnabled(); + void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled); + + boolean isServiceAccountsEnabled(); + void setServiceAccountsEnabled(boolean serviceAccountsEnabled); + + } diff --git a/model/api/src/main/java/org/keycloak/models/entities/ClientTemplateEntity.java b/model/api/src/main/java/org/keycloak/models/entities/ClientTemplateEntity.java index 8ca932f0f1..a9879f7657 100755 --- a/model/api/src/main/java/org/keycloak/models/entities/ClientTemplateEntity.java +++ b/model/api/src/main/java/org/keycloak/models/entities/ClientTemplateEntity.java @@ -15,8 +15,17 @@ public class ClientTemplateEntity extends AbstractIdentifiableEntity { private String realmId; private String protocol; private boolean fullScopeAllowed; - private List scopeIds = new ArrayList(); - private List protocolMappers = new ArrayList(); + private boolean bearerOnly; + private boolean consentRequired; + private boolean standardFlowEnabled; + private boolean implicitFlowEnabled; + private boolean directAccessGrantsEnabled; + private boolean serviceAccountsEnabled; + private boolean publicClient; + private boolean frontchannelLogout; + private List scopeIds = new ArrayList<>(); + private List protocolMappers = new ArrayList<>(); + private Map attributes = new HashMap<>(); public String getName() { return name; @@ -73,5 +82,77 @@ public class ClientTemplateEntity extends AbstractIdentifiableEntity { public void setScopeIds(List scopeIds) { this.scopeIds = scopeIds; } + + public boolean isBearerOnly() { + return bearerOnly; + } + + public void setBearerOnly(boolean bearerOnly) { + this.bearerOnly = bearerOnly; + } + + public boolean isConsentRequired() { + return consentRequired; + } + + public void setConsentRequired(boolean consentRequired) { + this.consentRequired = consentRequired; + } + + public boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + this.standardFlowEnabled = standardFlowEnabled; + } + + public boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + this.implicitFlowEnabled = implicitFlowEnabled; + } + + public boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + this.directAccessGrantsEnabled = directAccessGrantsEnabled; + } + + public boolean isServiceAccountsEnabled() { + return serviceAccountsEnabled; + } + + public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { + this.serviceAccountsEnabled = serviceAccountsEnabled; + } + + public boolean isPublicClient() { + return publicClient; + } + + public void setPublicClient(boolean publicClient) { + this.publicClient = publicClient; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + public boolean isFrontchannelLogout() { + return frontchannelLogout; + } + + public void setFrontchannelLogout(boolean frontchannelLogout) { + this.frontchannelLogout = frontchannelLogout; + } } diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientTemplateAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientTemplateAdapter.java index 13b68aa881..d403adc170 100755 --- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientTemplateAdapter.java +++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/ClientTemplateAdapter.java @@ -194,6 +194,127 @@ public class ClientTemplateAdapter implements ClientTemplateModel { return false; } + public boolean isPublicClient() { + if (updated != null) return updated.isPublicClient(); + return cached.isPublicClient(); + } + + public void setPublicClient(boolean flag) { + getDelegateForUpdate(); + updated.setPublicClient(flag); + } + + public boolean isFrontchannelLogout() { + if (updated != null) return updated.isPublicClient(); + return cached.isFrontchannelLogout(); + } + + public void setFrontchannelLogout(boolean flag) { + getDelegateForUpdate(); + updated.setFrontchannelLogout(flag); + } + + @Override + public void setAttribute(String name, String value) { + getDelegateForUpdate(); + updated.setAttribute(name, value); + + } + + @Override + public void removeAttribute(String name) { + getDelegateForUpdate(); + updated.removeAttribute(name); + + } + + @Override + public String getAttribute(String name) { + if (updated != null) return updated.getAttribute(name); + return cached.getAttributes().get(name); + } + + @Override + public Map getAttributes() { + if (updated != null) return updated.getAttributes(); + Map copy = new HashMap(); + copy.putAll(cached.getAttributes()); + return copy; + } + + @Override + public boolean isBearerOnly() { + if (updated != null) return updated.isBearerOnly(); + return cached.isBearerOnly(); + } + + @Override + public void setBearerOnly(boolean only) { + getDelegateForUpdate(); + updated.setBearerOnly(only); + } + + @Override + public boolean isConsentRequired() { + if (updated != null) return updated.isConsentRequired(); + return cached.isConsentRequired(); + } + + @Override + public void setConsentRequired(boolean consentRequired) { + getDelegateForUpdate(); + updated.setConsentRequired(consentRequired); + } + + @Override + public boolean isStandardFlowEnabled() { + if (updated != null) return updated.isStandardFlowEnabled(); + return cached.isStandardFlowEnabled(); + } + + @Override + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + getDelegateForUpdate(); + updated.setStandardFlowEnabled(standardFlowEnabled); + } + + @Override + public boolean isImplicitFlowEnabled() { + if (updated != null) return updated.isImplicitFlowEnabled(); + return cached.isImplicitFlowEnabled(); + } + + @Override + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + getDelegateForUpdate(); + updated.setImplicitFlowEnabled(implicitFlowEnabled); + } + + @Override + public boolean isDirectAccessGrantsEnabled() { + if (updated != null) return updated.isDirectAccessGrantsEnabled(); + return cached.isDirectAccessGrantsEnabled(); + } + + @Override + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + getDelegateForUpdate(); + updated.setDirectAccessGrantsEnabled(directAccessGrantsEnabled); + } + + @Override + public boolean isServiceAccountsEnabled() { + if (updated != null) return updated.isServiceAccountsEnabled(); + return cached.isServiceAccountsEnabled(); + } + + @Override + public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { + getDelegateForUpdate(); + updated.setServiceAccountsEnabled(serviceAccountsEnabled); + } + + @Override diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientTemplate.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientTemplate.java index 58bcc126cd..b03430deee 100755 --- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientTemplate.java +++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientTemplate.java @@ -29,8 +29,17 @@ public class CachedClientTemplate implements Serializable { private String realm; private String protocol; private boolean fullScopeAllowed; + private boolean publicClient; + private boolean frontchannelLogout; + private boolean bearerOnly; + private boolean consentRequired; + private boolean standardFlowEnabled; + private boolean implicitFlowEnabled; + private boolean directAccessGrantsEnabled; + private boolean serviceAccountsEnabled; private Set scope = new HashSet(); private Set protocolMappers = new HashSet(); + private Map attributes = new HashMap(); public CachedClientTemplate(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientTemplateModel model) { id = model.getId(); @@ -45,6 +54,15 @@ public class CachedClientTemplate implements Serializable { for (RoleModel role : model.getScopeMappings()) { scope.add(role.getId()); } + attributes.putAll(model.getAttributes()); + frontchannelLogout = model.isFrontchannelLogout(); + publicClient = model.isPublicClient(); + bearerOnly = model.isBearerOnly(); + consentRequired = model.isConsentRequired(); + standardFlowEnabled = model.isStandardFlowEnabled(); + implicitFlowEnabled = model.isImplicitFlowEnabled(); + directAccessGrantsEnabled = model.isDirectAccessGrantsEnabled(); + serviceAccountsEnabled = model.isServiceAccountsEnabled(); } public String getId() { return id; @@ -77,4 +95,40 @@ public class CachedClientTemplate implements Serializable { public Set getScope() { return scope; } + + public boolean isPublicClient() { + return publicClient; + } + + public boolean isFrontchannelLogout() { + return frontchannelLogout; + } + + public boolean isBearerOnly() { + return bearerOnly; + } + + public boolean isConsentRequired() { + return consentRequired; + } + + public boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + public boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + public boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + + public boolean isServiceAccountsEnabled() { + return serviceAccountsEnabled; + } + + public Map getAttributes() { + return attributes; + } } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientTemplateAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientTemplateAdapter.java index 2d5c78b7c2..4e70debd06 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/ClientTemplateAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/ClientTemplateAdapter.java @@ -287,6 +287,111 @@ public class ClientTemplateAdapter implements ClientTemplateModel { return false; } + @Override + public boolean isPublicClient() { + return entity.isPublicClient(); + } + + @Override + public void setPublicClient(boolean flag) { + entity.setPublicClient(flag); + } + + @Override + public boolean isFrontchannelLogout() { + return entity.isFrontchannelLogout(); + } + + @Override + public void setFrontchannelLogout(boolean flag) { + entity.setFrontchannelLogout(flag); + } + + @Override + public void setAttribute(String name, String value) { + entity.getAttributes().put(name, value); + + } + + @Override + public void removeAttribute(String name) { + entity.getAttributes().remove(name); + } + + @Override + public String getAttribute(String name) { + return entity.getAttributes().get(name); + } + + @Override + public Map getAttributes() { + Map copy = new HashMap<>(); + copy.putAll(entity.getAttributes()); + return copy; + } + + @Override + public boolean isBearerOnly() { + return entity.isBearerOnly(); + } + + @Override + public void setBearerOnly(boolean only) { + entity.setBearerOnly(only); + } + + @Override + public boolean isConsentRequired() { + return entity.isConsentRequired(); + } + + @Override + public void setConsentRequired(boolean consentRequired) { + entity.setConsentRequired(consentRequired); + } + + @Override + public boolean isStandardFlowEnabled() { + return entity.isStandardFlowEnabled(); + } + + @Override + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + entity.setStandardFlowEnabled(standardFlowEnabled); + } + + @Override + public boolean isImplicitFlowEnabled() { + return entity.isImplicitFlowEnabled(); + } + + @Override + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + entity.setImplicitFlowEnabled(implicitFlowEnabled); + } + + @Override + public boolean isDirectAccessGrantsEnabled() { + return entity.isDirectAccessGrantsEnabled(); + } + + @Override + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + entity.setDirectAccessGrantsEnabled(directAccessGrantsEnabled); + } + + @Override + public boolean isServiceAccountsEnabled() { + return entity.isServiceAccountsEnabled(); + } + + @Override + public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { + entity.setServiceAccountsEnabled(serviceAccountsEnabled); + } + + + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java index 5da01c4c57..ba3fc623be 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/ClientTemplateEntity.java @@ -48,6 +48,35 @@ public class ClientTemplateEntity { @Column(name="FULL_SCOPE_ALLOWED") private boolean fullScopeAllowed; + @Column(name="CONSENT_REQUIRED") + private boolean consentRequired; + + @Column(name="STANDARD_FLOW_ENABLED") + private boolean standardFlowEnabled; + + @Column(name="IMPLICIT_FLOW_ENABLED") + private boolean implicitFlowEnabled; + + @Column(name="DIRECT_ACCESS_GRANTS_ENABLED") + private boolean directAccessGrantsEnabled; + + @Column(name="SERVICE_ACCOUNTS_ENABLED") + private boolean serviceAccountsEnabled; + + @Column(name="FRONTCHANNEL_LOGOUT") + private boolean frontchannelLogout; + @Column(name="PUBLIC_CLIENT") + private boolean publicClient; + @Column(name="BEARER_ONLY") + private boolean bearerOnly; + + + @ElementCollection + @MapKeyColumn(name="NAME") + @Column(name="VALUE", length = 2048) + @CollectionTable(name="CLIENT_TEMPLATE_ATTRIBUTES", joinColumns={ @JoinColumn(name="TEMPLATE_ID") }) + protected Map attributes = new HashMap(); + public RealmEntity getRealm() { return realm; } @@ -103,4 +132,76 @@ public class ClientTemplateEntity { public void setFullScopeAllowed(boolean fullScopeAllowed) { this.fullScopeAllowed = fullScopeAllowed; } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + public boolean isConsentRequired() { + return consentRequired; + } + + public void setConsentRequired(boolean consentRequired) { + this.consentRequired = consentRequired; + } + + public boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + this.standardFlowEnabled = standardFlowEnabled; + } + + public boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + this.implicitFlowEnabled = implicitFlowEnabled; + } + + public boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + this.directAccessGrantsEnabled = directAccessGrantsEnabled; + } + + public boolean isServiceAccountsEnabled() { + return serviceAccountsEnabled; + } + + public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { + this.serviceAccountsEnabled = serviceAccountsEnabled; + } + + public boolean isFrontchannelLogout() { + return frontchannelLogout; + } + + public void setFrontchannelLogout(boolean frontchannelLogout) { + this.frontchannelLogout = frontchannelLogout; + } + + public boolean isPublicClient() { + return publicClient; + } + + public void setPublicClient(boolean publicClient) { + this.publicClient = publicClient; + } + + public boolean isBearerOnly() { + return bearerOnly; + } + + public void setBearerOnly(boolean bearerOnly) { + this.bearerOnly = bearerOnly; + } } diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientTemplateAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientTemplateAdapter.java index 91725bffd0..9b2d9215ac 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientTemplateAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ClientTemplateAdapter.java @@ -274,6 +274,121 @@ public class ClientTemplateAdapter extends AbstractMongoAdapter getAttributes() { + Map copy = new HashMap(); + copy.putAll(getMongoEntity().getAttributes()); + return copy; + } + + @Override + public boolean isBearerOnly() { + return getMongoEntity().isBearerOnly(); + } + + @Override + public void setBearerOnly(boolean only) { + getMongoEntity().setBearerOnly(only); + updateMongoEntity(); + } + + @Override + public boolean isConsentRequired() { + return getMongoEntity().isConsentRequired(); + } + + @Override + public void setConsentRequired(boolean consentRequired) { + getMongoEntity().setConsentRequired(consentRequired); + updateMongoEntity(); + } + + @Override + public boolean isStandardFlowEnabled() { + return getMongoEntity().isStandardFlowEnabled(); + } + + @Override + public void setStandardFlowEnabled(boolean standardFlowEnabled) { + getMongoEntity().setStandardFlowEnabled(standardFlowEnabled); + updateMongoEntity(); + } + + @Override + public boolean isImplicitFlowEnabled() { + return getMongoEntity().isImplicitFlowEnabled(); + } + + @Override + public void setImplicitFlowEnabled(boolean implicitFlowEnabled) { + getMongoEntity().setImplicitFlowEnabled(implicitFlowEnabled); + updateMongoEntity(); + } + + @Override + public boolean isDirectAccessGrantsEnabled() { + return getMongoEntity().isDirectAccessGrantsEnabled(); + } + + @Override + public void setDirectAccessGrantsEnabled(boolean directAccessGrantsEnabled) { + getMongoEntity().setDirectAccessGrantsEnabled(directAccessGrantsEnabled); + updateMongoEntity(); + } + + @Override + public boolean isServiceAccountsEnabled() { + return getMongoEntity().isServiceAccountsEnabled(); + } + + @Override + public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) { + getMongoEntity().setServiceAccountsEnabled(serviceAccountsEnabled); + updateMongoEntity(); + } + + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java index 42ff3ee0c0..20f78109f4 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java @@ -4,10 +4,8 @@ import org.keycloak.Config; import org.keycloak.dom.saml.v2.metadata.*; import org.keycloak.exportimport.ClientDescriptionConverter; import org.keycloak.exportimport.ClientDescriptionConverterFactory; -import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.RealmModel; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.saml.SignatureAlgorithm; @@ -80,12 +78,12 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo app.setFullScopeAllowed(true); app.setProtocol(SamlProtocol.LOGIN_PROTOCOL); - attributes.put(SamlProtocol.SAML_SERVER_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); // default to true - attributes.put(SamlProtocol.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.toString()); - attributes.put(SamlProtocol.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE); + attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); // default to true + attributes.put(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.toString()); + attributes.put(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE); SPSSODescriptorType spDescriptorType = CoreConfigUtil.getSPDescriptor(entity); if (spDescriptorType.isWantAssertionsSigned()) { - attributes.put(SamlProtocol.SAML_ASSERTION_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); + attributes.put(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); } String logoutPost = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get()); if (logoutPost != null) attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, logoutPost); @@ -114,10 +112,10 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo } String certPem = KeycloakModelUtils.getPemFromCertificate(cert); if (keyDescriptor.getUse() == KeyTypes.SIGNING) { - attributes.put(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); - attributes.put(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem); + attributes.put(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); + attributes.put(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem); } else if (keyDescriptor.getUse() == KeyTypes.ENCRYPTION) { - attributes.put(SamlProtocol.SAML_ENCRYPT, SamlProtocol.ATTRIBUTE_TRUE_VALUE); + attributes.put(SamlConfigAttributes.SAML_ENCRYPT, SamlProtocol.ATTRIBUTE_TRUE_VALUE); attributes.put(SamlProtocol.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, certPem); } } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java index 241cc268f8..df27de2fbc 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java @@ -8,23 +8,31 @@ import org.keycloak.saml.SignatureAlgorithm; * @version $Revision: 1 $ */ public class SamlClient { - public static final String SAML_SIGNING_PRIVATE_KEY = "saml.signing.private.key"; protected ClientModel client; public SamlClient(ClientModel client) { this.client = client; } + public String getId() { + return client.getId(); + } + + public String getClientId() { + return client.getClientId(); + } +// + public String getCanonicalizationMethod() { - return client.getAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); + return client.getAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); } public void setCanonicalizationMethod(String value) { - client.setAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE, value); + client.setAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE, value); } public SignatureAlgorithm getSignatureAlgorithm() { - String alg = client.getAttribute(SamlProtocol.SAML_SIGNATURE_ALGORITHM); + String alg = client.getAttribute(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM); if (alg != null) { SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg); if (algorithm != null) @@ -34,94 +42,91 @@ public class SamlClient { } public void setSignatureAlgorithm(SignatureAlgorithm algorithm) { - client.setAttribute(SamlProtocol.SAML_SIGNATURE_ALGORITHM, algorithm.name()); + client.setAttribute(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, algorithm.name()); } public String getNameIDFormat() { - return client.getAttributes().get(SamlProtocol.SAML_NAME_ID_FORMAT_ATTRIBUTE); + return client.getAttributes().get(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE); } public void setNameIDFormat(String format) { - client.setAttribute(SamlProtocol.SAML_NAME_ID_FORMAT_ATTRIBUTE, format); + client.setAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, format); } public boolean includeAuthnStatement() { - return "true".equals(client.getAttribute(SamlProtocol.SAML_AUTHNSTATEMENT)); + return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT)); } public void setIncludeAuthnStatement(boolean val) { - client.setAttribute(SamlProtocol.SAML_AUTHNSTATEMENT, Boolean.toString(val)); + client.setAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, Boolean.toString(val)); } public boolean forceNameIDFormat() { - return "true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE)); + return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE)); } public void setForceNameIDFormat(boolean val) { - client.setAttribute(SamlProtocol.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, Boolean.toString(val)); + client.setAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, Boolean.toString(val)); } - public boolean requiresRealmSignature(ClientModel client) { - return "true".equals(client.getAttribute(SamlProtocol.SAML_SERVER_SIGNATURE)); + public boolean requiresRealmSignature() { + return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE)); } public void setRequiresRealmSignature(boolean val) { - client.setAttribute(SamlProtocol.SAML_SERVER_SIGNATURE, Boolean.toString(val)); + client.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val)); } - public boolean forcePostBinding(ClientModel client) { - return "true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING)); + public boolean forcePostBinding() { + return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING)); } public void setForcePostBinding(boolean val) { - client.setAttribute(SamlProtocol.SAML_FORCE_POST_BINDING, Boolean.toString(val)); + client.setAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING, Boolean.toString(val)); } - public boolean samlAssertionSignature(ClientModel client) { - return "true".equals(client.getAttribute(SamlProtocol.SAML_ASSERTION_SIGNATURE)); + public boolean requiresAssertionSignature() { + return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE)); } - public void setAssertionSignature(boolean val) { - client.setAttribute(SamlProtocol.SAML_ASSERTION_SIGNATURE , Boolean.toString(val)); + public void setRequiresAssertionSignature(boolean val) { + client.setAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE , Boolean.toString(val)); } - public boolean requiresEncryption(ClientModel client) { - return "true".equals(client.getAttribute(SamlProtocol.SAML_ENCRYPT)); + public boolean requiresEncryption() { + return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_ENCRYPT)); } public void setRequiresEncryption(boolean val) { - client.setAttribute(SamlProtocol.SAML_ENCRYPT, Boolean.toString(val)); + client.setAttribute(SamlConfigAttributes.SAML_ENCRYPT, Boolean.toString(val)); } - public boolean requiresClientSignature(ClientModel client) { - return "true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE)); + public boolean requiresClientSignature() { + return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE)); } public void setRequiresClientSignature(boolean val) { - client.setAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE , Boolean.toString(val)); + client.setAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE , Boolean.toString(val)); } public String getClientSigningCertificate() { - return client.getAttribute(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE); + return client.getAttribute(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE); } public void setClientSigningCertificate(String val) { - client.setAttribute(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, val); + client.setAttribute(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, val); } public String getClientSigningPrivateKey() { - return client.getAttribute(SAML_SIGNING_PRIVATE_KEY); + return client.getAttribute(SamlConfigAttributes.SAML_SIGNING_PRIVATE_KEY); } public void setClientSigningPrivateKey(String val) { - client.setAttribute(SAML_SIGNING_PRIVATE_KEY, val); + client.setAttribute(SamlConfigAttributes.SAML_SIGNING_PRIVATE_KEY, val); } - - - } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java index 4151d625a7..0ce4beba43 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java @@ -1,6 +1,5 @@ package org.keycloak.protocol.saml; -import org.keycloak.models.ClientModel; import org.keycloak.representations.idm.ClientRepresentation; /** @@ -16,45 +15,45 @@ public class SamlClientRepresentation { public String getCanonicalizationMethod() { if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); + return rep.getAttributes().get(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); } public String getSignatureAlgorithm() { if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlProtocol.SAML_SIGNATURE_ALGORITHM); + return rep.getAttributes().get(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM); } public String getNameIDFormat() { if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlProtocol.SAML_NAME_ID_FORMAT_ATTRIBUTE); + return rep.getAttributes().get(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE); } public String getIncludeAuthnStatement() { if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlProtocol.SAML_AUTHNSTATEMENT); + return rep.getAttributes().get(SamlConfigAttributes.SAML_AUTHNSTATEMENT); } public String getForceNameIDFormat() { if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlProtocol.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE); + return rep.getAttributes().get(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE); } public String getSamlServerSignature() { if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlProtocol.SAML_SERVER_SIGNATURE); + return rep.getAttributes().get(SamlConfigAttributes.SAML_SERVER_SIGNATURE); } public String getForcePostBinding() { if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlProtocol.SAML_FORCE_POST_BINDING); + return rep.getAttributes().get(SamlConfigAttributes.SAML_FORCE_POST_BINDING); } public String getClientSignature() { if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE); + return rep.getAttributes().get(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE); } } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java new file mode 100755 index 0000000000..eea258ac0b --- /dev/null +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java @@ -0,0 +1,22 @@ +package org.keycloak.protocol.saml; + +import org.keycloak.services.resources.admin.ClientAttributeCertificateResource; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public interface SamlConfigAttributes { + String SAML_SIGNING_PRIVATE_KEY = "saml.signing.private.key"; + String SAML_CANONICALIZATION_METHOD_ATTRIBUTE = "saml_signature_canonicalization_method"; + String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm"; + String SAML_NAME_ID_FORMAT_ATTRIBUTE = "saml_name_id_format"; + 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_FORCE_POST_BINDING = "saml.force.post.binding"; + String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature"; + String SAML_ENCRYPT = "saml.encrypt"; + String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature"; + String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE; +} diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java index 07c528ddd9..e7bd3ebd4d 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java @@ -70,28 +70,17 @@ public class SamlProtocol implements LoginProtocol { public static final String ATTRIBUTE_TRUE_VALUE = "true"; public static final String ATTRIBUTE_FALSE_VALUE = "false"; - public static final String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE; public static final String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.X509CERTIFICATE; - public static final String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature"; public static final String SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE = "saml_assertion_consumer_url_post"; public static final String SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE = "saml_assertion_consumer_url_redirect"; public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE = "saml_single_logout_service_url_post"; public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE = "saml_single_logout_service_url_redirect"; - public static final String SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE = "saml_force_name_id_format"; - public static final String SAML_NAME_ID_FORMAT_ATTRIBUTE = "saml_name_id_format"; - public static final String SAML_CANONICALIZATION_METHOD_ATTRIBUTE = "saml_signature_canonicalization_method"; public static final String LOGIN_PROTOCOL = "saml"; public static final String SAML_BINDING = "saml_binding"; public static final String SAML_IDP_INITIATED_LOGIN = "saml_idp_initiated_login"; public static final String SAML_POST_BINDING = "post"; public static final String SAML_SOAP_BINDING = "soap"; public static final String SAML_REDIRECT_BINDING = "get"; - public static final String SAML_SERVER_SIGNATURE = "saml.server.signature"; - public static final String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature"; - public static final String SAML_AUTHNSTATEMENT = "saml.authnstatement"; - public static final String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm"; - public static final String SAML_ENCRYPT = "saml.encrypt"; - public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding"; 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_REQUEST_ID = "SAML_LOGOUT_REQUEST_ID"; @@ -218,7 +207,8 @@ public class SamlProtocol implements LoginProtocol { protected boolean isPostBinding(ClientSessionModel clientSession) { ClientModel client = clientSession.getClient(); - return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING)) || forcePostBinding(client); + SamlClient samlClient = new SamlClient(client); + return SamlProtocol.SAML_POST_BINDING.equals(clientSession.getNote(SamlProtocol.SAML_BINDING)) || samlClient.forcePostBinding(); } public static boolean isLogoutPostBindingForInitiator(UserSessionModel session) { @@ -228,6 +218,7 @@ public class SamlProtocol implements LoginProtocol { protected boolean isLogoutPostBindingForClient(ClientSessionModel clientSession) { ClientModel client = clientSession.getClient(); + SamlClient samlClient = new SamlClient(client); String logoutPostUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE); String logoutRedirectUrl = client.getAttribute(SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE); @@ -238,7 +229,7 @@ public class SamlProtocol implements LoginProtocol { return false; } - if (forcePostBinding(client)) { + if (samlClient.forcePostBinding()) { return true; // configured to force a post binding and post binding logout url is not null } @@ -255,15 +246,11 @@ public class SamlProtocol implements LoginProtocol { } - public static boolean forcePostBinding(ClientModel client) { - return "true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING)); - } - - protected String getNameIdFormat(ClientSessionModel clientSession) { + protected String getNameIdFormat(SamlClient samlClient, ClientSessionModel clientSession) { String nameIdFormat = clientSession.getNote(GeneralConstants.NAMEID_FORMAT); - ClientModel client = clientSession.getClient(); - boolean forceFormat = forceNameIdFormat(client); - String configuredNameIdFormat = client.getAttribute(SAML_NAME_ID_FORMAT_ATTRIBUTE); + + boolean forceFormat = samlClient.forceNameIDFormat(); + String configuredNameIdFormat = samlClient.getNameIDFormat(); if ((nameIdFormat == null || forceFormat) && configuredNameIdFormat != null) { if (configuredNameIdFormat.equals("email")) { nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get(); @@ -282,11 +269,7 @@ public class SamlProtocol implements LoginProtocol { return nameIdFormat; } - public static boolean forceNameIdFormat(ClientModel client) { - return "true".equals(client.getAttribute(SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE)); - } - - protected String getNameId(String nameIdFormat, ClientSessionModel clientSession, UserSessionModel userSession) { + protected String getNameId(String nameIdFormat, ClientSessionModel clientSession, UserSessionModel userSession) { if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get())) { return userSession.getUser().getEmail(); } else if (nameIdFormat.equals(JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get())) { @@ -315,11 +298,12 @@ public class SamlProtocol implements LoginProtocol { public Response authenticated(UserSessionModel userSession, ClientSessionCode accessCode) { ClientSessionModel clientSession = accessCode.getClientSession(); ClientModel client = clientSession.getClient(); + SamlClient samlClient = new SamlClient(client); String requestID = clientSession.getNote(SAML_REQUEST_ID); String relayState = clientSession.getNote(GeneralConstants.RELAY_STATE); String redirectUri = clientSession.getRedirectUri(); String responseIssuer = getResponseIssuer(realm); - String nameIdFormat = getNameIdFormat(clientSession); + String nameIdFormat = getNameIdFormat(samlClient, clientSession); String nameId = getNameId(nameIdFormat, clientSession, userSession); // save NAME_ID and format in clientSession as they may be persistent or transient or email and not username @@ -330,7 +314,7 @@ public class SamlProtocol implements LoginProtocol { SAML2LoginResponseBuilder builder = new SAML2LoginResponseBuilder(); builder.requestID(requestID).destination(redirectUri).issuer(responseIssuer).assertionExpiration(realm.getAccessCodeLifespan()).subjectExpiration(realm.getAccessTokenLifespan()).sessionIndex(clientSession.getId()) .requestIssuer(clientSession.getClient().getClientId()).nameIdentifier(nameIdFormat, nameId).authMethod(JBossSAMLURIConstants.AC_UNSPECIFIED.get()); - if (!includeAuthnStatement(client)) { + if (!samlClient.includeAuthnStatement()) { builder.disableAuthnStatement(true); } @@ -370,21 +354,21 @@ public class SamlProtocol implements LoginProtocol { JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder(); bindingBuilder.relayState(relayState); - if (requiresRealmSignature(client)) { - String canonicalization = client.getAttribute(SAML_CANONICALIZATION_METHOD_ATTRIBUTE); + if (samlClient.requiresRealmSignature()) { + String canonicalization = samlClient.getCanonicalizationMethod(); if (canonicalization != null) { bindingBuilder.canonicalizationMethod(canonicalization); } - bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client)).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument(); + bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument(); } - if (requiresAssertionSignature(client)) { - String canonicalization = client.getAttribute(SAML_CANONICALIZATION_METHOD_ATTRIBUTE); + if (samlClient.requiresAssertionSignature()) { + String canonicalization = samlClient.getCanonicalizationMethod(); if (canonicalization != null) { bindingBuilder.canonicalizationMethod(canonicalization); } - bindingBuilder.signatureAlgorithm(getSignatureAlgorithm(client)).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signAssertions(); + bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signAssertions(); } - if (requiresEncryption(client)) { + if (samlClient.requiresEncryption()) { PublicKey publicKey = null; try { publicKey = SamlProtocolUtils.getEncryptionValidationKey(client); @@ -410,32 +394,6 @@ public class SamlProtocol implements LoginProtocol { } } - public static boolean requiresRealmSignature(ClientModel client) { - return "true".equals(client.getAttribute(SAML_SERVER_SIGNATURE)); - } - - public static boolean requiresAssertionSignature(ClientModel client) { - return "true".equals(client.getAttribute(SAML_ASSERTION_SIGNATURE)); - } - - public static boolean includeAuthnStatement(ClientModel client) { - return "true".equals(client.getAttribute(SAML_AUTHNSTATEMENT)); - } - - public static SignatureAlgorithm getSignatureAlgorithm(ClientModel client) { - String alg = client.getAttribute(SAML_SIGNATURE_ALGORITHM); - if (alg != null) { - SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg); - if (algorithm != null) - return algorithm; - } - return SignatureAlgorithm.RSA_SHA256; - } - - private boolean requiresEncryption(ClientModel client) { - return "true".equals(client.getAttribute(SAML_ENCRYPT)); - } - public static class ProtocolMapperProcessor { final public T mapper; final public ProtocolMapperModel model; @@ -499,19 +457,20 @@ public class SamlProtocol implements LoginProtocol { @Override public Response frontchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) { ClientModel client = clientSession.getClient(); + SamlClient samlClient = new SamlClient(client); if (!(client instanceof ClientModel)) return null; try { if (isLogoutPostBindingForClient(clientSession)) { String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING); SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client); - JaxrsSAML2BindingBuilder binding = createBindingBuilder(client); + 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); - JaxrsSAML2BindingBuilder binding = createBindingBuilder(client); + JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient); return binding.redirectBinding(logoutBuilder.buildDocument()).request(bindingUri); } } catch (ConfigurationException e) { @@ -574,6 +533,7 @@ public class SamlProtocol implements LoginProtocol { @Override public void backchannelLogout(UserSessionModel userSession, ClientSessionModel clientSession) { ClientModel client = clientSession.getClient(); + SamlClient samlClient = new SamlClient(client); String logoutUrl = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING); if (logoutUrl == null) { logger.warnv("Can't do backchannel logout. No SingleLogoutService POST Binding registered for client: {1}", client.getClientId()); @@ -583,7 +543,7 @@ public class SamlProtocol implements LoginProtocol { String logoutRequestString = null; try { - JaxrsSAML2BindingBuilder binding = createBindingBuilder(client); + JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient); logoutRequestString = binding.postBinding(logoutBuilder.buildDocument()).encoded(); } catch (Exception e) { logger.warn("failed to send saml logout", e); @@ -636,10 +596,10 @@ public class SamlProtocol implements LoginProtocol { return logoutBuilder; } - private JaxrsSAML2BindingBuilder createBindingBuilder(ClientModel client) { + private JaxrsSAML2BindingBuilder createBindingBuilder(SamlClient samlClient) { JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(); - if (requiresRealmSignature(client)) { - binding.signatureAlgorithm(getSignatureAlgorithm(client)).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument(); + if (samlClient.requiresRealmSignature()) { + binding.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument(); } return binding; } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java index f7b4d1e97b..6ec5a1abd3 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java @@ -3,6 +3,7 @@ package org.keycloak.protocol.saml; import org.keycloak.Config; import org.keycloak.events.EventBuilder; import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientTemplateModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; @@ -14,6 +15,7 @@ import org.keycloak.protocol.saml.mappers.RoleListMapper; import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper; import org.keycloak.representations.idm.CertificateRepresentation; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.saml.SignatureAlgorithm; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java index d625f2a740..5742f7d1a3 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java @@ -25,7 +25,8 @@ public class SamlProtocolUtils { public static void verifyDocumentSignature(ClientModel client, Document document) throws VerificationException { - if (!"true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) { + SamlClient samlClient = new SamlClient(client); + if (!samlClient.requiresClientSignature()) { return; } PublicKey publicKey = getSignatureValidationKey(client); @@ -44,7 +45,7 @@ public class SamlProtocolUtils { } public static PublicKey getSignatureValidationKey(ClientModel client) throws VerificationException { - return getPublicKey(client, SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE); + return getPublicKey(new SamlClient(client).getClientSigningCertificate()); } public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException { @@ -53,6 +54,10 @@ public class SamlProtocolUtils { public static PublicKey getPublicKey(ClientModel client, String attribute) throws VerificationException { String certPem = client.getAttribute(attribute); + return getPublicKey(certPem); + } + + private static PublicKey getPublicKey(String certPem) throws VerificationException { if (certPem == null) throw new VerificationException("Client does not have a public key."); Certificate cert = null; try { diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java index f9aa30b148..30017145d5 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java @@ -193,6 +193,7 @@ public class SamlService extends AuthorizationEndpointBase { protected abstract SAMLDocumentHolder extractResponseDocument(String response); protected Response loginRequest(String relayState, AuthnRequestType requestAbstractType, ClientModel client) { + SamlClient samlClient = new SamlClient(client); // validate destination if (requestAbstractType.getDestination() != null && !uriInfo.getAbsolutePath().equals(requestAbstractType.getDestination())) { event.detail(Details.REASON, "invalid_destination"); @@ -200,7 +201,7 @@ public class SamlService extends AuthorizationEndpointBase { return ErrorPage.error(session, Messages.INVALID_REQUEST); } String bindingType = getBindingType(requestAbstractType); - if ("true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING))) + if (samlClient.forcePostBinding()) bindingType = SamlProtocol.SAML_POST_BINDING; String redirect = null; URI redirectUri = requestAbstractType.getAssertionConsumerServiceURL(); @@ -234,7 +235,7 @@ public class SamlService extends AuthorizationEndpointBase { // Handle NameIDPolicy from SP NameIDPolicyType nameIdPolicy = requestAbstractType.getNameIDPolicy(); - if (nameIdPolicy != null && !SamlProtocol.forceNameIdFormat(client)) { + if (nameIdPolicy != null && !samlClient.forceNameIDFormat()) { String nameIdFormat = nameIdPolicy.getFormat().toString(); // TODO: Handle AllowCreate too, relevant for persistent NameID. if (isSupportedNameIdFormat(nameIdFormat)) { @@ -274,6 +275,7 @@ public class SamlService extends AuthorizationEndpointBase { protected abstract String getBindingType(); protected Response logoutRequest(LogoutRequestType logoutRequest, ClientModel client, String relayState) { + SamlClient samlClient = new SamlClient(client); // validate destination if (logoutRequest.getDestination() != null && !uriInfo.getAbsolutePath().equals(logoutRequest.getDestination())) { event.detail(Details.REASON, "invalid_destination"); @@ -285,20 +287,20 @@ public class SamlService extends AuthorizationEndpointBase { AuthenticationManager.AuthResult authResult = authManager.authenticateIdentityCookie(session, realm, false); if (authResult != null) { String logoutBinding = getBindingType(); - if ("true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING))) + if ("true".equals(samlClient.forcePostBinding())) logoutBinding = SamlProtocol.SAML_POST_BINDING; String bindingUri = SamlProtocol.getLogoutServiceUrl(uriInfo, client, logoutBinding); UserSessionModel userSession = authResult.getSession(); userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING_URI, bindingUri); - if (SamlProtocol.requiresRealmSignature(client)) { - userSession.setNote(SamlProtocol.SAML_LOGOUT_SIGNATURE_ALGORITHM, SamlProtocol.getSignatureAlgorithm(client).toString()); + if (samlClient.requiresRealmSignature()) { + userSession.setNote(SamlProtocol.SAML_LOGOUT_SIGNATURE_ALGORITHM, samlClient.getSignatureAlgorithm().toString()); } if (relayState != null) 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_CANONICALIZATION, client.getAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE)); + userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, samlClient.getCanonicalizationMethod()); userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL); // remove client from logout requests for (ClientSessionModel clientSession : userSession.getClientSessions()) { @@ -348,8 +350,8 @@ public class SamlService extends AuthorizationEndpointBase { builder.destination(logoutBindingUri); builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString()); JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder().relayState(logoutRelayState); - if (SamlProtocol.requiresRealmSignature(client)) { - SignatureAlgorithm algorithm = SamlProtocol.getSignatureAlgorithm(client); + if (samlClient.requiresRealmSignature()) { + SignatureAlgorithm algorithm = samlClient.getSignatureAlgorithm(); binding.signatureAlgorithm(algorithm).signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate()).signDocument(); } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileProtocolFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileProtocolFactory.java old mode 100644 new mode 100755 index dba1b295b9..81e05c2032 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileProtocolFactory.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileProtocolFactory.java @@ -9,6 +9,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.LoginProtocol; import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder; +import org.keycloak.protocol.saml.SamlClient; import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.protocol.saml.SamlProtocolFactory; import org.keycloak.protocol.saml.profile.ecp.util.Soap; @@ -69,8 +70,9 @@ public class SamlEcpProfileProtocolFactory extends SamlProtocolFactory { private void createRequestAuthenticatedHeader(ClientSessionModel clientSession, SoapMessageBuilder messageBuilder) { ClientModel client = clientSession.getClient(); + SamlClient samlClient = new SamlClient(client); - if ("true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) { + if (samlClient.requiresClientSignature()) { SOAPHeaderElement ecpRequestAuthenticated = messageBuilder.addHeader(JBossSAMLConstants.REQUEST_AUTHENTICATED.get(), NS_PREFIX_PROFILE_ECP); ecpRequestAuthenticated.setMustUnderstand(true); From 3bacbdf6ff5007c5aca0bb4719202e71741b7514 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Mon, 4 Jan 2016 17:13:15 -0500 Subject: [PATCH 2/2] set framework for template config --- .../idm/ClientTemplateRepresentation.java | 82 +++++++++++ .../keycloak/models/ClientConfigResolver.java | 55 ++++++++ .../models/utils/RepresentationToModel.java | 36 ++++- .../keycloak/protocol/saml/SamlClient.java | 36 ++--- .../saml/SamlClientRepresentation.java | 59 -------- .../protocol/saml/SamlClientTemplate.java | 131 ++++++++++++++++++ .../protocol/saml/SamlProtocolFactory.java | 46 +++++- .../saml/SamlRepresentationAttributes.java | 65 +++++++++ .../keycloak/protocol/saml/SamlService.java | 3 +- .../profile/ecp/SamlEcpProfileService.java | 3 +- .../protocol/LoginProtocolFactory.java | 12 +- .../oidc/OIDCLoginProtocolFactory.java | 7 + 12 files changed, 449 insertions(+), 86 deletions(-) create mode 100755 model/api/src/main/java/org/keycloak/models/ClientConfigResolver.java delete mode 100755 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java create mode 100755 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java create mode 100755 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java mode change 100644 => 100755 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java diff --git a/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java index dc575c4109..e478c27600 100755 --- a/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/idm/ClientTemplateRepresentation.java @@ -17,6 +17,16 @@ public class ClientTemplateRepresentation { protected String description; protected String protocol; protected Boolean fullScopeAllowed; + protected Boolean bearerOnly; + protected Boolean consentRequired; + protected Boolean standardFlowEnabled; + protected Boolean implicitFlowEnabled; + protected Boolean directAccessGrantsEnabled; + protected Boolean serviceAccountsEnabled; + protected Boolean publicClient; + protected Boolean frontchannelLogout; + protected Map attributes; + protected List protocolMappers; public String getId() { @@ -67,4 +77,76 @@ public class ClientTemplateRepresentation { public void setFullScopeAllowed(Boolean fullScopeAllowed) { this.fullScopeAllowed = fullScopeAllowed; } + + public Boolean isBearerOnly() { + return bearerOnly; + } + + public void setBearerOnly(Boolean bearerOnly) { + this.bearerOnly = bearerOnly; + } + + public Boolean isConsentRequired() { + return consentRequired; + } + + public void setConsentRequired(Boolean consentRequired) { + this.consentRequired = consentRequired; + } + + public Boolean isStandardFlowEnabled() { + return standardFlowEnabled; + } + + public void setStandardFlowEnabled(Boolean standardFlowEnabled) { + this.standardFlowEnabled = standardFlowEnabled; + } + + public Boolean isImplicitFlowEnabled() { + return implicitFlowEnabled; + } + + public void setImplicitFlowEnabled(Boolean implicitFlowEnabled) { + this.implicitFlowEnabled = implicitFlowEnabled; + } + + public Boolean isDirectAccessGrantsEnabled() { + return directAccessGrantsEnabled; + } + + public void setDirectAccessGrantsEnabled(Boolean directAccessGrantsEnabled) { + this.directAccessGrantsEnabled = directAccessGrantsEnabled; + } + + public Boolean isServiceAccountsEnabled() { + return serviceAccountsEnabled; + } + + public void setServiceAccountsEnabled(Boolean serviceAccountsEnabled) { + this.serviceAccountsEnabled = serviceAccountsEnabled; + } + + public Boolean isPublicClient() { + return publicClient; + } + + public void setPublicClient(Boolean publicClient) { + this.publicClient = publicClient; + } + + public Boolean isFrontchannelLogout() { + return frontchannelLogout; + } + + public void setFrontchannelLogout(Boolean frontchannelLogout) { + this.frontchannelLogout = frontchannelLogout; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } } diff --git a/model/api/src/main/java/org/keycloak/models/ClientConfigResolver.java b/model/api/src/main/java/org/keycloak/models/ClientConfigResolver.java new file mode 100755 index 0000000000..8ec8728da7 --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/ClientConfigResolver.java @@ -0,0 +1,55 @@ +package org.keycloak.models; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class ClientConfigResolver { + protected ClientModel client; + protected ClientTemplateModel clientTemplate; + + public ClientConfigResolver(ClientModel client) { + this.client = client; + this.clientTemplate = client.getClientTemplate(); + } + + public String resolveAttribute(String name) { + if (clientTemplate != null && client.useTemplateConfig()) { + return clientTemplate.getAttribute(name); + } else { + return client.getAttribute(name); + } + } + + public boolean isFrontchannelLogout() { + if (clientTemplate != null && client.useTemplateConfig()) { + return clientTemplate.isFrontchannelLogout(); + } + + return client.isFrontchannelLogout(); + } + + boolean isConsentRequired() { + if (clientTemplate != null && client.useTemplateConfig()) { + return clientTemplate.isConsentRequired(); + } + + return client.isConsentRequired(); + } + + boolean isStandardFlowEnabled() { + if (clientTemplate != null && client.useTemplateConfig()) { + return clientTemplate.isStandardFlowEnabled(); + } + + return client.isStandardFlowEnabled(); + } + + boolean isServiceAccountsEnabled() { + if (clientTemplate != null && client.useTemplateConfig()) { + return clientTemplate.isServiceAccountsEnabled(); + } + + return client.isServiceAccountsEnabled(); + } +} diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java index 2432fc1274..3b2cfdc5db 100755 --- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java +++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java @@ -903,7 +903,7 @@ public class RepresentationToModel { } } if (resourceRep.isUseTemplateConfig() != null) client.setUseTemplateConfig(resourceRep.isUseTemplateConfig()); - else client.setUseTemplateConfig(resourceRep.getClientTemplate() != null); + else client.setUseTemplateConfig(false); // default to false for now if (resourceRep.isUseTemplateScope() != null) client.setUseTemplateScope(resourceRep.isUseTemplateScope()); else client.setUseTemplateScope(resourceRep.getClientTemplate() != null); @@ -1022,6 +1022,23 @@ public class RepresentationToModel { client.addProtocolMapper(toModel(mapper)); } } + if (resourceRep.isBearerOnly() != null) client.setBearerOnly(resourceRep.isBearerOnly()); + if (resourceRep.isConsentRequired() != null) client.setConsentRequired(resourceRep.isConsentRequired()); + + if (resourceRep.isStandardFlowEnabled() != null) client.setStandardFlowEnabled(resourceRep.isStandardFlowEnabled()); + if (resourceRep.isImplicitFlowEnabled() != null) client.setImplicitFlowEnabled(resourceRep.isImplicitFlowEnabled()); + if (resourceRep.isDirectAccessGrantsEnabled() != null) client.setDirectAccessGrantsEnabled(resourceRep.isDirectAccessGrantsEnabled()); + if (resourceRep.isServiceAccountsEnabled() != null) client.setServiceAccountsEnabled(resourceRep.isServiceAccountsEnabled()); + + if (resourceRep.isPublicClient() != null) client.setPublicClient(resourceRep.isPublicClient()); + if (resourceRep.isFrontchannelLogout() != null) client.setFrontchannelLogout(resourceRep.isFrontchannelLogout()); + + if (resourceRep.getAttributes() != null) { + for (Map.Entry entry : resourceRep.getAttributes().entrySet()) { + client.setAttribute(entry.getKey(), entry.getValue()); + } + } + return client; } @@ -1035,6 +1052,23 @@ public class RepresentationToModel { if (rep.getProtocol() != null) resource.setProtocol(rep.getProtocol()); + + if (rep.isBearerOnly() != null) resource.setBearerOnly(rep.isBearerOnly()); + if (rep.isConsentRequired() != null) resource.setConsentRequired(rep.isConsentRequired()); + if (rep.isStandardFlowEnabled() != null) resource.setStandardFlowEnabled(rep.isStandardFlowEnabled()); + if (rep.isImplicitFlowEnabled() != null) resource.setImplicitFlowEnabled(rep.isImplicitFlowEnabled()); + if (rep.isDirectAccessGrantsEnabled() != null) resource.setDirectAccessGrantsEnabled(rep.isDirectAccessGrantsEnabled()); + if (rep.isServiceAccountsEnabled() != null) resource.setServiceAccountsEnabled(rep.isServiceAccountsEnabled()); + if (rep.isPublicClient() != null) resource.setPublicClient(rep.isPublicClient()); + if (rep.isFullScopeAllowed() != null) resource.setFullScopeAllowed(rep.isFullScopeAllowed()); + if (rep.isFrontchannelLogout() != null) resource.setFrontchannelLogout(rep.isFrontchannelLogout()); + + if (rep.getAttributes() != null) { + for (Map.Entry entry : rep.getAttributes().entrySet()) { + resource.setAttribute(entry.getKey(), entry.getValue()); + } + } + } public static long getClaimsMask(ClaimRepresentation rep) { diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java index df27de2fbc..d935f83e2a 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java @@ -1,5 +1,6 @@ package org.keycloak.protocol.saml; +import org.keycloak.models.ClientConfigResolver; import org.keycloak.models.ClientModel; import org.keycloak.saml.SignatureAlgorithm; @@ -7,24 +8,14 @@ import org.keycloak.saml.SignatureAlgorithm; * @author Bill Burke * @version $Revision: 1 $ */ -public class SamlClient { - protected ClientModel client; +public class SamlClient extends ClientConfigResolver { public SamlClient(ClientModel client) { - this.client = client; + super(client); } - public String getId() { - return client.getId(); - } - - public String getClientId() { - return client.getClientId(); - } -// - public String getCanonicalizationMethod() { - return client.getAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); + return resolveAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); } public void setCanonicalizationMethod(String value) { @@ -32,7 +23,7 @@ public class SamlClient { } public SignatureAlgorithm getSignatureAlgorithm() { - String alg = client.getAttribute(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM); + String alg = resolveAttribute(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM); if (alg != null) { SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg); if (algorithm != null) @@ -46,14 +37,14 @@ public class SamlClient { } public String getNameIDFormat() { - return client.getAttributes().get(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE); + return resolveAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE); } public void setNameIDFormat(String format) { client.setAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, format); } public boolean includeAuthnStatement() { - return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT)); + return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT)); } public void setIncludeAuthnStatement(boolean val) { @@ -61,7 +52,7 @@ public class SamlClient { } public boolean forceNameIDFormat() { - return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE)); + return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE)); } public void setForceNameIDFormat(boolean val) { @@ -69,7 +60,7 @@ public class SamlClient { } public boolean requiresRealmSignature() { - return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE)); + return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE)); } public void setRequiresRealmSignature(boolean val) { @@ -78,7 +69,7 @@ public class SamlClient { } public boolean forcePostBinding() { - return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING)); + return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING)); } public void setForcePostBinding(boolean val) { @@ -86,7 +77,7 @@ public class SamlClient { } public boolean requiresAssertionSignature() { - return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE)); + return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE)); } public void setRequiresAssertionSignature(boolean val) { @@ -94,7 +85,7 @@ public class SamlClient { } public boolean requiresEncryption() { - return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_ENCRYPT)); + return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_ENCRYPT)); } @@ -104,7 +95,7 @@ public class SamlClient { } public boolean requiresClientSignature() { - return "true".equals(client.getAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE)); + return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE)); } public void setRequiresClientSignature(boolean val) { @@ -129,4 +120,5 @@ public class SamlClient { client.setAttribute(SamlConfigAttributes.SAML_SIGNING_PRIVATE_KEY, val); } + } diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java deleted file mode 100755 index 0ce4beba43..0000000000 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.keycloak.protocol.saml; - -import org.keycloak.representations.idm.ClientRepresentation; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class SamlClientRepresentation { - protected ClientRepresentation rep; - - public SamlClientRepresentation(ClientRepresentation rep) { - this.rep = rep; - } - - public String getCanonicalizationMethod() { - if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); - } - - public String getSignatureAlgorithm() { - if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM); - } - - public String getNameIDFormat() { - if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE); - - } - - public String getIncludeAuthnStatement() { - if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlConfigAttributes.SAML_AUTHNSTATEMENT); - - } - - public String getForceNameIDFormat() { - if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE); - } - - public String getSamlServerSignature() { - if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlConfigAttributes.SAML_SERVER_SIGNATURE); - - } - - public String getForcePostBinding() { - if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlConfigAttributes.SAML_FORCE_POST_BINDING); - - } - public String getClientSignature() { - if (rep.getAttributes() == null) return null; - return rep.getAttributes().get(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE); - - } -} diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java new file mode 100755 index 0000000000..ae6d491795 --- /dev/null +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java @@ -0,0 +1,131 @@ +package org.keycloak.protocol.saml; + +import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientTemplateModel; +import org.keycloak.saml.SignatureAlgorithm; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SamlClientTemplate { + protected ClientTemplateModel clientTemplate; + + public SamlClientTemplate(ClientTemplateModel template) { + this.clientTemplate = template; + } + + public String getId() { + return clientTemplate.getId(); + } + +// + + public String getCanonicalizationMethod() { + return clientTemplate.getAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); + } + + public void setCanonicalizationMethod(String value) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE, value); + } + + public SignatureAlgorithm getSignatureAlgorithm() { + String alg = null; + alg = clientTemplate.getAttribute(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); + if (alg != null) { + SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg); + if (algorithm != null) + return algorithm; + } + return SignatureAlgorithm.RSA_SHA256; + } + + public void setSignatureAlgorithm(SignatureAlgorithm algorithm) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, algorithm.name()); + } + + public String getNameIDFormat() { + return clientTemplate.getAttributes().get(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE); + } + public void setNameIDFormat(String format) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, format); + } + + public boolean includeAuthnStatement() { + return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT)); + } + + public void setIncludeAuthnStatement(boolean val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, Boolean.toString(val)); + } + + public boolean forceNameIDFormat() { + return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE)); + + } + public void setForceNameIDFormat(boolean val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, Boolean.toString(val)); + } + + public boolean requiresRealmSignature() { + return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE)); + } + + public void setRequiresRealmSignature(boolean val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val)); + + } + + public boolean forcePostBinding() { + return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING)); + } + + public void setForcePostBinding(boolean val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_FORCE_POST_BINDING, Boolean.toString(val)); + + } + public boolean requiresAssertionSignature() { + return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE)); + } + + public void setRequiresAssertionSignature(boolean val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_ASSERTION_SIGNATURE , Boolean.toString(val)); + + } + public boolean requiresEncryption() { + return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_ENCRYPT)); + } + + + public void setRequiresEncryption(boolean val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_ENCRYPT, Boolean.toString(val)); + + } + + public boolean requiresClientSignature() { + return "true".equals(clientTemplate.getAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE)); + } + + public void setRequiresClientSignature(boolean val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE , Boolean.toString(val)); + + } + + public String getClientSigningCertificate() { + return clientTemplate.getAttribute(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE); + } + + public void setClientSigningCertificate(String val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, val); + + } + + public String getClientSigningPrivateKey() { + return clientTemplate.getAttribute(SamlConfigAttributes.SAML_SIGNING_PRIVATE_KEY); + } + + public void setClientSigningPrivateKey(String val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_SIGNING_PRIVATE_KEY, val); + + } +} diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java index 6ec5a1abd3..1de9f456aa 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java @@ -105,7 +105,7 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory { @Override public void setupClientDefaults(ClientRepresentation clientRep, ClientModel newClient) { - SamlClientRepresentation rep = new SamlClientRepresentation(clientRep); + SamlRepresentationAttributes rep = new SamlRepresentationAttributes(clientRep.getAttributes()); SamlClient client = new SamlClient(newClient); if (clientRep.isStandardFlowEnabled() == null) newClient.setStandardFlowEnabled(true); if (rep.getCanonicalizationMethod() == null) { @@ -136,9 +136,53 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory { if (rep.getClientSignature() == null) { client.setRequiresClientSignature(true); + } + + if (client.requiresClientSignature() && client.getClientSigningCertificate() == null) { CertificateRepresentation info = KeycloakModelUtils.generateKeyPairCertificate(newClient.getClientId()); client.setClientSigningCertificate(info.getCertificate()); client.setClientSigningPrivateKey(info.getPrivateKey()); + + } + + if (clientRep.isFrontchannelLogout() == null) { + newClient.setFrontchannelLogout(true); + } + } + + @Override + public void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient) { + SamlRepresentationAttributes rep = new SamlRepresentationAttributes(clientRep.getAttributes()); + SamlClientTemplate client = new SamlClientTemplate(newClient); + if (clientRep.isStandardFlowEnabled() == null) newClient.setStandardFlowEnabled(true); + if (rep.getCanonicalizationMethod() == null) { + client.setCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE); + } + if (rep.getSignatureAlgorithm() == null) { + client.setSignatureAlgorithm(SignatureAlgorithm.RSA_SHA256); + } + + if (rep.getNameIDFormat() == null) { + client.setNameIDFormat("username"); + } + + if (rep.getIncludeAuthnStatement() == null) { + client.setIncludeAuthnStatement(true); + } + + if (rep.getForceNameIDFormat() == null) { + client.setForceNameIDFormat(false); + } + + if (rep.getSamlServerSignature() == null) { + client.setRequiresRealmSignature(true); + } + if (rep.getForcePostBinding() == null) { + client.setForcePostBinding(true); + } + + if (rep.getClientSignature() == null) { + client.setRequiresClientSignature(true); } if (clientRep.isFrontchannelLogout() == null) { diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java new file mode 100755 index 0000000000..5933773d3d --- /dev/null +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java @@ -0,0 +1,65 @@ +package org.keycloak.protocol.saml; + +import org.keycloak.representations.idm.ClientRepresentation; + +import java.util.Map; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class SamlRepresentationAttributes { + protected Map attributes; + + public SamlRepresentationAttributes(Map attributes) { + this.attributes = attributes; + } + + public String getCanonicalizationMethod() { + if (getAttributes() == null) return null; + return getAttributes().get(SamlConfigAttributes.SAML_CANONICALIZATION_METHOD_ATTRIBUTE); + } + + protected Map getAttributes() { + return attributes; + } + + public String getSignatureAlgorithm() { + if (getAttributes() == null) return null; + return getAttributes().get(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM); + } + + public String getNameIDFormat() { + if (getAttributes() == null) return null; + return getAttributes().get(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE); + + } + + public String getIncludeAuthnStatement() { + if (getAttributes() == null) return null; + return getAttributes().get(SamlConfigAttributes.SAML_AUTHNSTATEMENT); + + } + + public String getForceNameIDFormat() { + if (getAttributes() == null) return null; + return getAttributes().get(SamlConfigAttributes.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE); + } + + public String getSamlServerSignature() { + if (getAttributes() == null) return null; + return getAttributes().get(SamlConfigAttributes.SAML_SERVER_SIGNATURE); + + } + + public String getForcePostBinding() { + if (getAttributes() == null) return null; + return getAttributes().get(SamlConfigAttributes.SAML_FORCE_POST_BINDING); + + } + public String getClientSignature() { + if (getAttributes() == null) return null; + return getAttributes().get(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE); + + } +} diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java index a09dd390cd..bd6e84693f 100755 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlService.java @@ -412,7 +412,8 @@ public class SamlService extends AuthorizationEndpointBase { @Override protected void verifySignature(SAMLDocumentHolder documentHolder, ClientModel client) throws VerificationException { - if (!"true".equals(client.getAttribute("saml.client.signature"))) { + SamlClient samlClient = new SamlClient(client); + if (!samlClient.requiresClientSignature()) { return; } PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client); diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java old mode 100644 new mode 100755 index 52b855702b..f2623464b9 --- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java +++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/profile/ecp/SamlEcpProfileService.java @@ -9,6 +9,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder; +import org.keycloak.protocol.saml.SamlConfigAttributes; import org.keycloak.protocol.saml.SamlProtocol; import org.keycloak.protocol.saml.SamlService; import org.keycloak.protocol.saml.profile.ecp.util.Soap; @@ -99,7 +100,7 @@ public class SamlEcpProfileService extends SamlService { private void createRequestAuthenticatedHeader(ClientSessionModel clientSession, Soap.SoapMessageBuilder messageBuilder) { ClientModel client = clientSession.getClient(); - if ("true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) { + if ("true".equals(client.getAttribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE))) { SOAPHeaderElement ecpRequestAuthenticated = messageBuilder.addHeader(JBossSAMLConstants.REQUEST_AUTHENTICATED.get(), NS_PREFIX_PROFILE_ECP); ecpRequestAuthenticated.setMustUnderstand(true); diff --git a/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java index a876b194b5..2238e6f21f 100755 --- a/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java +++ b/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java @@ -2,10 +2,12 @@ package org.keycloak.protocol; import org.keycloak.events.EventBuilder; import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientTemplateModel; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; import org.keycloak.provider.ProviderFactory; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.services.managers.AuthenticationManager; import java.util.List; @@ -31,10 +33,18 @@ public interface LoginProtocolFactory extends ProviderFactory { Object createProtocolEndpoint(RealmModel realm, EventBuilder event, AuthenticationManager authManager); /** - * Setup default values for new clients. + * Setup default values for new clients. This expects that the representation has already set up the client * * @param rep * @param newClient */ void setupClientDefaults(ClientRepresentation rep, ClientModel newClient); + + /** + * Setup default values for new templates. This expects that the representation has already set up the template + * + * @param clientRep + * @param newClient + */ + void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient); } diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java index 8cd0d8fed5..ee710cec48 100755 --- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java @@ -21,6 +21,7 @@ import org.keycloak.common.constants.KerberosConstants; import org.keycloak.common.util.UriUtils; import org.keycloak.events.EventBuilder; import org.keycloak.models.ClientModel; +import org.keycloak.models.ClientTemplateModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; @@ -32,6 +33,7 @@ import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.UserPropertyMapper; import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ClientTemplateRepresentation; import org.keycloak.services.managers.AuthenticationManager; import java.util.ArrayList; @@ -206,4 +208,9 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory { if (rep.isPublicClient() == null) newClient.setPublicClient(true); if (rep.isFrontchannelLogout() == null) newClient.setFrontchannelLogout(false); } + + @Override + public void setupTemplateDefaults(ClientTemplateRepresentation clientRep, ClientTemplateModel newClient) { + + } }