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/04/26 14:34:58 UTC
[cxf] 03/03: CXF-7729 - Merge duplicate Attribute elements with an
AttributeStatement
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.git
commit 0cbdea0fa72f812b80cc96ff760ae30b73d3123e
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Thu Apr 26 15:34:11 2018 +0100
CXF-7729 - Merge duplicate Attribute elements with an AttributeStatement
---
.../claims/ClaimsAttributeStatementProvider.java | 5 +-
.../CombinedClaimsAttributeStatementProvider.java | 180 +++++++++++++++++++++
.../cxf/sts/token/provider/SAMLTokenProvider.java | 18 ++-
.../apache/cxf/sts/common/CustomClaimsHandler.java | 11 +-
.../cxf/sts/token/provider/SAMLClaimsTest.java | 89 +++++++++-
5 files changed, 295 insertions(+), 8 deletions(-)
diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsAttributeStatementProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsAttributeStatementProvider.java
index 2e3624f..a4065b9 100644
--- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsAttributeStatementProvider.java
+++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/ClaimsAttributeStatementProvider.java
@@ -48,6 +48,8 @@ public class ClaimsAttributeStatementProvider implements AttributeStatementProvi
List<AttributeBean> attributeList = new ArrayList<>();
String tokenType = providerParameters.getTokenRequirements().getTokenType();
+ boolean saml2 = WSS4JConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType)
+ || WSS4JConstants.SAML2_NS.equals(tokenType);
AttributeStatementBean attrBean = new AttributeStatementBean();
while (claimIterator.hasNext()) {
@@ -55,8 +57,7 @@ public class ClaimsAttributeStatementProvider implements AttributeStatementProvi
AttributeBean attributeBean = new AttributeBean();
URI claimType = claim.getClaimType();
- if (WSS4JConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType)
- || WSS4JConstants.SAML2_NS.equals(tokenType)) {
+ if (saml2) {
attributeBean.setQualifiedName(claimType.toString());
attributeBean.setNameFormat(nameFormat);
} else {
diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/CombinedClaimsAttributeStatementProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/CombinedClaimsAttributeStatementProvider.java
new file mode 100644
index 0000000..cea998a
--- /dev/null
+++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/claims/CombinedClaimsAttributeStatementProvider.java
@@ -0,0 +1,180 @@
+/**
+ * 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.sts.claims;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cxf.sts.token.provider.AttributeStatementProvider;
+import org.apache.cxf.sts.token.provider.TokenProviderParameters;
+import org.apache.wss4j.common.WSS4JConstants;
+import org.apache.wss4j.common.saml.bean.AttributeBean;
+import org.apache.wss4j.common.saml.bean.AttributeStatementBean;
+import org.apache.wss4j.common.saml.builder.SAML2Constants;
+
+/**
+ * This class differs from the ClaimsAttributeStatementProvider in that it combines claims that have the same name.
+ */
+public class CombinedClaimsAttributeStatementProvider implements AttributeStatementProvider {
+
+ private String nameFormat = SAML2Constants.ATTRNAME_FORMAT_UNSPECIFIED;
+
+ public AttributeStatementBean getStatement(TokenProviderParameters providerParameters) {
+ // Handle Claims
+ ProcessedClaimCollection retrievedClaims = ClaimsUtils.processClaims(providerParameters);
+ if (retrievedClaims == null) {
+ return null;
+ }
+
+ Iterator<ProcessedClaim> claimIterator = retrievedClaims.iterator();
+ if (!claimIterator.hasNext()) {
+ return null;
+ }
+
+ Map<AttributeKey, AttributeBean> attributeMap = new LinkedHashMap<>();
+
+ String tokenType = providerParameters.getTokenRequirements().getTokenType();
+ boolean saml2 = WSS4JConstants.WSS_SAML2_TOKEN_TYPE.equals(tokenType)
+ || WSS4JConstants.SAML2_NS.equals(tokenType);
+
+ while (claimIterator.hasNext()) {
+ ProcessedClaim claim = claimIterator.next();
+ AttributeKey attributeKey = createAttributeKey(claim, saml2);
+
+ attributeMap.merge(
+ attributeKey,
+ createAttributeBean(attributeKey, claim.getValues()),
+ (v1, v2) -> {
+ v1.getAttributeValues().addAll(claim.getValues());
+ return v1;
+ });
+ }
+
+ AttributeStatementBean attrBean = new AttributeStatementBean();
+ attrBean.setSamlAttributes(new ArrayList<>(attributeMap.values()));
+
+ return attrBean;
+ }
+
+ private AttributeBean createAttributeBean(AttributeKey attributeKey, List<Object> claimValues) {
+ AttributeBean attributeBean =
+ new AttributeBean(attributeKey.getSimpleName(), attributeKey.getQualifiedName(), claimValues);
+ attributeBean.setNameFormat(attributeKey.getNameFormat());
+ return attributeBean;
+ }
+
+ private AttributeKey createAttributeKey(ProcessedClaim claim, boolean saml2) {
+
+ URI claimType = claim.getClaimType();
+ if (saml2) {
+ return new AttributeKey(claimType.toString(), nameFormat, null);
+ } else {
+ String uri = claimType.toString();
+ int lastSlash = uri.lastIndexOf("/");
+ if (lastSlash == (uri.length() - 1)) {
+ uri = uri.substring(0, lastSlash);
+ lastSlash = uri.lastIndexOf("/");
+ }
+
+ String namespace = uri.substring(0, lastSlash);
+ String name = uri.substring(lastSlash + 1, uri.length());
+
+ return new AttributeKey(namespace, null, name);
+ }
+ }
+
+ public String getNameFormat() {
+ return nameFormat;
+ }
+
+ public void setNameFormat(String nameFormat) {
+ this.nameFormat = nameFormat;
+ }
+
+ private static class AttributeKey {
+ private final String qualifiedName;
+ private final String simpleName;
+ private final String nameFormat;
+
+ // SAML 2.0 constructor
+ AttributeKey(String qualifiedName, String nameFormat, String simpleName) {
+ this.qualifiedName = qualifiedName;
+ this.nameFormat = nameFormat;
+ this.simpleName = simpleName;
+ }
+
+ public String getQualifiedName() {
+ return qualifiedName;
+ }
+
+ public String getSimpleName() {
+ return simpleName;
+ }
+
+ public String getNameFormat() {
+ return nameFormat;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof AttributeKey)) {
+ return false;
+ }
+
+ AttributeKey that = (AttributeKey) o;
+
+ if (qualifiedName == null && that.qualifiedName != null
+ || qualifiedName != null && !qualifiedName.equals(that.qualifiedName)) {
+ return false;
+ }
+
+ if (simpleName == null && that.simpleName != null
+ || simpleName != null && !simpleName.equals(that.simpleName)) {
+ return false;
+ }
+
+ return !(nameFormat == null && that.nameFormat != null
+ || nameFormat != null && !nameFormat.equals(that.nameFormat));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ if (qualifiedName != null) {
+ result = 31 * result + qualifiedName.hashCode();
+ }
+ if (simpleName != null) {
+ result = 31 * result + simpleName.hashCode();
+ }
+ if (nameFormat != null) {
+ result = 31 * result + nameFormat.hashCode();
+ }
+
+ return result;
+ }
+ }
+}
+
diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java
index c5918b6..4181736 100644
--- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java
+++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/SAMLTokenProvider.java
@@ -36,6 +36,7 @@ import org.apache.cxf.sts.STSConstants;
import org.apache.cxf.sts.STSPropertiesMBean;
import org.apache.cxf.sts.cache.CacheUtils;
import org.apache.cxf.sts.claims.ClaimsAttributeStatementProvider;
+import org.apache.cxf.sts.claims.CombinedClaimsAttributeStatementProvider;
import org.apache.cxf.sts.request.KeyRequirements;
import org.apache.cxf.sts.request.TokenRequirements;
import org.apache.cxf.sts.token.realm.RealmProperties;
@@ -68,6 +69,7 @@ public class SAMLTokenProvider extends AbstractSAMLTokenProvider implements Toke
private boolean signToken = true;
private Map<String, RealmProperties> realmMap = new HashMap<>();
private SamlCustomHandler samlCustomHandler;
+ private boolean combineClaimAttributes = true;
/**
* Return true if this TokenProvider implementation is capable of providing a token
@@ -388,7 +390,13 @@ public class SAMLTokenProvider extends AbstractSAMLTokenProvider implements Toke
// Also handle "ActAs" via the ActAsAttributeStatementProvider
if (!statementAdded) {
attrBeanList = new ArrayList<>();
- AttributeStatementProvider attributeProvider = new ClaimsAttributeStatementProvider();
+ AttributeStatementProvider attributeProvider = null;
+ if (combineClaimAttributes) {
+ attributeProvider = new CombinedClaimsAttributeStatementProvider();
+ } else {
+ attributeProvider = new ClaimsAttributeStatementProvider();
+ }
+
AttributeStatementBean attributeBean = attributeProvider.getStatement(tokenParameters);
if (attributeBean != null && attributeBean.getSamlAttributes() != null
&& !attributeBean.getSamlAttributes().isEmpty()) {
@@ -459,5 +467,13 @@ public class SAMLTokenProvider extends AbstractSAMLTokenProvider implements Toke
}
+ public boolean isCombineClaimAttributes() {
+ return combineClaimAttributes;
+ }
+
+ public void setCombineClaimAttributes(boolean combineClaimAttributes) {
+ this.combineClaimAttributes = combineClaimAttributes;
+ }
+
}
diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/common/CustomClaimsHandler.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/common/CustomClaimsHandler.java
index 7b52b48..ed8182b 100644
--- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/common/CustomClaimsHandler.java
+++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/common/CustomClaimsHandler.java
@@ -42,9 +42,9 @@ import org.opensaml.saml.saml2.core.AttributeValue;
*/
public class CustomClaimsHandler implements ClaimsHandler {
- private static List<URI> knownURIs = new ArrayList<>();
- private static final URI ROLE_CLAIM =
+ public static final URI ROLE_CLAIM =
URI.create("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role");
+ private static List<URI> knownURIs = new ArrayList<>();
static {
knownURIs.add(ClaimTypes.FIRSTNAME);
@@ -55,6 +55,8 @@ public class CustomClaimsHandler implements ClaimsHandler {
knownURIs.add(ROLE_CLAIM);
}
+ private String role = "DUMMY";
+
public List<URI> getSupportedClaimTypes() {
return knownURIs;
}
@@ -108,7 +110,7 @@ public class CustomClaimsHandler implements ClaimsHandler {
}
} else {
// If no specific role was requested return DUMMY role for user
- claim.addValue("DUMMY");
+ claim.addValue(role);
}
}
claimCollection.add(claim);
@@ -123,4 +125,7 @@ public class CustomClaimsHandler implements ClaimsHandler {
return true;
}
+ public void setRole(String role) {
+ this.role = role;
+ }
}
diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLClaimsTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLClaimsTest.java
index f951052..ac67ee1 100644
--- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLClaimsTest.java
+++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/SAMLClaimsTest.java
@@ -20,6 +20,7 @@ package org.apache.cxf.sts.token.provider;
import java.net.URI;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -367,6 +368,92 @@ public class SAMLClaimsTest extends org.junit.Assert {
assertTrue(tokenString.contains(ClaimTypes.MOBILEPHONE.toString()));
}
+ /**
+ * Here we have two ClaimsHandlers that can both add claims for the same claim type. By default we will
+ * combine the Claims into one Attribute.
+ */
+ @org.junit.Test
+ public void testSaml2CombinedClaimsMultipleClaimsHandlers() throws Exception {
+ TokenProvider samlTokenProvider = new SAMLTokenProvider();
+ TokenProviderParameters providerParameters =
+ createProviderParameters(WSS4JConstants.WSS_SAML2_TOKEN_TYPE, STSConstants.BEARER_KEY_KEYTYPE, null);
+
+ ClaimsManager claimsManager = new ClaimsManager();
+ ClaimsHandler claimsHandler = new CustomClaimsHandler();
+ ClaimsHandler claimsHandler2 = new CustomClaimsHandler();
+ ((CustomClaimsHandler) claimsHandler2).setRole("CustomRole");
+ claimsManager.setClaimHandlers(new ArrayList<ClaimsHandler>(Arrays.asList(claimsHandler, claimsHandler2)));
+ providerParameters.setClaimsManager(claimsManager);
+
+ ClaimCollection claims = new ClaimCollection();
+
+ Claim claim = new Claim();
+ claim.setClaimType(CustomClaimsHandler.ROLE_CLAIM);
+ claims.add(claim);
+
+ providerParameters.setRequestedPrimaryClaims(claims);
+
+ assertTrue(samlTokenProvider.canHandleToken(WSS4JConstants.WSS_SAML2_TOKEN_TYPE));
+ TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters);
+ assertTrue(providerResponse != null);
+ assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null);
+
+ Element token = (Element)providerResponse.getToken();
+ String tokenString = DOM2Writer.nodeToString(token);
+ assertTrue(tokenString.contains(providerResponse.getTokenId()));
+ assertTrue(tokenString.contains("AttributeStatement"));
+
+ String requiredClaim = CustomClaimsHandler.ROLE_CLAIM.toString();
+ assertTrue(tokenString.contains(requiredClaim));
+ assertTrue(tokenString.contains("DUMMY"));
+ assertTrue(tokenString.contains("CustomRole"));
+ // Check only one Role Claim
+ assertEquals(tokenString.indexOf(requiredClaim), tokenString.lastIndexOf(requiredClaim));
+ }
+
+ /**
+ * Here we have two ClaimsHandlers that can both add claims for the same claim type. We configure the
+ * SAMLTokenProvider not to combine the claims unlike the test above.
+ */
+ @org.junit.Test
+ public void testSaml2SeparateClaimsMultipleClaimsHandlers() throws Exception {
+ TokenProvider samlTokenProvider = new SAMLTokenProvider();
+ ((SAMLTokenProvider) samlTokenProvider).setCombineClaimAttributes(false);
+ TokenProviderParameters providerParameters =
+ createProviderParameters(WSS4JConstants.WSS_SAML2_TOKEN_TYPE, STSConstants.BEARER_KEY_KEYTYPE, null);
+
+ ClaimsManager claimsManager = new ClaimsManager();
+ ClaimsHandler claimsHandler = new CustomClaimsHandler();
+ ClaimsHandler claimsHandler2 = new CustomClaimsHandler();
+ ((CustomClaimsHandler) claimsHandler2).setRole("CustomRole");
+ claimsManager.setClaimHandlers(new ArrayList<ClaimsHandler>(Arrays.asList(claimsHandler, claimsHandler2)));
+ providerParameters.setClaimsManager(claimsManager);
+
+ ClaimCollection claims = new ClaimCollection();
+
+ Claim claim = new Claim();
+ claim.setClaimType(CustomClaimsHandler.ROLE_CLAIM);
+ claims.add(claim);
+
+ providerParameters.setRequestedPrimaryClaims(claims);
+
+ assertTrue(samlTokenProvider.canHandleToken(WSS4JConstants.WSS_SAML2_TOKEN_TYPE));
+ TokenProviderResponse providerResponse = samlTokenProvider.createToken(providerParameters);
+ assertTrue(providerResponse != null);
+ assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null);
+
+ Element token = (Element)providerResponse.getToken();
+ String tokenString = DOM2Writer.nodeToString(token);
+ assertTrue(tokenString.contains(providerResponse.getTokenId()));
+ assertTrue(tokenString.contains("AttributeStatement"));
+
+ String requiredClaim = CustomClaimsHandler.ROLE_CLAIM.toString();
+ assertTrue(tokenString.contains(requiredClaim));
+ assertTrue(tokenString.contains("DUMMY"));
+ assertTrue(tokenString.contains("CustomRole"));
+ // Check we have two Role Claims
+ assertNotEquals(tokenString.indexOf(requiredClaim), tokenString.lastIndexOf(requiredClaim));
+ }
private TokenProviderParameters createProviderParameters(
String tokenType, String keyType, String appliesTo
@@ -406,8 +493,6 @@ public class SAMLClaimsTest extends org.junit.Assert {
parameters.setEncryptionProperties(new EncryptionProperties());
-
-
return parameters;
}
--
To stop receiving notification emails like this one, please contact
coheigea@apache.org.