All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dk.itst.oiosaml.sp.metadata.IdpMetadata Maven / Gradle / Ivy

Go to download

SAML Servlet Filter, configured to work with the danish SAML profile OIOSAML 2.0.9

The newest version!
/*
 * The contents of this file are subject to the Mozilla Public 
 * License Version 1.1 (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.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an 
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express 
 * or implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 *
 * The Original Code is OIOSAML Java Service Provider.
 * 
 * The Initial Developer of the Original Code is Trifork A/S. Portions 
 * created by Trifork A/S are Copyright (C) 2008 Danish National IT 
 * and Telecom Agency (http://www.itst.dk). All Rights Reserved.
 * 
 * Contributor(s):
 *   Joakim Recht 
 *   Rolf Njor Jensen 
 *   Aage Nielsen 
 *   Carsten Larsen 
 *
 */
package dk.itst.oiosaml.sp.metadata;

import dk.itst.oiosaml.configuration.SAMLConfiguration;
import dk.itst.oiosaml.configuration.SAMLConfigurationFactory;
import dk.itst.oiosaml.error.Layer;
import dk.itst.oiosaml.error.WrappedException;
import dk.itst.oiosaml.logging.Logger;
import dk.itst.oiosaml.logging.LoggerFactory;
import dk.itst.oiosaml.security.SecurityHelper;
import dk.itst.oiosaml.sp.service.util.Constants;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.metadata.ArtifactResolutionService;
import org.opensaml.saml2.metadata.AttributeAuthorityDescriptor;
import org.opensaml.saml2.metadata.AttributeService;
import org.opensaml.saml2.metadata.Endpoint;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.IDPSSODescriptor;
import org.opensaml.saml2.metadata.KeyDescriptor;
import org.opensaml.saml2.metadata.SingleLogoutService;
import org.opensaml.saml2.metadata.SingleSignOnService;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.security.credential.UsageType;
import org.opensaml.xml.signature.X509Data;

import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Utility class to extract relevant values of the meta data related to the Login Site.
 * 
 * @author Joakim Recht 
 * @author Rolf Njor Jensen 
 * @author Aage Nielsen 
 * @author Carsten Larsen 
 * 
 */
public class IdpMetadata {
	public static final String VERSION = "$Id: IdpMetadata.java 2964 2008-06-02 11:34:06Z jre $";
	public static final String METADATA_DIRECTORY = "common.saml2.metadata.idp.directory";
	private static IdpMetadata instance;

	private static final Logger log = LoggerFactory.getLogger(IdpMetadata.class);

	private final Map metadata = new HashMap();

	public IdpMetadata(String protocol, EntityDescriptor ... entityDescriptor) {
		for (EntityDescriptor descriptor : entityDescriptor) {
			if (metadata.containsKey(descriptor.getEntityID())) {
				metadata.get(descriptor.getEntityID()).addCertificates(new Metadata(descriptor, protocol).getCertificates());
			} else {
				metadata.put(descriptor.getEntityID(), new Metadata(descriptor, protocol));
			}
		}
	}

	public static IdpMetadata getInstance() {
		if (instance == null) {
			SAMLConfiguration configuration = SAMLConfigurationFactory.getConfiguration();
			String protocol = configuration.getSystemConfiguration().getString(Constants.PROP_PROTOCOL);
			List descriptors = configuration.getListOfIdpMetadata();
			instance = new IdpMetadata(protocol, descriptors.toArray(new EntityDescriptor[descriptors.size()]));
		}
		return instance ;
	}
	
	public static void setMetadata(IdpMetadata metadata) {
		instance = metadata;
	}

	public Metadata getMetadata(String entityID) {
		Metadata md = metadata.get(entityID);
		if (md == null) {
			throw new IllegalArgumentException("No metadata found for " + entityID);
		}
		return md;
	}

	/**
	 * Check if SAML Discovery Profile should be enabled.
	 * 
	 * If more than one metadata file exists, discovery should be enabled, and this method will return true.
	 */
	public boolean enableDiscovery() {
		return metadata.size() > 1;
	}

	/**
	 * Get the first registered metadata.
	 * 
	 * This method should only be used when {@link #enableDiscovery()} returns true, as the 
	 * metadata list is not ordered.
	 */
	public Metadata getFirstMetadata() {
		return getMetadata(getEntityIDs().iterator().next());
	}

	public Collection getEntityIDs() {
		return metadata.keySet();
	}


