/*
* Copyright [2006] [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.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import javax.xml.namespace.QName;
import org.opensaml.saml2.common.SAML2Helper;
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.opensaml.xml.io.Unmarshaller;
import org.opensaml.xml.io.UnmarshallingException;
import org.opensaml.xml.parse.ParserPool;
import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
/**
* An abstract, base, implementation of a metadata provider.
*/
public abstract class AbstractMetadataProvider extends BaseMetadataProvider {
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(AbstractMetadataProvider.class);
/** Cache of entity IDs to their descriptors. */
private HashMap<String, EntityDescriptor> indexedDescriptors;
/** Pool of parsers used to process XML. */
private ParserPool parser;
/** Constructor. */
public AbstractMetadataProvider() {
super();
indexedDescriptors = new HashMap<String, EntityDescriptor>();
}
/** {@inheritDoc} */
public EntitiesDescriptor getEntitiesDescriptor(String name) throws MetadataProviderException {
XMLObject metadata = getMetadata();
if (metadata instanceof EntitiesDescriptor) {
EntitiesDescriptor descriptor = (EntitiesDescriptor) metadata;
return getEntitiesDescriptorByName(name, descriptor);
}
return null;
}
/** {@inheritDoc} */
public EntityDescriptor getEntityDescriptor(String entityID) throws MetadataProviderException {
log.debug("Getting descriptor for entity {}", entityID);
XMLObject metadata = getMetadata();
EntityDescriptor descriptor = getEntityDescriptorById(entityID, metadata);
if (descriptor == null) {
log.debug("Metadata document does not contain an entity descriptor with the ID {}", entityID);
return null;
}
return descriptor;
}
/** {@inheritDoc} */
public List<RoleDescriptor> getRole(String entityID, QName roleName) throws MetadataProviderException {
EntityDescriptor entity = getEntityDescriptor(entityID);
if (entity != null) {
return entity.getRoleDescriptors(roleName);
} else {
return null;
}
}
/** {@inheritDoc} */
public RoleDescriptor getRole(String entityID, QName roleName, String supportedProtocol)
throws MetadataProviderException {
List<RoleDescriptor> roles = getRole(entityID, roleName);
if (roles == null) {
return null;
}
Iterator<RoleDescriptor> rolesItr = roles.iterator();
RoleDescriptor role;
while (rolesItr.hasNext()) {
role = rolesItr.next();
if (role.isSupportedProtocol(supportedProtocol)) {
return role;
}
}
return null;
}
/**
* Gets the pool of parsers to use to parse XML.
*
* @return pool of parsers to use to parse XML
*/
public ParserPool getParserPool() {
return parser;
}
/**
* Sets the pool of parsers to use to parse XML.
*
* @param pool pool of parsers to use to parse XML
*/
public void setParserPool(ParserPool pool) {
parser = pool;
}
/**
* Clears the entity ID to entity descriptor index.
*/
protected void clearDescriptorIndex() {
indexedDescriptors.clear();
}
/**
* Unmarshalls the metadata from the given stream. The stream is closed by this method and the returned metadata
* released its DOM representation.
*
* @param metadataInput the input reader to the metadata.
*
* @return the unmarshalled metadata
*
* @throws UnmarshallingException thrown if the metadata can no be unmarshalled
*/
protected XMLObject unmarshallMetadata(InputStream metadataInput) throws UnmarshallingException {
try {
log.trace("Parsing retrieved metadata into a DOM object");
Document mdDocument = parser.parse(metadataInput);
log.trace("Unmarshalling and caching metdata DOM");
Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(mdDocument.getDocumentElement());
XMLObject metadata = unmarshaller.unmarshall(mdDocument.getDocumentElement());
return metadata;
} catch (Exception e) {
throw new UnmarshallingException(e);
} finally {
try {
metadataInput.close();
} catch (IOException e) {
// ignore
}
}
}
/**
* Filters the given metadata.
*
* @param metadata the metadata to be filtered
*
* @throws FilterException thrown if there is an error filtering the metadata
*/
protected void filterMetadata(XMLObject metadata) throws FilterException {
if (getMetadataFilter() != null) {
log.debug("Applying metadata filter");
getMetadataFilter().doFilter(metadata);
}
}
/**
* Releases the DOM representation from the metadata object.
*
* @param metadata the metadata object
*/
protected void releaseMetadataDOM(XMLObject metadata) {
if (metadata != null) {
metadata.releaseDOM();
metadata.releaseChildrenDOM(true);
}
}
/**
* Gets the EntityDescriptor with the given ID from the cached metadata.
*
* @param entityID the ID of the entity to get the descriptor for
* @param metadata metadata associated with the entity
*
* @return the EntityDescriptor
*/
protected EntityDescriptor getEntityDescriptorById(String entityID, XMLObject metadata) {
EntityDescriptor descriptor = null;
log.debug("Searching for entity descriptor with an entity ID of {}", entityID);
if (indexedDescriptors.containsKey(entityID)) {
log.debug("Entity descriptor for the ID {} was found in index cache, returning", entityID);
descriptor = indexedDescriptors.get(entityID);
if (isValid(descriptor)) {
return descriptor;
} else {
indexedDescriptors.remove(descriptor);
}
}
if (metadata != null) {
if (metadata instanceof EntityDescriptor) {
log.debug("Metadata root is an entity descriptor, checking if it's the one we're looking for.");
descriptor = (EntityDescriptor) metadata;
if (!descriptor.getEntityID().equals(entityID) || !isValid(descriptor)) {
log.debug("Entity descriptor does not have the correct entity ID or is not valid, returning null");
descriptor = null;
}
} else {
log.debug("Metadata was an EntitiesDescriptor, checking if any of its descendant EntityDescriptor elements is the one we're looking for.");
if (metadata instanceof EntitiesDescriptor) {
descriptor = getEntityDescriptorById(entityID, (EntitiesDescriptor) metadata);
}
}
}
if (descriptor != null) {
log.debug("Located entity descriptor, creating an index to it for faster lookups");
indexedDescriptors.put(entityID, descriptor);
}
return descriptor;
}
/**
* Gets the entity descriptor with the given ID that is a descedant of the given entities descriptor.
*
* @param entityID the ID of the entity whose descriptor is to be fetched
* @param descriptor the entities descriptor
*
* @return the entity descriptor
*/
protected EntityDescriptor getEntityDescriptorById(String entityID, EntitiesDescriptor descriptor) {
log.trace("Checking to see if any of the child entity descriptors of entities descriptor {} is the requested descriptor", descriptor.getName());
List<EntityDescriptor> entityDescriptors = descriptor.getEntityDescriptors();
if (entityDescriptors != null) {
for (EntityDescriptor entityDescriptor : entityDescriptors) {
log.trace("Checking entity descriptor with entity ID {}", entityDescriptor.getEntityID());
if (entityDescriptor.getEntityID().equals(entityID) && isValid(entityDescriptor)) {
return entityDescriptor;
}
}
}
log.trace("Checking to see if any of the child entities descriptors contains the entity descriptor requested");
EntityDescriptor entityDescriptor;
List<EntitiesDescriptor> entitiesDescriptors = descriptor.getEntitiesDescriptors();
if (entitiesDescriptors != null) {
for (EntitiesDescriptor entitiesDescriptor : descriptor.getEntitiesDescriptors()) {
entityDescriptor = getEntityDescriptorById(entityID, entitiesDescriptor);
if (entityDescriptor != null) {
// We don't need to check for validity because getEntityDescriptorById only returns a valid
// descriptor
return entityDescriptor;
}
}
}
return null;
}
/**
* Gets the entities descriptor with the given name.
*
* @param name name of the entities descriptor
* @param rootDescriptor the root descriptor to search in
*
* @return the EntitiesDescriptor with the given name
*/
protected EntitiesDescriptor getEntitiesDescriptorByName(String name, EntitiesDescriptor rootDescriptor) {
EntitiesDescriptor descriptor = null;
if (DatatypeHelper.safeEquals(name, rootDescriptor.getName()) && isValid(rootDescriptor)) {
descriptor = rootDescriptor;
} else {
List<EntitiesDescriptor> childDescriptors = rootDescriptor.getEntitiesDescriptors();
if (childDescriptors != null) {
for (EntitiesDescriptor childDescriptor : childDescriptors) {
childDescriptor = getEntitiesDescriptorByName(name, childDescriptor);
if (childDescriptor != null) {
descriptor = childDescriptor;
}
}
}
}
return descriptor;
}
/**
* Returns whether the given descriptor is valid. If valid metadata is not required this method always returns true.
*
* @param descriptor the descriptor to check
*
* @return true if valid metadata is not required or the given descriptor is valid, false otherwise
*/
protected boolean isValid(XMLObject descriptor) {
if (!requireValidMetadata()) {
return true;
}
return SAML2Helper.isValid(descriptor);
}
}