/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig 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 the following location:
*
* 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.jasig.cas.services.support;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import java.util.regex.Pattern;
import javax.validation.constraints.NotNull;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.RegisteredServiceAttributeFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The regex filter that is responsible to make sure only attributes that match a certain regex pattern
* registered service are released.
*
* @author Misagh Moayyed
* @since 4.0.0
*/
public class RegisteredServiceRegexAttributeFilter implements RegisteredServiceAttributeFilter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public RegisteredServiceRegexAttributeFilter(final String regex) {
this.pattern = Pattern.compile(regex);
}
@NotNull
private Pattern pattern;
/**
* {@inheritDoc}
*
* Given attribute values may be an extension of {@link Collection}, {@link Map} or an array.
* <ul>
* <li>The filtering operation is non-recursive. </li>
* <li>Multi-valued attributes such as those of type {@link Collection} and
* {@link Map} are expected to allow casting to <code>Map<String, String></code>
* or <code>Collection<String></code>.
* Values that are of type array are expected to allow casting to <code>String[]</code>.
* </li>
* <li>Multi-valued attributes are always put back into the final released collection of
* attributes as <code>String[]</code>.</li>
* <li>If the final filtered collection is empty, it will not be put into the collection of attributes.</li>
* </ul>
*/
@Override
@SuppressWarnings("unchecked")
public Map<String, Object> filter(final String principalId, final Map<String, Object> givenAttributes,
final RegisteredService registeredService) {
final Map<String, Object> attributesToRelease = new HashMap<String, Object>();
for (final String attributeName : givenAttributes.keySet()) {
final Object attributeValue = givenAttributes.get(attributeName);
logger.debug("Received attribute [{}] with value [{}]", attributeName, attributeValue);
if (attributeValue != null) {
if (attributeValue instanceof Collection) {
final String[] filteredAttributes = filterArrayAttributes(
((Collection<String>) attributeValue).toArray(new String[] {}), attributeName);
if (filteredAttributes.length > 0) {
attributesToRelease.put(attributeName, filteredAttributes);
}
} else if (attributeValue.getClass().isArray()) {
final String[] filteredAttributes = filterArrayAttributes((String[]) attributeValue, attributeName);
if (filteredAttributes.length > 0) {
attributesToRelease.put(attributeName, filteredAttributes);
}
} else if (attributeValue instanceof Map) {
final Map<String, String> filteredAttributes = filterMapAttributes((Map<String, String>) attributeValue);
if (filteredAttributes.size() > 0) {
attributesToRelease.put(attributeName, filteredAttributes);
}
} else if (patternMatchesAttributeValue(attributeValue.toString())) {
logReleasedAttributeEntry(attributeName, attributeValue.toString());
attributesToRelease.put(attributeName, attributeValue);
}
}
}
logger.debug("Received {} attributes. Filtered and released {}", givenAttributes.size(),
attributesToRelease.size());
return attributesToRelease;
}
private Map<String, String> filterMapAttributes(final Map<String, String> valuesToFilter) {
final Map<String, String> attributesToFilter = new HashMap<String, String>(valuesToFilter.size());
for (final String attributeName : valuesToFilter.keySet()) {
final String attributeValue = valuesToFilter.get(attributeName);
if (patternMatchesAttributeValue(attributeValue)) {
logReleasedAttributeEntry(attributeName, attributeValue);
attributesToFilter.put(attributeName, valuesToFilter.get(attributeName));
}
}
return attributesToFilter;
}
private boolean patternMatchesAttributeValue(final String value) {
return this.pattern.matcher(value).matches();
}
private String[] filterArrayAttributes(final String[] valuesToFilter, final String attributeName) {
final Vector<String> vector = new Vector<String>(valuesToFilter.length);
for (final String attributeValue : valuesToFilter) {
if (patternMatchesAttributeValue(attributeValue)) {
logReleasedAttributeEntry(attributeName, attributeValue);
vector.add(attributeValue);
}
}
return vector.toArray(new String[] {});
}
private void logReleasedAttributeEntry(final String attributeName, final String attributeValue) {
logger.debug("The attribute value [{}] for attribute name {} matches the pattern {}. Releasing attribute...",
attributeValue, attributeName, this.pattern.pattern());
}
}