/*
 * Copyright (c) 2011-2012 ICM Uniwersytet Warszawski All rights reserved.
 * See LICENCE file for licensing information.
 *
 * Derived from the code copyrighted and licensed as follows:
 * 
 * Copyright (c) Members of the EGEE Collaboration. 2004.
 * 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 eu.emi.security.authn.x509.helpers.proxy;

import static eu.emi.security.authn.x509.helpers.proxy.DraftRFCProxyCertInfoExtension.DRAFT_EXTENSION_OID;
import static eu.emi.security.authn.x509.helpers.proxy.RFCProxyCertInfoExtension.RFC_EXTENSION_OID;

import java.io.IOException;
import java.security.cert.X509Certificate;

import javax.security.auth.x500.X500Principal;

import org.bouncycastle.asn1.x500.AttributeTypeAndValue;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.asn1.x500.style.IETFUtils;

import eu.emi.security.authn.x509.helpers.CertificateHelpers;
import eu.emi.security.authn.x509.proxy.ProxyPolicy;

/**
 * Various helpers for handling proxy certificates
 *
 * @author J. Hahkala
 * @author K. Benedyczak
 */
public class ProxyHelper
{
	/**
	 * Checks if the certificate is a proxy and if so return its type.
	 * @param certificate to be checked
	 * @return the proxy type or info that it is a normal certificate.
	 */
	public static ExtendedProxyType getProxyType(X509Certificate certificate)
	{
		if (certificate.getExtensionValue(RFC_EXTENSION_OID) != null
				&& certificate.getExtensionValue(RFC_EXTENSION_OID).length > 0)
			return ExtendedProxyType.RFC3820;
		
		if (certificate.getExtensionValue(DRAFT_EXTENSION_OID) != null
				&& certificate.getExtensionValue(DRAFT_EXTENSION_OID).length > 0)
			return ExtendedProxyType.DRAFT_RFC;

		String value;
		try
		{
			value = getLastCN(certificate.getSubjectX500Principal());
		} catch (IllegalArgumentException e) //empty subject DN
		{
			value = "";
		}
		
		if ("proxy".equals(value.toLowerCase())
				|| "limited proxy".equals(value.toLowerCase()))
			return ExtendedProxyType.LEGACY;
		return ExtendedProxyType.NOT_A_PROXY;
	}


	public static String getLastCN(X500Principal principal) throws IllegalArgumentException
	{
		X500Name x500Name = CertificateHelpers.toX500Name(principal);
		return getLastCN(x500Name);
	}

	public static String getLastCN(X500Name x500Name) throws IllegalArgumentException
	{
		RDN[] rdns = x500Name.getRDNs();
		if (rdns.length == 0)
			throw new IllegalArgumentException("The DN is empty");
		RDN last = rdns[rdns.length-1];
		
		if (last.isMultiValued())
			throw new IllegalArgumentException("The DN is ended with a multivalued RDN");
		AttributeTypeAndValue cn = last.getFirst();
		if (!cn.getType().equals(BCStyle.CN))
			throw new IllegalArgumentException("The DN is not ended with a CN AVA");

		return IETFUtils.valueToString(cn.getValue());
	}
	
	
	/**
	 * Returns the proxy path limit of the proxy. The argument is not checked if
	 * is a real proxy. 
	 * @param cert certificate
	 * @return path limit as set for the DRAFT and RFC proxies. In case of legacy proxies
	 * or unlimited proxies Integer.MAX_VALUE is returned.
	 * @throws IOException if the extension can not be parsed
	 */
	public static int getProxyPathLimit(X509Certificate cert)
			throws IOException
	{
		ProxyCertInfoExtension info = ProxyCertInfoExtension.getInstance(cert);
		if (info == null)
			return Integer.MAX_VALUE;
		return info.getProxyPathLimit();
	}

	/**
	 * Checks if the certificate is a limited proxy in Globus sense, i.e. if its last CN is equal to 'limited proxy'
	 * (in case of legacy proxies) or if the special limited proxy policy is used.
	 * @param cert certificate
	 * @return true only if the parameter is a limited proxy
	 * @throws IOException IO exception
	 */
	public static boolean isLimited(X509Certificate cert) throws IOException
	{
		ExtendedProxyType type = getProxyType(cert);
		if (type == ExtendedProxyType.RFC3820 || type == ExtendedProxyType.DRAFT_RFC)
		{
			ProxyCertInfoExtension ext = ProxyCertInfoExtension.getInstance(cert);
			ProxyPolicy policy = ext.getPolicy();
			return ProxyPolicy.LIMITED_PROXY_OID.equals(policy.getPolicyOID());
		} else if (type == ExtendedProxyType.LEGACY)
		{
			String cn;
			try
			{
				cn = getLastCN(cert.getSubjectX500Principal());
			} catch (IllegalArgumentException e)
			{
				cn = "";
			}
			return "limited proxy".equals(cn.toLowerCase());
		}
		return false;
	}
}
