diff --git a/distribution/docs-dist/assembly.xml b/distribution/docs-dist/assembly.xml index 9eb9c71ae0..00862c28ea 100755 --- a/distribution/docs-dist/assembly.xml +++ b/distribution/docs-dist/assembly.xml @@ -17,11 +17,11 @@ rest-api - ../../docbook/target/auth-server-docs/publish/en-US + ../../docbook/auth-server-docs/target/docbook/publish/en-US userguide/keycloak-server - ../../docbook/target/saml-adapter-docs/publish/en-US + ../../docbook/saml-adapter-docs/target/docbook/publish/en-US userguide/saml-client-adapter diff --git a/distribution/docs-dist/src/index.html b/distribution/docs-dist/src/index.html index 891f7786a6..f196cd56ac 100755 --- a/distribution/docs-dist/src/index.html +++ b/distribution/docs-dist/src/index.html @@ -1,8 +1,11 @@

Keyloak Documentation

\ No newline at end of file diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml index eeb303114d..b112aa09bf 100755 --- a/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml +++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml @@ -5,380 +5,471 @@ look like: - + + + + + + + + + + + + + + + - "connection-pool-size" : 20, - "disable-trust-manager": false, - "allow-any-hostname" : false, - "truststore" : "path/to/truststore.jks", - "truststore-password" : "geheim", - "client-keystore" : "path/to/client-keystore.jks", - "client-keystore-password" : "geheim", - "client-key-password" : "geheim" -}]]> + + + + + + + + + + +]]> Some of these configuration switches may be adapter specific and some are common across all adapters. For Java adapters you can use ${...} enclosure as System property replacement. - For example ${jboss.server.config.dir}. Also, you can obtain a template - for this config file from the admin console. Go to the realm and select the application you want a template for. - Go to the Installation tab and this will provide you with a template that includes - the public key of the realm. + For example ${jboss.server.config.dir}. +
+ SP Element + + Here is the explanation of the SP element attributes + + +... +]]> + + + + entityID + + + This is the identifier for this client. The IDP needs this value to determine + who the client is that is communicating with it. + REQUIRED. + + + + + sslPolicy + + + This is the SSL policy the adapter will enforce. Valid values are: + ALL, EXTERNAL, and NONE. For ALL, all requests must come in via HTTPS. For + EXTERNAL, only non-private IP addresses must come over the wire via HTTPS. For + NONE, no requests are required to come over via HTTPS. This is + OPTIONAL. and defaults to EXTERNAL. + + + + + nameIDPolicyFormat + + + SAML clients can request a specific NameID Subject format. Fill in this value + if you want a specific format. It must be a standard SAML format identifier, i.e. + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + OPTIONAL.. By default, no special format is requested. + + + + + forceAuthentication + + + SAML clients can request that a user is re-authenticated even if + they are already logged in at the IDP. Set this to true if you + want this. + OPTIONAL.. Set to false by default. + + + + + +
+
+ SP Keys and Key elements + + If the IDP requires that the SP sign all of its requests and/or if the IDP will + encrypt assertions, you must define the keys used to do this. For client signed + documents you must define both the private and public key or certificate that will + be used to sign documents. For encryption, you only have to define the private key + that will be used to decrypt. + + + There are two ways to describe your keys. Either they are stored within a Java KeyStore + or you can cut and paste the keys directly within keycloak-saml.xml + in the PEM format. + + + + + + + + + +]]> + + + The Key element has two optional attributes signing + and encryption. When set to true these tell the adapter what the + key will be used for. If both attributes are set to true, then the key will be used for both + signing documents and decrypting encrypted assertions. You must set at least one of these + attributes to true. + +
+ KeyStore element + + + + file + + + File path to the key store. + OPTIONAL. The file or resource attribute + must be set. + + + + + resource + + + WAR resource path to the KeyStore. This is a path used in method call to ServletContext.getResourceAsStream(). + OPTIONAL. The file or resource attribute + must be set. + + + + + password + + + The password of the KeyStore + REQUIRED. + + + + + + + + You can and must also specify references to your private keys and certificates within + the Java KeyStore. The PrivateKey and Certificate + elements do this. The alias attribute defines the alias within the + KeyStore for the key. For PrivateKey, a password is required to access this key + specify that value in the password attribute. + +
+
+ Key PEMS + + Within the Key element you alternatively declare your keys and certificates + directly using the sub elements PrivateKeyPem, PublicKeyPem, and + CertificatePem. The values contained in these elements must conform to the + PEM key format. You usually use this option if you are generating keys using openssl + +
+
+
+ SP PrincipalNameMapping element + + This element is optional. When creating a Java Principal object that you obtain from + methods like HttpServletRequest.getUserPrincipal(), you can define what name that is returned + by the Principal.getName() method. The policy attribute defines the + policy used to populate this value. The values are FROM_NAME_ID. This policy + just grabs whatever the SAML subject value is. The other is FROM_ATTRIBUTE. This will + pull the value of Principal.getName() from one of the attributes in the SAML assertion received from the server. + The default value is FROM_NAME_ID. + +
+
+ RoleIdentifiers element + + + + + +]]> + + This element is optional. It defines which SAML attribute values in the assertion should be + mapped to a Java EE role. By default Role attribute values are converted + to Java EE roles. Some IDPs send roles via a member or memberOf + attribute assertion. You define one or more Attribute elements to specify + which SAML attributes must be converted into roles. + +
+
+ IDP Element + + Everything in the IDP element describes the settings for the IDP the SP is communicating + with. + + + +... +]]> + + + + + entityID + + + This is the issuer ID of the IDP. + REQUIRED.. + + + + + signaturesRequired + + + If set to true, the client adapter will sign every document + it sends to the IDP. Also, the client will expect that the IDP + will be signing an documents sent to it. This switch sets + the default for all request and response types, but you will see + later that you have some fine grain control over this. + OPTIONAL. + + + + + signatureAlgorithm + + + This is the signature algorithm that the IDP expects signed documents + to use + OPTIONAL.. The default value is RSA_SHA1, but + you can also use RSA_256, RSA_512, and DSA_SHA1. + + + + + signatureCanonicalizationMethod + + + This is the signature canonicalization method that the IDP expects signed documents + to use + OPTIONAL.. The default value is http://www.w3.org/2001/10/xml-exc-c14n# + and should be good for most IDPs. + + + + + +
+
+ IDP SingleSignOnService sub element + + The SignleSignOnService sub element defines the + login SAML endpoint of the IDP. + + +]]> + + + + signRequest + + + Should the client sign authn requests? + OPTIONAL.. Defaults to whatever the + IDP signaturesRequired element value is. + + + + + validateResponseSignature + + + Should the client expect the IDP to sign the assertion response document + sent back from an auhtn request? + OPTIONAL. Defaults to whatever the + IDP signaturesRequired element value is. + + + + + requestBinding + + + This is the SAML binding type used for communicating with the IDP + OPTIONAL.. The default value is POST, but + you can set it to REDIRECT as well. + + + + + responseBinding + + + SAML allows the client to request what binding type it wants authn responses + to use. The values of this can be POST or REDIRECT + OPTIONAL.. The default is that the client will not request + a specific binding type for responses. + + + + + bindingUrl + + + This is the URL for the ID login service that the client will send requests to. + REQUIRED.. + + + + + +
+ IDP SingleSignOnService sub element - Here is a description of each item: + The SignleSignOnService sub element defines the + login SAML endpoint of the IDP. + +]]> - realm + signRequest - Name of the realm representing the users of your distributed applications and services. - This is - REQUIRED. + Should the client sign logout requests it makes to the IDP? + OPTIONAL.. Defaults to whatever the + IDP signaturesRequired element value is. - resource + signResponse - Username of the application. Each application has a username that is used when the - application connects with the Keycloak server to turn an access code into an access token - (part of the OAuth 2.0 protocol). This is - REQUIRED. + Should the client sign logout responses it sends to the IDP requests? + OPTIONAL.. Defaults to whatever the + IDP signaturesRequired element value is. - realm-public-key + validateRequestSignature - PEM format of public key. You can obtain this from the administration console. - This is - REQUIRED. + Should the client expect signed logout request documents from the IDP? + OPTIONAL. Defaults to whatever the + IDP signaturesRequired element value is. - auth-server-url + validateResponseSignature - The base URL of the Keycloak Server. All other Keycloak pages and REST services are derived - from this. It is usually of the form https://host:port/auth - This is - REQUIRED. + Should the client expect signed logout response documents from the IDP? + OPTIONAL. Defaults to whatever the + IDP signaturesRequired element value is. - ssl-required + requestBinding - Ensures that all communication to and from the Keycloak server from the adapter is over HTTPS. - This is OPTIONAL. The default value is - external - meaning that HTTPS is required by default for external requests. Valid values are 'all', 'external' - and 'none'. + This is the SAML binding type used for communicating SAML requests to the IDP + OPTIONAL.. The default value is POST, but + you can set it to REDIRECT as well. - use-resource-role-mappings + responseBinding - If set to true, the adapter will look inside the token for application level role mappings for - the - user. If false, it will look at the realm level for user role mappings. - This is OPTIONAL. The default value is false. + This is the SAML binding type used for communicating SAML responses to the IDP + The values of this can be POST or REDIRECT + OPTIONAL.. The default value is POST, but + you can set it to REDIRECT as well. - public-client + postBindingUrl - If set to true, the adapter will not send credentials for the client to Keycloak. - The default value is false. + This is the URL for the IDP's logout service when using the POST binding. + REQUIRED if using the POST binding at all. - enable-cors + redirectBindingUrl - This enables CORS support. It will handle CORS preflight requests. It will also look into - the access token to determine valid origins. - This is OPTIONAL. The default value is false. - - - - - cors-max-age - - - If CORS is enabled, this sets the value of the - Access-Control-Max-Age - header. - This is OPTIONAL. If not set, this header is not returned in CORS - responses. - - - - - cors-allowed-methods - - - If CORS is enabled, this sets the value of the - Access-Control-Allow-Methods - header. This should be a comma-separated string. - This is OPTIONAL. If not set, this header is not returned in CORS - responses. - - - - - cors-allowed-headers - - - If CORS is enabled, this sets the value of the - Access-Control-Allow-Headers - header. This should be a comma-separated string. - This is OPTIONAL. If not set, this header is not returned in CORS - responses. - - - - - bearer-only - - - This tells the adapter to only do bearer token authentication. That is, it will not do - OAuth 2.0 redirects, but only accept bearer tokens through the - Authorization - header. - This is OPTIONAL. The default value is false. - - - - - enable-basic-auth - - - This tells the adapter to also support basic authentication. If this option is enabled, - then secret must also be provided. - This is OPTIONAL. The default value is false. - - - - - expose-token - - - If true, an authenticated browser client (via a Javascript HTTP invocation) - can obtain the signed access token via the URL root/k_query_bearer_token. - This is OPTIONAL. The default value is false. - - - - - credentials - - - Specify the credentials of the application. This is an object notation where the key - is the credential type and the value is the value of the credential type. Currently only - password - is supported. - This is REQUIRED. - - - - - - connection-pool-size - - - Adapters will make separate HTTP invocations to the Keycloak Server to turn an access code - into an access token. This config option defines how many connections to the Keycloak Server - should be pooled. - This is OPTIONAL. The default value is 20. - - - - - disable-trust-manager - - - If the Keycloak Server requires HTTPS and this config option is set to true - you do not have to specify a truststore. While convenient, this setting is not recommended - as you will not be verifying the host name of the Keycloak Server. - This is OPTIONAL. The default value is false. - - - - - allow-any-hostname - - - If the Keycloak Server requires HTTPS and this config option is set to true - the Keycloak Server's certificate is validated via the truststore, but host name validation is - not done. This is not a recommended. This seting may be useful in test environments - This is OPTIONAL. The default value is false. - - - - - - truststore - - - This setting is for Java adapters. The value is the file path to a Java keystore file. If - you prefix the path with classpath:, then the truststore will be obtained - from the deployment's classpath instead. - Used for outgoing HTTPS communications to the Keycloak server. Client making HTTPS - requests need a way to verify the host of the server they are talking to. This is - what the trustore does. The keystore contains one or more trusted - host certificates or certificate authorities. You can - create this truststore by extracting the public certificate of the Keycloak server's SSL - keystore. - This is - OPTIONAL - if - ssl-required - is - none - or - disable-trust-manager - is true. - - - - - truststore-password - - - Password for the truststore keystore. - This is - REQUIRED - if - truststore - is set. - - - - - client-keystore - - - Not supported yet, but we will support in future versions. - - This setting is for Java adapters. This is the file path to a Java keystore file. - This keystore contains client certificate for two-way SSL when the adapter makes - HTTPS requests to the Keycloak server. - This is OPTIONAL. - - - - - client-keystore-password - - - Not supported yet, but we will support in future versions. - Password for the client keystore. - This is - REQUIRED - if - client-keystore - is set. - - - - - client-key-password - - - Not supported yet, but we will support in future versions. - Password for the client's key. - This is - REQUIRED - if - client-keystore - is set. - - - - - auth-server-url-for-backend-requests - - - Alternative location of auth-server-url used just for backend requests. It must be absolute URI. Useful - especially in cluster (see Relative URI Optimization) or if you would like to use https for browser requests - but stick with http for backend requests etc. - - - - - always-refresh-token - - - If true, Keycloak will refresh token in every request. More info in Refresh token in each request . - - - - - register-node-at-startup - - - If true, then adapter will send registration request to Keycloak. It's false - by default and useful just in cluster (See Registration of application nodes to Keycloak) - - - - - register-node-period - - - Period for re-registration adapter to Keycloak. Useful in cluster. See Registration of application nodes to Keycloak for details. - - - - - token-store - - - Possible values are session and cookie. Default is session, - which means that adapter stores account info in HTTP Session. Alternative cookie means storage of info in cookie. - See Stateless token store for details. - - - - - principal-attribute - - - OpenID Connection ID Token attribute to populate the UserPrincipal name with. If token attribute is null, defaults to sub. - Possible values are sub, preferred_username, email, name, nickname, given_name, family_name. + This is the URL for the IDP's logout service when using the REDIRECT binding. + REQUIRED if using the REDIRECT binding at all. +
+
+ IDP Keys subelement + + The Keys sub element of IDP is only used to define the certificate or + public key to use to verify documents signed by the IDP. It is defined + in the same way as the SP's Key's element. But + again, you only have to define one certificate or public key reference. + + +
+ diff --git a/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml b/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml index c3132e2168..a094acf3f4 100755 --- a/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml +++ b/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml @@ -13,9 +13,9 @@ - + - + - + - + - + - + ${project.version} zip + + org.keycloak + keycloak-saml-tomcat6-adapter-dist + ${project.version} + zip + + + org.keycloak + keycloak-saml-tomcat7-adapter-dist + ${project.version} + zip + + + org.keycloak + keycloak-saml-tomcat8-adapter-dist + ${project.version} + zip + + + org.keycloak + keycloak-saml-jetty81-adapter-dist + ${project.version} + zip + + + org.keycloak + keycloak-saml-jetty92-adapter-dist + ${project.version} + zip + + + org.keycloak + keycloak-saml-as7-adapter-dist + ${project.version} + zip + + + org.keycloak + keycloak-saml-eap6-adapter-dist + ${project.version} + zip + org.keycloak keycloak-testsuite-integration diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java index 681e405a99..258f14bca3 100755 --- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java +++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java @@ -67,8 +67,7 @@ public interface SamlDeployment { enum PrincipalNamePolicy { FROM_NAME_ID, - FROM_ATTRIBUTE_NAME, - FROM_FRIENDLY_ATTRIBUTE_NAME + FROM_ATTRIBUTE } PrincipalNamePolicy getPrincipalNamePolicy(); String getPrincipalAttributeName(); diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java index fc08e449a3..d1aaea9c66 100755 --- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java +++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java @@ -24,16 +24,16 @@ public class IDPXmlParser extends AbstractParser { StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, ConfigXmlConstants.IDP_ELEMENT); IDP idp = new IDP(); - String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR); + String entityID = SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR); if (entityID == null) { throw new ParsingException("entityID must be set on IDP"); } idp.setEntityID(entityID); - boolean signaturesRequired = StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR); - idp.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR)); - idp.setSignatureAlgorithm(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR)); + boolean signaturesRequired = SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR); + idp.setSignatureCanonicalizationMethod(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR)); + idp.setSignatureAlgorithm(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR)); while (xmlEventReader.hasNext()) { XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); if (xmlEvent == null) @@ -73,25 +73,25 @@ public class IDPXmlParser extends AbstractParser { protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException { IDP.SingleLogoutService slo = new IDP.SingleLogoutService(); StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - slo.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired)); - slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired)); - slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired)); - slo.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR)); - slo.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR)); - slo.setSignResponse(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired)); - slo.setPostBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR)); - slo.setRedirectBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR)); + slo.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired)); + slo.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired)); + slo.setValidateRequestSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired)); + slo.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR)); + slo.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR)); + slo.setSignResponse(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired)); + slo.setPostBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR)); + slo.setRedirectBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR)); return slo; } protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException { IDP.SingleSignOnService sso = new IDP.SingleSignOnService(); StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - sso.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired)); - sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired)); - sso.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR)); - sso.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR)); - sso.setBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR)); + sso.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired)); + sso.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired)); + sso.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR)); + sso.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR)); + sso.setBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR)); return sso; } diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java index 6fbd8d037f..0308a56a22 100755 --- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java +++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java @@ -7,8 +7,6 @@ import org.keycloak.saml.common.util.StaxParserUtil; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.Characters; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; @@ -24,8 +22,8 @@ public class KeyXmlParser extends AbstractParser { StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, ConfigXmlConstants.KEY_ELEMENT); Key key = new Key(); - key.setSigning(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR)); - key.setEncryption(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR)); + key.setSigning(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR)); + key.setEncryption(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR)); while (xmlEventReader.hasNext()) { XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); if (xmlEvent == null) @@ -46,13 +44,13 @@ public class KeyXmlParser extends AbstractParser { key.setKeystore(parseKeyStore(xmlEventReader)); } else if (tag.equals(ConfigXmlConstants.CERTIFICATE_PEM_ELEMENT)) { StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - key.setCertificatePem(StaxParserUtil.getElementText(xmlEventReader)); + key.setCertificatePem(SPXmlParser.getElementText(xmlEventReader)); } else if (tag.equals(ConfigXmlConstants.PUBLIC_KEY_PEM_ELEMENT)) { StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - key.setPublicKeyPem(StaxParserUtil.getElementText(xmlEventReader)); + key.setPublicKeyPem(SPXmlParser.getElementText(xmlEventReader)); } else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_PEM_ELEMENT)) { StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - key.setPrivateKeyPem(StaxParserUtil.getElementText(xmlEventReader)); + key.setPrivateKeyPem(SPXmlParser.getElementText(xmlEventReader)); } else { StaxParserUtil.bypassElementBlock(xmlEventReader, tag); } @@ -65,14 +63,14 @@ public class KeyXmlParser extends AbstractParser { StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_STORE_ELEMENT); Key.KeyStoreConfig keyStore = new Key.KeyStoreConfig(); - keyStore.setType(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR)); - keyStore.setAlias(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR)); - keyStore.setFile(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR)); - keyStore.setResource(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR)); + keyStore.setType(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR)); + keyStore.setAlias(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR)); + keyStore.setFile(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR)); + keyStore.setResource(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR)); if (keyStore.getFile() == null && keyStore.getResource() == null) { throw new ParsingException("KeyStore element must have the url or classpath attribute set"); } - keyStore.setPassword(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR)); + keyStore.setPassword(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR)); if (keyStore.getPassword() == null) { throw new ParsingException("KeyStore element must have the password attribute set"); } @@ -97,19 +95,19 @@ public class KeyXmlParser extends AbstractParser { String tag = StaxParserUtil.getStartElementName(startElement); if (tag.equals(ConfigXmlConstants.CERTIFICATE_ELEMENT)) { StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - keyStore.setCertificateAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR)); + keyStore.setCertificateAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR)); if (keyStore.getCertificateAlias() == null) { throw new ParsingException("KeyStore Certificate element must have the alias attribute set"); } } else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_ELEMENT)) { StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - keyStore.setPrivateKeyAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR)); + keyStore.setPrivateKeyAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR)); if (keyStore.getPrivateKeyAlias() == null) { throw new ParsingException("KeyStore PrivateKey element must have the alias attribute set"); } - keyStore.setPrivateKeyPassword(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR)); + keyStore.setPrivateKeyPassword(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR)); if (keyStore.getPrivateKeyPassword() == null) { throw new ParsingException("KeyStore PrivateKey element must have the password attribute set"); diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java index f0104707b4..34b924d1ae 100755 --- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java +++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java @@ -6,9 +6,11 @@ import org.keycloak.adapters.saml.config.SP; import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.parsers.AbstractParser; import org.keycloak.saml.common.util.StaxParserUtil; +import org.keycloak.util.StringPropertyReplacer; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.Attribute; import javax.xml.stream.events.EndElement; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; @@ -23,21 +25,44 @@ import java.util.Set; */ public class SPXmlParser extends AbstractParser { + public static String getAttributeValue(StartElement startElement, String tag) { + String str = StaxParserUtil.getAttributeValue(startElement, tag); + if (str != null) return StringPropertyReplacer.replaceProperties(str); + else return str; + } + + public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) { + String result = getAttributeValue(startElement, tag); + if (result == null) return defaultValue; + return Boolean.valueOf(result); + } + + public static boolean getBooleanAttributeValue(StartElement startElement, String tag) { + return getBooleanAttributeValue(startElement, tag, false); + } + + public static String getElementText(XMLEventReader xmlEventReader) throws ParsingException { + String result = StaxParserUtil.getElementText(xmlEventReader); + if (result != null) result = StringPropertyReplacer.replaceProperties(result); + return result; + } + + @Override public Object parse(XMLEventReader xmlEventReader) throws ParsingException { StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.validate(startElement, ConfigXmlConstants.SP_ELEMENT); SP sp = new SP(); - String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR); + String entityID = getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR); if (entityID == null) { throw new ParsingException("entityID must be set on SP"); } sp.setEntityID(entityID); - sp.setSslPolicy(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR)); - sp.setLogoutPage(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR)); - sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR)); - sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR)); + sp.setSslPolicy(getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR)); + sp.setLogoutPage(getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR)); + sp.setNameIDPolicyFormat(getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR)); + sp.setForceAuthentication(getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR)); while (xmlEventReader.hasNext()) { XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); if (xmlEvent == null) @@ -60,12 +85,12 @@ public class SPXmlParser extends AbstractParser { sp.setKeys(keys); } else if (tag.equals(ConfigXmlConstants.PRINCIPAL_NAME_MAPPING_ELEMENT)) { StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - String policy = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR); + String policy = getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR); if (policy == null) { throw new ParsingException("PrincipalNameMapping element must have the policy attribute set"); } - String attribute = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR); + String attribute = getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR); SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping(); mapping.setPolicy(policy); mapping.setAttributeName(attribute); @@ -107,7 +132,7 @@ public class SPXmlParser extends AbstractParser { String tag = StaxParserUtil.getStartElementName(startElement); if (tag.equals(ConfigXmlConstants.ATTRIBUTE_ELEMENT)) { StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader); - String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR); + String attributeValue = getAttributeValue(element, ConfigXmlConstants.NAME_ATTR); if (attributeValue == null) { throw new ParsingException("RoleMapping Attribute element must have the name attribute set"); diff --git a/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd index b9e0799e97..534c9aef56 100755 --- a/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd +++ b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd @@ -25,7 +25,7 @@ - + @@ -70,7 +70,7 @@ - + diff --git a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml index 5f881975e7..ef910dceca 100755 --- a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml +++ b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml @@ -20,9 +20,9 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +