/*
* Copyright (c) Members of the EGEE Collaboration. 2006-2010.
* See http://www.eu-egee.org/partners/ for details on the copyright holders.
*
* 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.glite.authz.pep.pip.provider;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import javax.security.auth.x500.X500Principal;
import org.glite.authz.common.config.ConfigurationException;
import org.glite.authz.common.model.Attribute;
import org.glite.authz.common.model.Environment;
import org.glite.authz.common.model.Request;
import org.glite.authz.common.profile.GLiteAuthorizationProfileConstants;
import org.glite.authz.pep.pip.PIPProcessingException;
import org.glite.voms.FQAN;
import org.glite.voms.PKIStore;
import org.glite.voms.VOMSAttribute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The PIP applies to request which have a profile identifier
* {@value GLiteAuthorizationProfileConstants#ID_ATTRIBUTE_PROFILE_ID} defined
* in the request environment. By default accept all profile identifier values,
* but a list of accepted profile identifier values can be specified.
* <p>
* The PIP extracts information from a X.509, version 3, certificate. The
* certificate may include VOMS attribute certificates. All extract information
* is added to the subject(s) containing a valid certificate chain.
* <p>
* The PEM encoded end entity certificate, and its certificate chain, are
* expected to be bound to the subject attribute
* {@value Attribute#ID_SUB_KEY_INFO} with a datatype of
* {@value Attribute#DT_STRING}.
* <p>
* Only one end-entity certificate may be present in the chain.
* <p>
* If the end entity certificate contains a VOMS attribute certificate, and VOMS
* certificate validation is enabled, information from that attribute
* certificate will also be added to the subject. Only one VOMS attribute
* certificate may be present in the end-entity certificate.
*
* @see <a href="https://twiki.cnaf.infn.it/cgi-bin/twiki/view/VOMS">VOMS
* website</a>
*/
public class GLiteAuthorizationProfilePIP extends AbstractX509PIP {
/** List of accepted profile IDs, if <code>null</code> accept all profile Id */
private List<String> acceptedProfileIds_= null;
/** Class logger. */
private Logger log= LoggerFactory.getLogger(GLiteAuthorizationProfilePIP.class);
/**
* The constructor for this PIP. This constructor enables support for the
* VOMS attribute certificates.
*
* @param pipID
* ID of this PIP
* @param requireProxy
* whether a subject's certificate chain must require a proxy in
* order to be valid
* @param eeTrustMaterial
* trust material used to validate the subject's end entity
* certificate
* @param acTrustMaterial
* trust material used to validate the subject's attribute
* certificate certificate, may be <code>null</code> if AC
* support is not desired
* @param performPKIXValidation
* perform or not PKIX validation on the certificate
* @throws ConfigurationException
* thrown if the configuration of the PIP fails
*/
public GLiteAuthorizationProfilePIP(String pipID, boolean requireProxy,
PKIStore eeTrustMaterial, PKIStore acTrustMaterial,
boolean performPKIXValidation) throws ConfigurationException {
super(pipID, requireProxy, eeTrustMaterial, acTrustMaterial);
performPKIXValidation(performPKIXValidation);
}
/**
* Constructor with a list of accepted profile IDs found in the request
* environment attribute
* {@value GLiteAuthorizationProfileConstants#ID_ATTRIBUTE_PROFILE_ID}
*
* @param pipID
* ID of this PIP
* @param requireProxy
* whether a subject's certificate chain must require a proxy in
* order to be valid
* @param eeTrustMaterial
* trust material used to validate the subject's end entity
* certificate
* @param acTrustMaterial
* trust material used to validate the subject's attribute
* certificate certificate, may be <code>null</code> if AC
* support is not desired
* @param performPKIXValidation
* perform or not PKIX validation on the certificate
* @param acceptedProfileIds
* list of accepted profile IDs found in the request environment.
* If <code>null</code> accept every profile IDs, if empty accept
* none.
* @throws ConfigurationException
* thrown if the configuration of the PIP fails
*/
public GLiteAuthorizationProfilePIP(String pipID, boolean requireProxy,
PKIStore eeTrustMaterial, PKIStore acTrustMaterial,
boolean performPKIXValidation, String[] acceptedProfileIds)
throws ConfigurationException {
this(pipID,
requireProxy,
eeTrustMaterial,
acTrustMaterial,
performPKIXValidation);
if (acceptedProfileIds == null) {
// accept all
log.debug("{}: accept all profile ID values", pipID);
acceptedProfileIds_= null;
}
else if (acceptedProfileIds.length == 0) {
// accept none
log.debug("{}: accept NO profile ID value", pipID);
acceptedProfileIds_= Collections.emptyList();
}
else {
log.debug("{}: accept profile ID values: ",
pipID,
Arrays.toString(acceptedProfileIds));
acceptedProfileIds_= new ArrayList<String>(Arrays.asList(acceptedProfileIds));
}
}
/**
* Checks that the incoming {@link Request} contains a profile identifier
* attribute in the environment.
*
* @param request
* the incoming request to be checked
*
* @return true if this PIP applies to the request, false if not
*/
protected boolean appliesToRequest(Request request) {
Environment env= request.getEnvironment();
if (env != null) {
for (Attribute attrib : env.getAttributes()) {
if (GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_PROFILE_ID.equals(attrib.getId())) {
if (acceptedProfileIds_ == null) {
// accept all profile IDs
log.trace("PIP '{}' accept all {} value",
getId(),
GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_PROFILE_ID);
return true;
}
else if (acceptedProfileIds_.isEmpty()) {
// accept none
log.warn("PIP '{}' don't accept any profile ID, specify 'acceptedProfileIDs = ...' in config.",
getId());
return false;
}
else {
// accept only listed one
for (String acceptedProfileId : acceptedProfileIds_) {
if (attrib.getValues().contains(acceptedProfileId)) {
log.trace("PIP '{}' accept {}",
getId(),
acceptedProfileId);
return true;
}
}
log.debug("PIP '{}' don't accept profile ID: {}",
getId(),
attrib.getValues());
return false;
}
}
}
}
log.debug("Skipping PIP '{}', request does not contain a profile identifier in environment",
getId());
return false;
}
/** {@inheritDoc} */
protected String getCertificateAttributeId() {
return Attribute.ID_SUB_KEY_INFO;
}
/** {@inheritDoc} */
protected String getCertificateAttributeDatatype() {
return Attribute.DT_STRING;
}
/**
* Processes one certificate chain and adds the information to the subjects
* in the request.
*
* @param endEntityCertificate
* end entity certificate for the subject currently being
* processed
* @param certChain
* the certificate chain containing the end entity certificate
* from which information will be extracted
*
* @return the attribute extracted from the certificate chain
*
* @throws PIPProcessingException
* thrown if there is a problem reading the information from the
* certificate chain
*/
protected Collection<Attribute> processCertChain(
X509Certificate endEntityCertificate, X509Certificate[] certChain)
throws PIPProcessingException {
if (endEntityCertificate == null || certChain == null
|| certChain.length == 0) {
return null;
}
log.debug("Extracting end-entity certificate attributes");
HashSet<Attribute> subjectAttributes= new HashSet<Attribute>();
// get and set the subject DN attribute.
String endEntitySubjectDN= endEntityCertificate.getSubjectX500Principal().getName(X500Principal.RFC2253);
Attribute attribute= new Attribute();
attribute.setId(Attribute.ID_SUB_ID);
attribute.setDataType(Attribute.DT_X500_NAME);
attribute.getValues().add(endEntitySubjectDN);
log.debug("Extracted attribute: {}", attribute);
subjectAttributes.add(attribute);
// set the issuer DN attribute.
attribute= new Attribute();
attribute.setId(GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_SUBJECT_ISSUER);
attribute.setDataType(Attribute.DT_X500_NAME);
for (int i= 1; i < certChain.length; i++) {
attribute.getValues().add(certChain[i].getSubjectX500Principal().getName(X500Principal.RFC2253));
}
log.debug("Extracted attribute: {}", attribute);
subjectAttributes.add(attribute);
if (isVOMSSupportEnabled()) {
Collection<Attribute> vomsAttributes= processVOMS(endEntityCertificate,
certChain);
if (vomsAttributes != null) {
subjectAttributes.addAll(vomsAttributes);
}
}
return subjectAttributes;
}
/**
* Processes the VOMS attributes and puts valid attributes into the subject
* object.
*
* @param endEntityCert
* the end entity certificate for the subject being processed
* @param certChain
* certificate chain containing the end entity certificate that
* contains the VOMS attribute certificate
*
* @return the attributes extracted from the VOMS attribute certificate
*
* @throws PIPProcessingException
* thrown if the end entity certificate contains more than one
* attribute certificate
*/
@SuppressWarnings("unchecked")
private Collection<Attribute> processVOMS(X509Certificate endEntityCert,
X509Certificate[] certChain) throws PIPProcessingException {
log.debug("Extracting VOMS attribute certificate attributes");
VOMSAttribute attributeCertificate= extractAttributeCertificate(certChain);
if (attributeCertificate == null) {
return null;
}
HashSet<Attribute> vomsAttributes= new HashSet<Attribute>();
Attribute voAttribute= new Attribute();
voAttribute.setId(GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_VIRTUAL_ORGANIZATION);
voAttribute.setDataType(Attribute.DT_STRING);
voAttribute.getValues().add(attributeCertificate.getVO());
log.debug("Extracted attribute: {}", voAttribute);
vomsAttributes.add(voAttribute);
List<FQAN> fqans= attributeCertificate.getListOfFQAN();
if (fqans != null && !fqans.isEmpty()) {
Attribute primaryFqanAttribute= new Attribute();
primaryFqanAttribute.setId(GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_PRIMARY_FQAN);
primaryFqanAttribute.setDataType(GLiteAuthorizationProfileConstants.DATATYPE_FQAN);
primaryFqanAttribute.getValues().add(fqans.get(0).getFQAN());
log.debug("Extracted attribute: {}", primaryFqanAttribute);
vomsAttributes.add(primaryFqanAttribute);
// handle rest of the fqans
Attribute fqanAttribute= new Attribute();
fqanAttribute.setId(GLiteAuthorizationProfileConstants.ID_ATTRIBUTE_FQAN);
fqanAttribute.setDataType(GLiteAuthorizationProfileConstants.DATATYPE_FQAN);
for (FQAN fqan : fqans) {
fqanAttribute.getValues().add(fqan.getFQAN());
}
log.debug("Extracted attribute: {}", fqanAttribute);
vomsAttributes.add(fqanAttribute);
}
return vomsAttributes;
}
}