	public Metadata findSupportedEntity(String ... entityIds) {
		for (String entityId : entityIds) {
			Metadata md = metadata.get(entityId);
			if (md != null) {
				return md;
			}
		}
		log.debug("No supported idp found in " + Arrays.toString(entityIds) + ". Supported ids: " + metadata.keySet());
		return null;
	}

	public static class Metadata {
		private EntityDescriptor entityDescriptor;
		private IDPSSODescriptor idpSSODescriptor;
		private Collection certificates = new ArrayList();
		private Map validCertificates = new HashMap();

		private Metadata(EntityDescriptor entityDescriptor, String protocol) {
			this.entityDescriptor = entityDescriptor;
			idpSSODescriptor = entityDescriptor.getIDPSSODescriptor(protocol);
			try {
				X509Certificate cert = SecurityHelper.buildJavaX509Cert(getCertificateNode().getValue());
				certificates.add(cert);
			} catch (CertificateException e) {
				throw new WrappedException(Layer.BUSINESS, e);
			}
		}

		public void addCertificates(Collection certs) {
			this.certificates.addAll(certs);
		}

		/**
		 * 
		 * @return The entityID of the Login Site
		 */
		public String getEntityID() {
			return entityDescriptor.getEntityID();
		}

		/**
		 * 
		 * @return The location (URL) of {@link ArtifactResolutionService}.
		 */
		public String getArtifactResolutionServiceLocation(String binding) throws IllegalArgumentException {
			for (ArtifactResolutionService artifactResolutionService : idpSSODescriptor.getArtifactResolutionServices()) {
				if (SAMLConstants.SAML2_SOAP11_BINDING_URI.equals(artifactResolutionService.getBinding())) {
					return artifactResolutionService.getLocation();
				}
			}
			throw new IllegalArgumentException("No artifact resolution service for binding " + binding);
		}

		/**
		 * Get a signon service location for a specific binding.
		 * @param binding SAML binding name,
		 * @return The url for the location.
		 * @throws IllegalArgumentException if the binding is not present in metadata.
		 */
		public String getSingleSignonServiceLocation(String binding) throws IllegalArgumentException {
			for (SingleSignOnService service : idpSSODescriptor.getSingleSignOnServices()) {
				if (service.getBinding().equals(binding)) {
					return service.getLocation();
				}
			}
			throw new IllegalArgumentException("Binding " + binding + " not found");
		}
		
		public String getAttributeQueryServiceLocation(String binding) throws IllegalArgumentException {
			AttributeAuthorityDescriptor descriptor = entityDescriptor.getAttributeAuthorityDescriptor(SAMLConstants.SAML20P_NS);
			if (descriptor == null) throw new IllegalArgumentException("Metadata does not contain a AttributeAuthorityDescriptor");
			for (AttributeService service : descriptor.getAttributeServices()) {
				if (binding.equals(service.getBinding())) {
					return service.getLocation();
				}
			}
			throw new IllegalArgumentException("Binding " + binding + " not found in AttributeServices");
		}

		public List getSingleSignonServices() {
			return idpSSODescriptor.getSingleSignOnServices();
		}
		/**
		 * 
		 * @return The location (URL) of {@link SingleSignOnService} at the Login Site
		 */
		public String getSingleLogoutServiceLocation() {
			String url = null;
			if (idpSSODescriptor.getSingleLogoutServices().size() > 0) {
				SingleLogoutService singleLogoutService = idpSSODescriptor.getSingleLogoutServices().get(0);
				url = singleLogoutService.getLocation();
			}
			return url;
		}

		/**
		 * 
		 * @return The response location (URL) of {@link SingleSignOnService} at the Login Site
		 */
		public String getSingleLogoutServiceResponseLocation() {
			if (idpSSODescriptor.getSingleLogoutServices().size() > 0) {
			    List singleLogoutServices = idpSSODescriptor.getSingleLogoutServices();

                // Prefer POST binding - due to browser redirect limitations.
			    SingleLogoutService singleLogoutService = idpSSODescriptor.getSingleLogoutServices().get(0);
			    for (SingleLogoutService sls : singleLogoutServices) {
			        if(sls.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) {
			            singleLogoutService = sls;
			            break;
			        }
                }

			    String location = singleLogoutService.getResponseLocation();
				if (location == null) {
					location = singleLogoutService.getLocation();
				}
				return location;
			}
			return null;
		}


		/**
		 * 
		 * @return The certificate node from the metadata associated with the Login
		 *         Site
		 */
		private org.opensaml.xml.signature.X509Certificate getCertificateNode() {
			if (idpSSODescriptor != null && idpSSODescriptor.getKeyDescriptors().size() > 0) {
				KeyDescriptor keyDescriptor = null;
				KeyDescriptor keyDescriptorUnspecified = null;
				
				for (KeyDescriptor kd : idpSSODescriptor.getKeyDescriptors()) {
					if (kd.getUse().equals(UsageType.SIGNING)) {
						keyDescriptor = kd;
					}
					else if (kd.getUse().equals(UsageType.UNSPECIFIED)) {
						keyDescriptorUnspecified = kd;
					}
				}
				
				// fallback to any unspecified keyDescriptor
				if (keyDescriptor == null) {
					keyDescriptor = keyDescriptorUnspecified;
				}
				
				if (keyDescriptor == null) {
					throw new IllegalStateException("IdP Metadata does not contain a KeyDescriptor for signing: " + getEntityID());
				}

				if (keyDescriptor.getKeyInfo().getX509Datas().size() > 0) {
					X509Data x509Data = keyDescriptor.getKeyInfo().getX509Datas().get(0);
					if (x509Data.getX509Certificates().size() > 0) {
						  return x509Data.getX509Certificates().get(0);
					}
				}
			}
			throw new IllegalStateException("IdP Metadata does not contain a certificate: " + getEntityID());
		}
		
		Collection getAllCertificates() {
			return certificates;
		}

		/**
		 * Get a list of all certificates for this IdP.
		 * 
		 * Any expired certificates will not be included in the list.
		 */
		public Collection getCertificates() {
			Collection res = new ArrayList();
			for (X509Certificate cert: certificates) {
				if (cert.getNotAfter().after(new Date())) {
					res.add(cert);
				} else {
					log.debug("Local Metadata certificateValidated for " + getEntityID() + " expired at " + cert.getNotAfter() + ", current: " + new Date());
				}
			}
			return res;
		}

        /**
         * Get a list of all certificates for this IdP.
         *
         * Any expired or revoked certificates will not be included in the list.
         */
        public Collection getValidCertificates() {
            Collection res = new ArrayList();
            for (X509Certificate cert: validCertificates.keySet()) {
                if (cert.getNotAfter().after(new Date())) {
                    res.add(cert);
                } else {
                    log.debug("Local Metadata certificateValidated for " + getEntityID() + " expired at " + cert.getNotAfter() + ", current: " + new Date());
                }
            }
            return res;
        }

		public void setCertificateValid(X509Certificate cert, boolean valid) {
			if (valid) {
                // HashMap does not add the cert if it already existed in the Map. Instead it is replaced with the current time of validation.
                validCertificates.put(cert, new Date());
			} else {
				validCertificates.remove(cert);
			}
		}

        /**
         * Returns the time for last successful certificate validation.
         * @param certificate certificate to from to get the time for last successful certificate validation.
         * @return the time for last successful certificate validation or null if certificate does not exist.
         */
        public Date getLastTimeForCertificationValidation(X509Certificate certificate){
            return validCertificates.get(certificate);
        }
		

		/**
		 * Find a supported login endpoint.
		 * @throws IllegalArgumentException If no services match the selected bindings. 
		 */
		public Endpoint findLoginEndpoint(String[] bindings) {
			if (bindings == null) throw new IllegalArgumentException("bindings cannot be null");
			
			for (String binding : bindings) {
				for (SingleSignOnService service : idpSSODescriptor.getSingleSignOnServices()) {
					if (service.getBinding().equalsIgnoreCase(binding)) {
						return service;
					}
				}
			}
			throw new IllegalArgumentException("No SingleSignonService found for " + Arrays.toString(bindings));
		}
		
		/**
		 * Get the name format for an attribute.
		 * 
		 * @param attribute The attribute to look for.
		 * @param defaultFormat The format to return if the attribute is not present in idp metadata.
		 */
		public String getAttributeNameFormat(String attribute, String defaultFormat) {
			for (Attribute attr : idpSSODescriptor.getAttributes()) {
				if (attribute.equals(attr.getName())) {
					return attr.getNameFormat();
				}
			}
			return defaultFormat;
		}


		public Collection getPublicKeys() {
			Collection res = new ArrayList();
			for (X509Certificate cert : getCertificates()) {
				res.add(cert.getPublicKey());
			}
			return res;
		}
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy