This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git
The following commit(s) were added to refs/heads/master by this push: new bb3ef16 FEDIZ-229 - Provide a way to map JWT claims into SAML attributes when converting a third party JWT token into a SAML Token bb3ef16 is described below commit bb3ef16c1dfa4995e34cc3c622a4d144dd777970 Author: Colm O hEigeartaigh <cohei...@apache.org> AuthorDate: Wed Sep 5 17:00:16 2018 +0100 FEDIZ-229 - Provide a way to map JWT claims into SAML attributes when converting a third party JWT token into a SAML Token --- .../AbstractTrustedIdpOAuth2ProtocolHandler.java | 31 +++++++++++- .../fediz/service/idp/protocols/ClaimsHandler.java | 32 +++++++++++++ .../service/idp/protocols/RoleClaimsHandler.java | 56 ++++++++++++++++++++++ .../TrustedIdpFacebookProtocolHandler.java | 2 +- .../protocols/TrustedIdpOIDCProtocolHandler.java | 3 +- .../oidc/idp/example/IdTokenProviderImpl.java | 1 + .../src/test/resources/realma/entities-realma.xml | 1 + 7 files changed, 123 insertions(+), 3 deletions(-) diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/AbstractTrustedIdpOAuth2ProtocolHandler.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/AbstractTrustedIdpOAuth2ProtocolHandler.java index a59daeb..6b62c39 100644 --- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/AbstractTrustedIdpOAuth2ProtocolHandler.java +++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/AbstractTrustedIdpOAuth2ProtocolHandler.java @@ -25,6 +25,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.time.Instant; +import java.util.Collections; import java.util.Date; import javax.security.auth.callback.Callback; @@ -35,14 +36,17 @@ import org.apache.cxf.fediz.core.util.CertsUtils; import org.apache.cxf.fediz.service.idp.IdpConstants; import org.apache.cxf.fediz.service.idp.domain.Idp; import org.apache.cxf.fediz.service.idp.domain.TrustedIdp; +import org.apache.cxf.jaxrs.json.basic.JsonMapObject; import org.apache.wss4j.common.crypto.Crypto; import org.apache.wss4j.common.saml.SAMLCallback; import org.apache.wss4j.common.saml.SAMLUtil; import org.apache.wss4j.common.saml.SamlAssertionWrapper; +import org.apache.wss4j.common.saml.bean.AttributeStatementBean; import org.apache.wss4j.common.saml.bean.ConditionsBean; import org.apache.wss4j.common.saml.bean.SubjectBean; import org.apache.wss4j.common.saml.bean.Version; import org.apache.wss4j.common.saml.builder.SAML2Constants; +import org.apache.wss4j.common.util.Loader; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,6 +75,12 @@ public abstract class AbstractTrustedIdpOAuth2ProtocolHandler extends AbstractTr */ public static final String SCOPE = "scope"; + /** + * The fully qualified class name of a ClaimsHandler implementation, designed to convert claims in a JWT token + * into claims in the generated SAML token. + */ + public static final String CLAIMS_HANDLER = "claims.handler"; + private static final Logger LOG = LoggerFactory.getLogger(AbstractTrustedIdpOAuth2ProtocolHandler.class); @Override @@ -115,7 +125,8 @@ public abstract class AbstractTrustedIdpOAuth2ProtocolHandler extends AbstractTr } } - protected SamlAssertionWrapper createSamlAssertion(Idp idp, TrustedIdp trustedIdp, String subjectName, + protected SamlAssertionWrapper createSamlAssertion(Idp idp, TrustedIdp trustedIdp, JsonMapObject claims, + String subjectName, Instant notBefore, Instant expires) throws Exception { SamlCallbackHandler callbackHandler = new SamlCallbackHandler(); @@ -143,6 +154,14 @@ public abstract class AbstractTrustedIdpOAuth2ProtocolHandler extends AbstractTr } callbackHandler.setConditionsBean(conditionsBean); + // Claims + String claimsHandler = getProperty(trustedIdp, CLAIMS_HANDLER); + if (claimsHandler != null) { + ClaimsHandler claimsHandlerImpl = (ClaimsHandler)Loader.loadClass(claimsHandler).newInstance(); + AttributeStatementBean attrStatementBean = claimsHandlerImpl.handleClaims(claims); + callbackHandler.setAttrBean(attrStatementBean); + } + SAMLCallback samlCallback = new SAMLCallback(); SAMLUtil.doSAMLCallback(callbackHandler, samlCallback); @@ -159,6 +178,7 @@ public abstract class AbstractTrustedIdpOAuth2ProtocolHandler extends AbstractTr private ConditionsBean conditionsBean; private SubjectBean subjectBean; private String issuer; + private AttributeStatementBean attrBean; /** * Set the SubjectBean @@ -197,10 +217,19 @@ public abstract class AbstractTrustedIdpOAuth2ProtocolHandler extends AbstractTr // Set the conditions samlCallback.setConditions(conditionsBean); + + // Set the attributes + if (attrBean != null) { + samlCallback.setAttributeStatementData(Collections.singletonList(attrBean)); + } } } } + public void setAttrBean(AttributeStatementBean attrBean) { + this.attrBean = attrBean; + } + } abstract String getScope(TrustedIdp trustedIdp); diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/ClaimsHandler.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/ClaimsHandler.java new file mode 100644 index 0000000..cb338dc --- /dev/null +++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/ClaimsHandler.java @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.fediz.service.idp.protocols; + +import org.apache.cxf.jaxrs.json.basic.JsonMapObject; +import org.apache.wss4j.common.saml.bean.AttributeStatementBean; + +/** + * An interface to convert JWT claims into a SAML AttributeStatementBean. + */ +public interface ClaimsHandler { + + AttributeStatementBean handleClaims(JsonMapObject claims); + +} diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/RoleClaimsHandler.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/RoleClaimsHandler.java new file mode 100644 index 0000000..8551203 --- /dev/null +++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/RoleClaimsHandler.java @@ -0,0 +1,56 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.fediz.service.idp.protocols; + +import java.net.URI; +import java.util.Collections; + +import org.apache.cxf.jaxrs.json.basic.JsonMapObject; +import org.apache.wss4j.common.saml.bean.AttributeBean; +import org.apache.wss4j.common.saml.bean.AttributeStatementBean; +import org.apache.wss4j.common.saml.builder.SAML2Constants; + +/** + * Convert a "role" claim into a SAML AttributeStatement + */ +public class RoleClaimsHandler implements ClaimsHandler { + + private static final URI ROLE = + URI.create("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"); + private String nameFormat = SAML2Constants.ATTRNAME_FORMAT_UNSPECIFIED; + + public AttributeStatementBean handleClaims(JsonMapObject claims) { + if (claims != null) { + String role = claims.getStringProperty("role"); + if (role != null) { + AttributeStatementBean attrBean = new AttributeStatementBean(); + AttributeBean attributeBean = new AttributeBean(); + attributeBean.setQualifiedName(ROLE.toString()); + attributeBean.setNameFormat(nameFormat); + attributeBean.setAttributeValues(Collections.singletonList(role)); + attrBean.setSamlAttributes(Collections.singletonList(attributeBean)); + return attrBean; + } + } + + return null; + } + +} diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpFacebookProtocolHandler.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpFacebookProtocolHandler.java index bafb62c..63ebf90 100644 --- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpFacebookProtocolHandler.java +++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpFacebookProtocolHandler.java @@ -128,7 +128,7 @@ public class TrustedIdpFacebookProtocolHandler extends AbstractTrustedIdpOAuth2P Instant expires = Instant.now().plusSeconds(accessToken.getExpiresIn()); SecurityToken idpToken = new SecurityToken(IDGenerator.generateID(null), null, expires); SamlAssertionWrapper assertion = - createSamlAssertion(idp, trustedIdp, subjectName, null, expires); + createSamlAssertion(idp, trustedIdp, null, subjectName, null, expires); Document doc = DOMUtils.createDocument(); Element token = assertion.toDOM(doc); diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java index 2b0d36f..dce403e 100644 --- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java +++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/protocols/TrustedIdpOIDCProtocolHandler.java @@ -211,7 +211,8 @@ public class TrustedIdpOIDCProtocolHandler extends AbstractTrustedIdpOAuth2Proto // Convert into a SAML Token SamlAssertionWrapper assertion = - createSamlAssertion(idp, trustedIdp, (String)jwt.getClaim(subjectName), notBefore, expires); + createSamlAssertion(idp, trustedIdp, jwt.getClaims(), (String)jwt.getClaim(subjectName), + notBefore, expires); Document doc = DOMUtils.createDocument(); Element token = assertion.toDOM(doc); diff --git a/systests/federation/oidcIdpWebapp/src/main/java/org/apache/cxf/fediz/oidc/idp/example/IdTokenProviderImpl.java b/systests/federation/oidcIdpWebapp/src/main/java/org/apache/cxf/fediz/oidc/idp/example/IdTokenProviderImpl.java index 5785350..44c18ce 100644 --- a/systests/federation/oidcIdpWebapp/src/main/java/org/apache/cxf/fediz/oidc/idp/example/IdTokenProviderImpl.java +++ b/systests/federation/oidcIdpWebapp/src/main/java/org/apache/cxf/fediz/oidc/idp/example/IdTokenProviderImpl.java @@ -46,6 +46,7 @@ public class IdTokenProviderImpl implements IdTokenProvider { token.setSubject(authenticatedUser.getLogin().toLowerCase()); token.setClaim("preferred_username", authenticatedUser.getLogin().toLowerCase()); token.setIssuer("OIDC IdP"); + token.setClaim("role", "user"); return token; } diff --git a/systests/federation/wsfed/src/test/resources/realma/entities-realma.xml b/systests/federation/wsfed/src/test/resources/realma/entities-realma.xml index 24dc488..d5eb99c 100644 --- a/systests/federation/wsfed/src/test/resources/realma/entities-realma.xml +++ b/systests/federation/wsfed/src/test/resources/realma/entities-realma.xml @@ -166,6 +166,7 @@ <entry key="client.id" value="consumer-id"/> <entry key="client.secret" value="this-is-a-secret"/> <entry key="token.endpoint" value="https://localhost:${idp.oidc.https.port}/idpoidc/services/token"/> + <entry key="claims.handler" value="org.apache.cxf.fediz.service.idp.protocols.RoleClaimsHandler"/> </util:map> </property> </bean>