/*
* Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
*
* Licensed 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.opensaml.saml2.metadata.provider;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.QName;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.metadata.EntitiesDescriptor;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.RoleDescriptor;
import org.opensaml.xml.XMLObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A filter the removes roles, from an entity descriptor. For those roles specified within the SAML metadata
* specification the role element QName is used to identify the role. For other roles, those that appear as
* <RoleDescriptor xsi:type="someRoleType"> the role schema type is used to identify the role.
*
* If the entity descriptor does not contain any roles after filter it may, optionally be removed as well. If the root
* element of the metadata document is an entity descriptor it will never be removed, regardless of of whether it still
* contains roles.
*
* If and entities descriptor does not contains any entity descriptors after filter it may, optionally, be removed as
* well. If the root element of the metadata document is an entities descriptor it will never be removed, regardless of
* of whether it still contains entity descriptors.
*/
public class EntityRoleFilter implements MetadataFilter {
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(EntityRoleFilter.class);
/** List of roles that are NOT removed by this filter. */
private List<QName> roleWhiteList;
/** Whether to keep entity descriptors that contain no roles; default value: true. */
private boolean removeRolelessEntityDescriptors;
/** Whether to keep entities descriptors that contain no entity descriptors; default value: true. */
private boolean removeEmptyEntitiesDescriptors;
/** QName of extension role element. */
private final QName extRoleDescriptor = new QName(SAMLConstants.SAML20MD_NS, "RoleDescriptor");
/**
* Constructor.
*
* @param keptRoles list of roles NOT removed by this filter
*/
public EntityRoleFilter(List<QName> keptRoles) {
roleWhiteList = new ArrayList<QName>();
if (keptRoles != null) {
roleWhiteList.addAll(keptRoles);
}
removeRolelessEntityDescriptors = true;
removeEmptyEntitiesDescriptors = true;
}
/**
* Gets the unmodifiable list of roles that are NOT removed by this filter.
*
* @return unmodifiable list of roles that are NOT removed by this filter
*/
public List<QName> getRoleWhiteList() {
return Collections.unmodifiableList(roleWhiteList);
}
/**
* Gets whether to remove an entity descriptor if it does not contain any roles after filtering.
*
* @return whether to remove an entity descriptor if it does not contain any roles after filtering
*/
public boolean getRemoveRolelessEntityDescriptors() {
return removeRolelessEntityDescriptors;
}
/**
* Sets whether to remove an entity descriptor if it does not contain any roles after filtering.
*
* @param remove whether to remove an entity descriptor if it does not contain any roles after filtering
*/
public void setRemoveRolelessEntityDescriptors(boolean remove) {
removeRolelessEntityDescriptors = remove;
}
/**
* Gets whether to remove an entities descriptor if it does not contain any entity descriptor or entities
* descriptors.
*
* @return whether to remove an entities descriptor if it does not contain any entity descriptor or entities
* descriptors
*/
public boolean getRemoveEmptyEntitiesDescriptors() {
return removeEmptyEntitiesDescriptors;
}
/**
* Sets whether to remove an entities descriptor if it does not contain any entity descriptor or entities
* descriptors.
*
* @param remove whether to remove an entities descriptor if it does not contain any entity descriptor or entities
* descriptors
*/
public void setRemoveEmptyEntitiesDescriptors(boolean remove) {
removeEmptyEntitiesDescriptors = remove;
}
/** {@inheritDoc} */
public void doFilter(XMLObject metadata) throws FilterException {
if (metadata == null) {
return;
}
if (metadata instanceof EntitiesDescriptor) {
filterEntitiesDescriptor((EntitiesDescriptor) metadata);
} else {
filterEntityDescriptor((EntityDescriptor) metadata);
}
}
/**
* Filters entities descriptor.
*
* @param descriptor entities descriptor to filter
*
* @throws FilterException thrown if an effective role name can not be determined
*/
protected void filterEntitiesDescriptor(EntitiesDescriptor descriptor) throws FilterException {
// First we filter out any contained EntityDescriptors
List<EntityDescriptor> entityDescriptors = descriptor.getEntityDescriptors();
if (entityDescriptors != null && !entityDescriptors.isEmpty()) {
Iterator<EntityDescriptor> entityDescriptorsItr = entityDescriptors.iterator();
EntityDescriptor entityDescriptor;
List<RoleDescriptor> entityRoles;
while (entityDescriptorsItr.hasNext()) {
entityDescriptor = entityDescriptorsItr.next();
filterEntityDescriptor(entityDescriptor);
if (getRemoveRolelessEntityDescriptors()) {
entityRoles = entityDescriptor.getRoleDescriptors();
if (entityRoles == null || entityRoles.isEmpty()) {
log.trace("Filtering out entity descriptor {} from entity group {}", entityDescriptor
.getEntityID(), descriptor.getName());
entityDescriptorsItr.remove();
}
}
}
}
// Next, contained EntityDescriptors
List<EntitiesDescriptor> entitiesDescriptors = descriptor.getEntitiesDescriptors();
if (entitiesDescriptors != null && !entitiesDescriptors.isEmpty()) {
Iterator<EntitiesDescriptor> entitiesDescriptorsItr = entitiesDescriptors.iterator();
EntitiesDescriptor entitiesDescriptor;
while (entitiesDescriptorsItr.hasNext()) {
entitiesDescriptor = entitiesDescriptorsItr.next();
filterEntitiesDescriptor(entitiesDescriptor);
if (getRemoveEmptyEntitiesDescriptors()) {
// Remove the EntitiesDescriptor if does not contain any EntitiesDescriptors or EntityDescriptors
if ((entitiesDescriptor.getEntityDescriptors() == null || entitiesDescriptor.getEntityDescriptors()
.isEmpty())
&& (entitiesDescriptor.getEntitiesDescriptors() == null || entitiesDescriptor
.getEntitiesDescriptors().isEmpty())) {
log.trace("Filtering out entity descriptor {} from entity group {}", entitiesDescriptor
.getName(), descriptor.getName());
entitiesDescriptorsItr.remove();
}
}
}
}
}
/**
* Filters entity descriptor roles.
*
* @param descriptor entity descriptor to filter
*
* @throws FilterException thrown if an effective role name can not be determined
*/
protected void filterEntityDescriptor(EntityDescriptor descriptor) throws FilterException {
List<RoleDescriptor> roles = descriptor.getRoleDescriptors();
if (roles != null && !roles.isEmpty()) {
Iterator<RoleDescriptor> rolesItr = roles.iterator();
QName roleName;
while (rolesItr.hasNext()) {
roleName = getRoleName(rolesItr.next());
if (!roleWhiteList.contains(roleName)) {
log.trace("Filtering out role {} from entity {}", roleName, descriptor.getEntityID());
rolesItr.remove();
}
}
}
}
/**
* Gets the effective name for the role. This is either the element QName for roles defined within the SAML metadata
* specification or the element schema type QName for those that are not.
*
* @param role role to get the effective name for
*
* @return effective name of the role
*
* @throws FilterException thrown if the effective role name can not be determined
*/
protected QName getRoleName(RoleDescriptor role) throws FilterException {
QName roleName = role.getElementQName();
if (extRoleDescriptor.equals(roleName)) {
roleName = role.getSchemaType();
if (roleName == null) {
throw new FilterException("Role descriptor element was " + extRoleDescriptor
+ " but did not contain a schema type. This is illegal.");
}
}
return roleName;
}
}