/** * 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.mapper; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import org.apache.cxf.sts.claims.ProcessedClaim; import org.apache.cxf.sts.claims.ProcessedClaimCollection; /** * This claim util class provides methods to make the handling of claims and claim values easier. The input * claims (and their values) shall be treated as immutable. All util methods return a clone of the * provided claim containing the desired claim update. */ public class ClaimUtils { /** * @param collection Collection that should be used to add further claims to * @param claims Claims to be added to the provided collection * @return Returns clone of the provided collection including additional claims */ public ProcessedClaimCollection add(ProcessedClaimCollection collection, ProcessedClaim... claims) { ProcessedClaimCollection resultClaimCollection = null; if (collection != null) { resultClaimCollection = (ProcessedClaimCollection)collection.clone(); for (ProcessedClaim c : claims) { if (c != null) { resultClaimCollection.add(c); } } } return resultClaimCollection; } /** * @param collection Collection that should be used to add claims from the other provided claim * collections * @param claimCollections All claims contained within the provided collections will be added to the * targetCollection * @return Returns a clone of the provided collection containing all claims from all other claimCollections */ public ProcessedClaimCollection add(ProcessedClaimCollection collection, ProcessedClaimCollection... claimCollections) { ProcessedClaimCollection resultClaimCollection = null; if (collection != null) { resultClaimCollection = (ProcessedClaimCollection)collection.clone(); for (ProcessedClaimCollection cc : claimCollections) { resultClaimCollection.addAll(cc); } } return resultClaimCollection; } /** * @param processedClaimTypeURI claim type URI * @param values values of created claim. Can be null if no values shall be added to claim. * @return Returns new claim with provided claim type and values */ public ProcessedClaim create(String processedClaimTypeURI, String... values) { ProcessedClaim processedClaim = new ProcessedClaim(); if (processedClaimTypeURI != null) { processedClaim.setClaimType(URI.create(processedClaimTypeURI)); } if (values != null) { processedClaim.getValues().addAll(Arrays.asList(values)); } return processedClaim; } /** * @param processedClaims Collection of multiple claims with different claim types * @param processedClaimType URI of claim type to be selected from claim collection * @return Returns first claim from claims collection matching the provided claimType */ public ProcessedClaim get(ProcessedClaimCollection processedClaims, String processedClaimType) { if (processedClaimType == null || processedClaims == null) { return null; } for (ProcessedClaim c : processedClaims) { if (c.getClaimType() != null && processedClaimType.equals(c.getClaimType().toString())) { return c; } } return null; } /** * @param processedClaims Collection of claims to be mapped to a different claim type * @param map Map of old:new claim types * @param keepUnmapped if set to false only claims with a claim type contained in the map will be * returned. If set to false claims with an unmapped claim type will also be returned. * @return Returns claim collection with mapped claim types */ public ProcessedClaimCollection mapType(ProcessedClaimCollection processedClaims, Map<String, String> map, boolean keepUnmapped) { ProcessedClaimCollection mappedProcessedClaims = new ProcessedClaimCollection(); if (processedClaims != null && map != null) { for (ProcessedClaim c : processedClaims) { String processedClaimType = (c.getClaimType() != null) ? c.getClaimType().toString() : ""; String mappedProcessedClaimType = map.get(processedClaimType); if (mappedProcessedClaimType != null) { ProcessedClaim processedClaim = c.clone(); processedClaim.setClaimType(URI.create(mappedProcessedClaimType)); mappedProcessedClaims.add(processedClaim); } else if (keepUnmapped) { mappedProcessedClaims.add(c.clone()); } } } return mappedProcessedClaims; } /** * Mapping all values from the given claim according to the provided map. Input claims will not be * modified. Result claim will be a clone of the provided claims just with different (mapped) claim * values. * * @param processedClaim Claim providing values to be mapped * @param map Map of old:new mapping values * @param keepUnmapped if set to false only values contained in the map will be returned. If set to true, * values not contained in the map will also remain in the returned claim. * @return Returns the provided claim with mapped values */ public ProcessedClaim mapValues(ProcessedClaim processedClaim, Map<Object, Object> mapping, boolean keepUnmapped) { ProcessedClaim resultClaim = null; if (processedClaim != null) { resultClaim = processedClaim.clone(); List<Object> values = resultClaim.getValues(); List<Object> mappedValues = new ArrayList<>(); if (values == null || mapping == null || mapping.size() == 0) { resultClaim.setValues(mappedValues); return resultClaim; } for (Object value : values) { Object newValue = mapping.get(value); if (newValue != null) { mappedValues.add(newValue); } else if (keepUnmapped) { mappedValues.add(value); } } resultClaim.setValues(mappedValues); } return resultClaim; } /** * Filtering all values from the given claim according to the provided regex filter. Input claims will not * be modified. Result claim will be a clone of the provided claims just possible fewer (filtered) claim * values. * * @param processedClaim Claim containing arbitrary values * @param filter Regex filter to be used to match with claim values * @return Returns a claim containing only values from the processedClaim which matched the provided * filter */ public ProcessedClaim filterValues(ProcessedClaim processedClaim, String filter) { ProcessedClaim resultClaim = null; if (processedClaim != null) { resultClaim = processedClaim.clone(); List<Object> values = resultClaim.getValues(); List<Object> filteredValues = new ArrayList<>(); if (values == null || filter == null) { resultClaim.setValues(filteredValues); return resultClaim; } for (Object value : values) { if (value != null && value.toString().matches(filter)) { filteredValues.add(value); } } resultClaim.setValues(filteredValues); } return resultClaim; } /** * Merges the first value (only) from different claim types in a collection to a new claim type separated * by the provided delimiter. * * @param processedClaims Collection of claims containing claims with claim types of listed * <code>claimType</code> array * @param targetClaimType claim type URI of merged result claim * @param delimiter Delimiter added between multiple claim types. Value can be <code>null</code>. * @param processedClaimType URIs of claim types to be merged. Merging will be in the same order as the * provided claim type URIs. If a claim type is not found in the collection this claim type * will be omitted. * @return Returns merged claim of all found claim types */ public ProcessedClaim merge(ProcessedClaimCollection processedClaims, String targetClaimType, String delimiter, String... processedClaimType) { ProcessedClaim mergedProcessedClaim = null; StringBuilder sbProcessedClaimValue = new StringBuilder(); for (String sc : processedClaimType) { ProcessedClaim c = get(processedClaims, sc); if (c != null) { List<Object> values = c.getValues(); if (values != null && !values.isEmpty()) { if (mergedProcessedClaim == null) { // First match TODO refactor for better method override mergedProcessedClaim = c.clone(); sbProcessedClaimValue.append(values.get(0)); mergedProcessedClaim.getValues().clear(); } else { sbProcessedClaimValue.append(delimiter).append(values.get(0)); } } } } if (mergedProcessedClaim != null) { mergedProcessedClaim.setClaimType(URI.create(targetClaimType)); mergedProcessedClaim.addValue(sbProcessedClaimValue.toString()); } return mergedProcessedClaim; } /** * @param processedClaim Claim to be updated * @param processedClaimTypeURI URI as String to be set as claim type in provided claim * @return Returns updated claim */ public ProcessedClaim setType(ProcessedClaim processedClaim, String processedClaimTypeURI) { if (processedClaim != null && processedClaimTypeURI != null) { processedClaim.setClaimType(URI.create(processedClaimTypeURI)); } return processedClaim; } /** * All claims within the provided collection will be updated in the following manner: If no original * issuer is set, the issuer in the provided claims will be set as original issuer. If an original issuer * was already set before, the original issuer will not be updated. All claims will be updated to have the * provided issuer name be set as the claim issuer. * * @param processedClaims Collection of claims to be updated * @param issuerName Issuer to be set for all claims within the collection * @return Returns a new claim collection with clones of updated claims */ public ProcessedClaimCollection updateIssuer(ProcessedClaimCollection processedClaims, String newIssuer) { ProcessedClaimCollection resultClaimCollection = null; if (processedClaims != null) { resultClaimCollection = new ProcessedClaimCollection(); for (ProcessedClaim c : processedClaims) { ProcessedClaim newClaim = c.clone(); if (newClaim.getOriginalIssuer() == null) { newClaim.setOriginalIssuer(newClaim.getIssuer()); } newClaim.setIssuer(newIssuer); resultClaimCollection.add(newClaim); } } return resultClaimCollection; } /** * @param processedClaim values of this claim will be used for result claim * @return Returns clone of the provided claim with values all in uppercase format */ public ProcessedClaim upperCaseValues(ProcessedClaim processedClaim) { ProcessedClaim resultClaim = null; if (processedClaim != null) { resultClaim = processedClaim.clone(); if (resultClaim.getValues() != null) { List<Object> oldValues = resultClaim.getValues(); List<Object> newValues = new ArrayList<>(); for (Object value : oldValues) { newValues.add(value.toString().toUpperCase()); } resultClaim.getValues().clear(); resultClaim.getValues().addAll(newValues); } } return resultClaim; } /** * @param processedClaim values of this claim will be used for result claim * @return Returns clone of provided claim with values all in lowercase format */ public ProcessedClaim lowerCaseValues(ProcessedClaim processedClaim) { ProcessedClaim resultClaim = null; if (processedClaim != null) { resultClaim = processedClaim.clone(); if (resultClaim.getValues() != null) { List<Object> oldValues = resultClaim.getValues(); List<Object> newValues = new ArrayList<>(); for (Object value : oldValues) { newValues.add(value.toString().toLowerCase()); } resultClaim.getValues().clear(); resultClaim.getValues().addAll(newValues); } } return resultClaim; } /** * @param processedClaim Claim providing values to be wrapped * @param prefix Prefix to be added to each claim value. Can be null. * @param suffix Suffix to be appended to each claim value. Can be null. * @return Returns a clone of the the provided claim with wrapped values */ public ProcessedClaim wrapValues(ProcessedClaim processedClaim, String prefix, String suffix) { prefix = (prefix == null) ? "" : prefix; suffix = (suffix == null) ? "" : suffix; ProcessedClaim resultClaim = null; if (processedClaim != null) { resultClaim = processedClaim.clone(); if (resultClaim.getValues() != null) { List<Object> oldValues = resultClaim.getValues(); List<Object> newValues = new ArrayList<>(); for (Object value : oldValues) { newValues.add(prefix + value.toString() + suffix); } resultClaim.getValues().clear(); resultClaim.getValues().addAll(newValues); } } return resultClaim; } /** * This function is especially useful if multi values from a claim are stored within a single value entry. * For example multi user roles could all be stored in a single value element separated by comma: * USER,MANAGER,ADMIN The result of this function will provide a claim with three distinct values: USER * and MANAGER and ADMIN. * * @param processedClaim claim containing multi-values in a single value entry * @param delimiter Delimiter to split multi-values into single values * @return Returns a clone of the provided claim containing only single values per value entry */ public ProcessedClaim singleToMultiValue(ProcessedClaim processedClaim, String delimiter) { ProcessedClaim resultClaim = null; if (processedClaim != null) { resultClaim = processedClaim.clone(); if (resultClaim.getValues() != null) { List<Object> oldValues = resultClaim.getValues(); List<Object> newValues = new ArrayList<>(); for (Object value : oldValues) { String multivalue = value.toString(); StringTokenizer st = new StringTokenizer(multivalue, delimiter); while (st.hasMoreTokens()) { newValues.add(st.nextToken()); } } resultClaim.getValues().clear(); resultClaim.getValues().addAll(newValues); } } return resultClaim; } /** * This function is especially useful if values from multiple claim values need to be condensed into a * single value element. For example a user has three roles: USER and MANAGER and ADMIN. If ',' is used as * a delimiter, then this method would provide the following claim with only a single value looking like * this: USER,MANAGER,ADMIN * * @param processedClaim claim containing multi-values * @param delimiter Delimiter to concatenate multi-values into a single value * @return Returns a clone of the provided claim containing only one single value */ public ProcessedClaim multiToSingleValue(ProcessedClaim processedClaim, String delimiter) { ProcessedClaim resultClaim = null; if (processedClaim != null) { resultClaim = processedClaim.clone(); if (resultClaim.getValues() != null) { List<Object> oldValues = resultClaim.getValues(); boolean first = true; StringBuilder sb = new StringBuilder(); for (Object value : oldValues) { if (first) { sb.append(value); first = false; } else { sb.append(delimiter).append(value); } } resultClaim.getValues().clear(); resultClaim.getValues().add(sb.toString()); } } return resultClaim; } /** * This function removes duplicated values. * * @param processedClaim claim containing multi-values of which some might be duplicated * @return Returns a clone of the provided claim containing only distinct values */ public ProcessedClaim distinctValues(ProcessedClaim processedClaim) { ProcessedClaim resultClaim = null; if (processedClaim != null) { resultClaim = processedClaim.clone(); if (resultClaim.getValues() != null) { List<Object> oldValues = resultClaim.getValues(); Set<Object> distincValues = new LinkedHashSet<>(oldValues); resultClaim.getValues().clear(); resultClaim.getValues().addAll(distincValues); } } return resultClaim; } /** * Removes Claims without values. * * @param processedClaims Collection of claims with and/or without values * @return Returns a collection of claims which contain values only */ public ProcessedClaimCollection removeEmptyClaims(ProcessedClaimCollection processedClaims) { ProcessedClaimCollection resultClaimCollection = null; if (processedClaims != null) { resultClaimCollection = new ProcessedClaimCollection(); for (ProcessedClaim c : processedClaims) { if (c.getValues() != null && c.getValues().size() > 0) { resultClaimCollection.add(c); } } } return resultClaimCollection; } }