You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2018/09/05 16:00:37 UTC

[cxf-fediz] branch master updated: FEDIZ-229 - Provide a way to map JWT claims into SAML attributes when converting a third party JWT token into a SAML Token

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