package lu.tudor.santec.gecamed.labo.ejb.entity.beans;

import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Transient;

import lu.tudor.santec.gecamed.core.ejb.entity.beans.GECAMedEntityBean;
import lu.tudor.santec.gecamed.labo.utils.CertificateSynchronizer;

/**
 * @author jens.ferring(at)tudor.lu
 * 
 * @version
 * <br>$Log: Certifier.java,v $
 * <br>Revision 1.2  2013-05-17 12:12:19  ferring
 * <br>Labo module changed to use the CA chain of the labo server to verify the labo and physician certificates.
 * <br>
 * <br>Revision 1.1  2013-05-13 08:48:19  ferring
 * <br>Labo certificate CA verification enhancement
 * <br>
 * <br>Revision 1.1  2013-05-08 13:45:08  ferring
 * <br>Labo rootCA fixes
 * <br>
 */

@NamedQueries
({
	@NamedQuery(name = Certifier.CHECK_CERTIFICATE_CHAIN,	query =	"SELECT COUNT(o.id) FROM Certifier o " +
																	"WHERE o.label = 'chain' " +
																	"AND o.certificate IS NOT NULL"),
																
	@NamedQuery(name = Certifier.GET_ALL_CERTIFICATES,		query = "SELECT OBJECT(o) FROM Certifier o"),
	
	@NamedQuery(name = Certifier.GET_CERTIFICATE_BY_LABEL,	query = "SELECT OBJECT(o) FROM Certifier o " +
																	"WHERE o.label = :label")
})

@Entity
@Table(name = "certifier", schema = "labo")
public class Certifier extends GECAMedEntityBean
{
	/* ======================================== */
	// 		CONSTANTS
	/* ======================================== */
	
	private static final long		serialVersionUID			= 2228110695783311883L;
	
	// Named query names
	public static final String		CHECK_CERTIFICATE_CHAIN		= "CheckCertificateChain";
	public static final String		GET_ALL_CERTIFICATES		= "LoadCertificates";
	public static final String		GET_CERTIFICATE_BY_LABEL	= "GetCertificateByLabel";
	
	// the chain label
	public static final String		CHAIN_LABEL					= "chain";
	
	
	
	/* ======================================== */
	// 		MEMBERS
	/* ======================================== */
	
	// DB members
	private String	label;
	
	private String	certificate;
	
	private	Date	lastModified;
	
	// transient members
	private String	ftpFileName;
	
	private String	ftpFolderName;
	
	private String	ftpServerName;
	
	
	
	/* ======================================== */
	// 		GETTER & SETTER
	/* ======================================== */
	
	public String getLabel()
	{
		return label;
	}
	
	
	public void setLabel(String label)
	{
		this.label = label;
	}
	
	
	public String getCertificate()
	{
		return certificate;
	}
	
	
	public void setCertificate(String certificate)
	{
		this.certificate = certificate;
	}
	
	
	@Column(name = "last_modified")
	public Date getLastModified()
	{
		return lastModified;
	}
	
	
	public void setLastModified(Date lastModified)
	{
		this.lastModified = lastModified;
	}
	
	
	
	/* ---------------------------------------- */
	// 		TRANSIENT GETTER & SETTER
	/* ---------------------------------------- */
	
	@Transient
	public String getFtpFileName()
	{
		return ftpFileName;
	}
	

	@Transient
	public void setFtpFileName(String ftpFileName)
	{
		this.ftpFileName = ftpFileName;
	}
	

	@Transient
	public String getFtpFolderName()
	{
		return ftpFolderName;
	}
	

	@Transient
	public void setFtpFolderName(String ftpFolderName)
	{
		this.ftpFolderName = ftpFolderName;
	}
	

	@Transient
	public String getFtpServerName()
	{
		return ftpServerName;
	}
	
	
	@Transient
	public void setFtpServerName(String ftpServerName)
	{
		this.ftpServerName = ftpServerName;
	}
	
	
	
	/* ======================================== */
	// 		CLASS BODY
	/* ======================================== */
	
	@Transient
	public static String generateLabel (String serverName, String folderName, String fileName)
	{
		return new StringBuilder(
						  serverName.length() 
						+ CertificateSynchronizer.c_CertificateFolder.length() 
						+ fileName.length() 
						+ 2)
				.append(serverName)
				.append("/")
				.append(folderName)
				.append("/")
				.append(fileName)
				.toString()
				.replaceAll("(/){2,}", "/");
	}
	
	
	@Transient
	public String generateLabel ()
	{
		return generateLabel(ftpServerName, ftpFolderName, ftpFileName);
	}
	
	
	@Transient
	public static boolean verify (X509Certificate userCertificate, Collection<X509Certificate> caChain)
	{
		for (X509Certificate ca : caChain) 
		{
			// for each CA in the chain: try to verify the certificate with it
			if (verify(userCertificate, ca))
			{
				if (verify(ca, ca))
				{
					// root CA found
					return true;
				}
				else
				{
					caChain.remove(ca);
					
					if (caChain.isEmpty())
						// chain is empty - certificate cannot be verified
						return false;
					else
						// check the chain recursively for an end
						return verify(ca, caChain);
				}
			}
		}
		
		// no CA found to verify the certificate
		return false;
	}
	
	
	@Transient
	public static boolean verify (X509Certificate userCertificate, X509Certificate rootCa)
	{
		try 
		{
			userCertificate.verify(rootCa.getPublicKey());
			
			// if there's no exception, the verification was successful
			return true;
		} 
		catch (GeneralSecurityException e) 
		{
			return false;
		}
	}
}
