package hk.hku.cecid.edi.sfrm.admin.listener;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.File;
import java.security.cert.CertificateFactory;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.security.cert.X509Certificate;
import java.security.MessageDigest;
import java.io.BufferedInputStream;
import java.net.URL;
import java.util.Enumeration;
import hk.hku.cecid.piazza.commons.io.IOHandler;
import hk.hku.cecid.piazza.commons.util.StringUtilities;
import hk.hku.cecid.piazza.corvus.admin.listener.AdminPageletAdaptor;
import hk.hku.cecid.piazza.commons.util.PropertyTree;
import hk.hku.cecid.piazza.commons.dao.DAOException;
import hk.hku.cecid.edi.sfrm.dao.SFRMPartnershipDAO;
import hk.hku.cecid.edi.sfrm.dao.SFRMPartnershipDVO;
import hk.hku.cecid.edi.sfrm.spa.SFRMException;
import hk.hku.cecid.edi.sfrm.spa.SFRMProcessor;
import hk.hku.cecid.edi.sfrm.spa.SFRMProperties;
import javax.servlet.http.HttpServletRequest;
import javax.xml.transform.Source;
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.FileUploadException;
/**
* @author Patrick Yip
* @version 1.0.0
*/
public class PartnershipPageletAdaptor extends AdminPageletAdaptor {
protected Source getCenterSource(HttpServletRequest request){
PropertyTree dom = new PropertyTree();
dom.setProperty("/partnerships", "");
dom.setProperty("add_partnership/", "");
try {
boolean isMultipart = FileUpload.isMultipartContent(request);
if (isMultipart) {
Hashtable ht = getHashtable(request);
String partnershipId = null;
if (((String) ht.get("request_action")).equalsIgnoreCase("change")) {
partnershipId = (String) ht.get("selected_partnership_id");
} else {
partnershipId = (String) ht.get("partnership_id");
modifyPartnership(ht, request, dom);
}
getSelectedPartnership(partnershipId, dom);
}
getAllPartnerships(dom);
} catch (Exception e) {
SFRMProcessor.getInstance().getLogger().debug("Unable to process the partnership page request", e);
throw new RuntimeException("Unable to process the partnership page request", e);
}
return dom.getSource();
}
private void getAllPartnerships(PropertyTree dom) throws DAOException{
SFRMPartnershipDAO partnershipDAO = (SFRMPartnershipDAO) SFRMProcessor.getInstance().getDAOFactory().createDAO(SFRMPartnershipDAO.class);
List partnerships = partnershipDAO.findAllPartnerships();
Iterator iter = partnerships.iterator();
for(int i = 1; iter.hasNext(); i++){
SFRMPartnershipDVO partnershipDVO = (SFRMPartnershipDVO) iter.next();
dom.setProperty("partnership[" + i + "]/partnership_id", partnershipDVO.getPartnershipId());
}
}
/*
* Get all partnership information from DAO and set the selected partnership to the dom object, if the given partnership ID exist
* @param parntershipId Partnership ID being selected
* @param dom The DOM object to insert the partnership information to
* @throws DAOException
*/
private void getSelectedPartnership(String partnershipId, PropertyTree dom) throws DAOException, SFRMException{
SFRMPartnershipDAO partnershipDAO = (SFRMPartnershipDAO) SFRMProcessor.getInstance().getDAOFactory().createDAO(SFRMPartnershipDAO.class);
SFRMPartnershipDVO partnershipDVO = (SFRMPartnershipDVO) partnershipDAO.createDVO();
partnershipDVO.setPartnershipId(partnershipId);
SFRMProcessor.getInstance().getLogger().info("Partnership ID: " + partnershipId);
if(partnershipDAO.retrieve(partnershipDVO)){
getPartnership(partnershipDVO, dom, "selected_partnership/");
}
}
/*
* Insert the information of given partnership DVO to the DOM object
* @param partnershipDVO The partnership DVO object contains information of the partnership to insert to the DOM object
* @param dom DOM object being insert the partnership information
* @param prefix The xpath location for inserting the partnership information
*/
private void getPartnership(SFRMPartnershipDVO partnershipDVO, PropertyTree dom, String prefix) throws SFRMException{
dom.setProperty(prefix + "partnership_id", partnershipDVO.getPartnershipId());
dom.setProperty(prefix + "description", partnershipDVO.getDescription());
dom.setProperty(prefix + "partner_endpoint", partnershipDVO.getOrgPartnerEndpoint());
dom.setProperty(prefix + "partner_cert_fingerprint", partnershipDVO.getPartnerCertFingerprint());
dom.setProperty(prefix + "is_hostname_verified", Boolean.toString(partnershipDVO.isHostnameVerified()));
dom.setProperty(prefix + "sign_algorithm", partnershipDVO.getSignAlgorithm());
dom.setProperty(prefix + "encrypt_algorithm", partnershipDVO.getEncryptAlgorithm());
dom.setProperty(prefix + "retry_max", Integer.toString(partnershipDVO.getRetryMax()));
dom.setProperty(prefix + "retry_interval", Integer.toString(partnershipDVO.getRetryInterval()));
dom.setProperty(prefix + "is_disabled", Boolean.toString(partnershipDVO.isDisabled()));
String certFingerPrint = partnershipDVO.getPartnerCertFingerprint();
if(certFingerPrint!=null && !certFingerPrint.equals("")){
//Check if the certificate is existing
if(!checkCertificateExist(certFingerPrint)){
dom.setProperty(prefix + "encrypt_cert_warn", "The certificate file with fingerprint (" + certFingerPrint + ") doesn't exist.");
}else{
X509Certificate cert = partnershipDVO.getVerifyX509Certificate();
if(cert!=null){
dom.setProperty(prefix + "encrypt_cert/issuer", cert.getIssuerDN()
.getName());
dom.setProperty(prefix + "encrypt_cert/subject", cert.getSubjectDN()
.getName());
dom.setProperty(prefix + "encrypt_cert/thumbprint", partnershipDVO.getPartnerCertFingerprint());
dom.setProperty(prefix + "encrypt_cert/valid-from", StringUtilities
.toGMTString(cert.getNotBefore()));
dom.setProperty(prefix + "encrypt_cert/valid-to", StringUtilities
.toGMTString(cert.getNotAfter()));
}
}
}
}
private boolean checkCertificateExist(String fingerPrint){
File certFile = new File(SFRMProperties.getTrustedCertStore(), fingerPrint);
return certFile.exists();
}
public Hashtable getHashtable(HttpServletRequest request)
throws FileUploadException, IOException {
Hashtable ht = new Hashtable();
DiskFileUpload upload = new DiskFileUpload();
List fileItems = upload.parseRequest(request);
Iterator iter = fileItems.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
if (item.isFormField()) {
ht.put(item.getFieldName(), item.getString());
} else {
if (item.getName().equals("")) {
//ht.put(item.getFieldName(), null);
} else if (item.getSize() == 0) {
//ht.put(item.getFieldName(), null);
} else {
ht.put(item.getFieldName(), item.getInputStream());
}
}
}
return ht;
}
private void modifyPartnership(Hashtable ht, HttpServletRequest request,
PropertyTree dom) throws DAOException, SFRMException{
String requestAction = (String) ht.get("request_action");
boolean success = false;
if(requestAction.equalsIgnoreCase("update")){
success = updatePartnership(ht, dom, request);
}else if(requestAction.equalsIgnoreCase("add")){
success = addPartnership(ht, dom, request);
}else if(requestAction.equalsIgnoreCase("delete")){
String partnershipId = (String)ht.get("partnership_id");
success = deletePartnership(partnershipId, request);
}
}
/**
* Add a new partnership
* @param ht Hastable that contain the information of partnership
* @param dom DOM object of the page
* @param request Servlet requset object
* @return whether add action is success, true for success, false otherwise
* @throws SFRMException
*/
private boolean addPartnership(Hashtable ht, PropertyTree dom, HttpServletRequest request) throws DAOException, SFRMException{
SFRMPartnershipDAO partnershipDAO = (SFRMPartnershipDAO) SFRMProcessor.getInstance().getDAOFactory().createDAO(SFRMPartnershipDAO.class);
SFRMPartnershipDVO partnershipDVO = (SFRMPartnershipDVO) partnershipDAO.createDVO();
boolean success = setPartnershipDVO(partnershipDVO, ht, request);
//Check whether partnership ID already exist
if(partnershipDAO.findPartnershipById((String)ht.get("partnership_id")) != null){
request.setAttribute(ATTR_MESSAGE, "Partnership ID already exist");
success = false;
}
if(success){
partnershipDAO.create(partnershipDVO);
//If the insertion of the new partnership to database is successful, then upload the user provided partnership to the system specified location
try{
if(ht.get("partner_cert") != null){
uploadCertificate(partnershipDVO.getPartnerCertFingerprint(), (InputStream)ht.get("partner_cert"));
}
}catch(Exception e){
SFRMProcessor.getInstance().getLogger().error("Error when uploading the partnership certificate file", e);
}
request.setAttribute(ATTR_MESSAGE, "Partnership added successfully");
dom.removeProperty("/partnerships/add_partnership");
}else{
getPartnership(partnershipDVO, dom, "add_partnership/");
}
return success;
}
/*
* To update the given partnerhsip that user selected
* @param ht Hastable that contain the information of partnership
* @param dom DOM object of the page
* @param request Servlet requset object
* @return Whether the update is success, true for success, false otherwise
*/
private boolean updatePartnership(Hashtable ht, PropertyTree dom, HttpServletRequest request) throws DAOException{
SFRMPartnershipDAO partnershipDAO = (SFRMPartnershipDAO) SFRMProcessor.getInstance().getDAOFactory().createDAO(SFRMPartnershipDAO.class);
SFRMPartnershipDVO partnershipDVO = (SFRMPartnershipDVO) partnershipDAO.createDVO();
String partnershipId = (String)ht.get("partnership_id");
partnershipDVO.setPartnershipId(partnershipId);
partnershipDAO.retrieve(partnershipDVO);
boolean success = setPartnershipDVO(partnershipDVO, ht, request);
// try{
if(success){
partnershipDAO.persist(partnershipDVO);
if(ht.get("partner_cert") != null){
uploadCertificate(partnershipDVO.getPartnerCertFingerprint(), (InputStream)ht.get("partner_cert"));
}
request.setAttribute(ATTR_MESSAGE, "Partnership updated successfully");
}
// }catch(Exception e){
// SFRMProcessor.getInstance().getLogger().error("Upload Error", e);
// }
return success;
}
/**
* To delete the partnernship
* @param partnershipId Partnership ID to delete
* @param request Servlet request object
* @return Whether the delete operation is success, true for success, false otherwise
*/
private boolean deletePartnership(String partnershipId, HttpServletRequest request) throws DAOException{
SFRMPartnershipDAO partnershipDAO = (SFRMPartnershipDAO) SFRMProcessor.getInstance().getDAOFactory().createDAO(SFRMPartnershipDAO.class);
SFRMPartnershipDVO partnershipDVO = (SFRMPartnershipDVO) partnershipDAO.createDVO();
partnershipDVO.setPartnershipId(partnershipId);
//partnershipDAO.retrieve(partnershipDVO);
boolean success = partnershipDAO.remove(partnershipDVO);
if(!success){
request.setAttribute(ATTR_MESSAGE, "Fail on deleting the Partnership with ID: " + partnershipId);
}else{
request.setAttribute(ATTR_MESSAGE, "Partnership deleted successfully");
}
return success;
}
/**
* Validate the partnership data
* @param ht Hashtable containing the partnership data
* @return Hashtable containing the errors detected
*/
private Hashtable validatePartnership(Hashtable ht){
Hashtable errors = new Hashtable();
//Validate the partnership ID
String partnership_id = (String) ht.get("partnership_id");
String endpoint = (String)ht.get("partner_endpoint");
String retry_max = (String)ht.get("retry_max");
String retry_interval = (String)ht.get("retry_interval");
boolean partnership_valid = true;
if(partnership_id==null || partnership_id.trim().equals("")){
errors.put("partnership_id", "Partnership ID cannot be empty");
partnership_valid = false;
}
if(partnership_valid != false && !partnership_id.matches(SFRMPartnershipDVO.PARTNERSHIPID_REGEXP)){
errors.put("partnership_id", "Partnership ID should contains the alphanumeric characters and @ _ + - only");
}
//Validate the partnership Endpoint
if(endpoint==null || endpoint.trim().equals("")){
errors.put("partner_endpoint", "Transport Endpoint cannot be empty");
}else{
URL endPointURL = null;
try{
endPointURL = new URL(endpoint);
}catch(Exception e){
errors.put("partner_endpoint", "Transport Endpoint is invalid");
}
if(endPointURL!=null){
String protocol = endPointURL.getProtocol();
if(protocol!=null && !(protocol.equals("http") || protocol.equals("https"))){
errors.put("partner_endpoint", "Transport Endpoint protocol is invalid");
}
}
}
//Validate retry max
try{
boolean valid = true;
if(retry_max.trim().equals("")){
errors.put("retry_max", "Maximum Retries should have a value");
valid = false;
}
if(valid == true){
long temp = Long.parseLong(retry_max);
if(temp < Integer.MIN_VALUE || temp > Integer.MAX_VALUE){
errors.put("retry_max", "Maximum Retries out of range");
}
}
}catch(NumberFormatException nfe){
errors.put("retry_max", "Maximum Retries must be an integer");
}
//Validate retry interval
try{
boolean valid = true;
if(retry_interval.trim().equals("")){
errors.put("retry_interval", "Retry Interval should have a value");
valid = false;
}
if(valid == true){
long temp = Long.parseLong(retry_interval);
if(temp < Integer.MIN_VALUE || temp > Integer.MAX_VALUE){
errors.put("retry_interval", "Retry Interval out of range");
}
}
}catch(NumberFormatException nfe){
errors.put("retry_interval", "Retry Interval must be an integer");
}
//Validate the uploaded certificate
boolean isUploadedCert = ht.get("partner_cert") != null;
InputStream certInStream = null;
if(isUploadedCert){
certInStream = (InputStream) ht.get("partner_cert");
try {
// certInStream.reset();
String fingerprint = generateX509CertificateFingerprint(certInStream);
}catch(IOException ioe){
errors.put("partner_cert", "Error when reading the partnership certificate");
} catch (Exception e) {
errors.put("partner_cert", "Uploaded cert is not an X.509 cert");
}
}
return errors;
}
/**
* To set the Partnership DVO to from the given hashtable
* @param partnershipDVO The PartnershipDVO object to set the value
* @param ht The given hashtable that contins the partnership information which need to set to DVO
* @param request Servlet request for setting the response message in text, if needed
* @return Whether it passes the validation of the data
*/
private boolean setPartnershipDVO(SFRMPartnershipDVO partnershipDVO, Hashtable ht, HttpServletRequest request){
boolean success = true;
try{
Hashtable errors = validatePartnership(ht);
if(errors.size()!=0){
Enumeration keys = errors.keys();
request.setAttribute(ATTR_MESSAGE, errors.get((String)keys.nextElement()));
if(errors.get("retry_max")!=null){
partnershipDVO.setRetryMax(3);
}
if(errors.get("retry_interval")!=null){
partnershipDVO.setRetryInterval(60000);
}
success = false;
}
if(errors.get("retry_max") == null)
partnershipDVO.setRetryMax(Integer.parseInt((String) ht.get("retry_max")));
if(errors.get("retry_interval") == null)
partnershipDVO.setRetryInterval(Integer.parseInt((String) ht.get("retry_interval")));
partnershipDVO.setPartnershipId((String)ht.get("partnership_id"));
partnershipDVO.setPartnerEndPoint((String)ht.get("partner_endpoint"));
partnershipDVO.setDescription((String)ht.get("description"));
String signAlg = null;
String encryptAlg = null;
if(!((String)ht.get("sign_algorithm")).equalsIgnoreCase("none")){
signAlg = (String)ht.get("sign_algorithm");
}
if(!((String)ht.get("encrypt_algorithm")).equalsIgnoreCase("none")){
encryptAlg = (String)ht.get("encrypt_algorithm");
}
partnershipDVO.setSignAlgorithm(signAlg);
partnershipDVO.setEncryptAlgorithm(encryptAlg);
partnershipDVO.setIsHostnameVerified(Boolean.valueOf((String)ht.get("is_hostname_verified")).booleanValue());
partnershipDVO.setIsDisabled(Boolean.valueOf((String)ht.get("is_disabled")).booleanValue());
//Check if user request to delete the partnership certificate
if (ht.get("encrypt_cert_remove") != null) {
if (((String) ht.get("encrypt_cert_remove")).equalsIgnoreCase("on")) {
partnershipDVO.setPartnerCertFingerprint(null);
}
}
//Get the certificate uploaded by user
if(errors.get("partner_cert") == null){
boolean isUploadedCert = ht.get("partner_cert") != null;
InputStream certInStream = null;
if(isUploadedCert){
certInStream = (InputStream) ht.get("partner_cert");
try {
certInStream.reset();
String fingerprint = generateX509CertificateFingerprint(certInStream);
partnershipDVO.setPartnerCertFingerprint(fingerprint.toUpperCase());
} catch (IOException ioe) {
request.setAttribute(ATTR_MESSAGE, "Error when reading the partnership certificate");
success = false;
}catch(Exception e){
request.setAttribute(ATTR_MESSAGE, "Partnership Certificate Error");
}
}
}
}catch(Exception e){
SFRMProcessor.getInstance().getLogger().error("Error which uplaoding the cert", e);
}
return success;
}
/**
* Generate the fingerprint for the provided certificate. This method also validate the certificate provided.
* @param inStream the file stream for the certificate to generate the fingerprint
* @return Fingerprint in the hexdecimal String representation
* @throws Exception if the certificate is invalid and it is not a X.509 format
*/
private String generateX509CertificateFingerprint(InputStream inStream) throws Exception{
BufferedInputStream fis = new BufferedInputStream(inStream);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(fis);
MessageDigest md = MessageDigest.getInstance("SHA-1");
byte fingerprint[] = md.digest(cert.getEncoded());
return toHexString(fingerprint);
}
private String toHexString(byte[] b) throws Exception {
String result = "";
for (int i=0; i < b.length; i++) {
result += Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 );
}
return result;
}
/**
* Upload the certificate to the application defined location at [PLUGIN_FOLDER]/hk.hku.cecid.edi.sfrm/conf/hk/hku/cecid/edi/sfrm/conf/sfrm.properties.xml, trusted-certificates element
* @param filename Filename to uplaod to the specified location
* @param certInStream InputStream that contains the certificate uploaded from client
*/
private void uploadCertificate(String filename, InputStream certInStream){
try{
File outFile = new File(SFRMProperties.getTrustedCertStore(), filename);
FileOutputStream certOutStream = new FileOutputStream(outFile);
certInStream.reset();
IOHandler.pipe(certInStream, certOutStream);
certOutStream.close();
}catch(Exception e){
SFRMProcessor.getInstance().getLogger().error("Error when uploading the partnership certificate file", e);
}
}
}