/*
 * @(#)SiteTransfer.java
 *
 * Copyright (C) 2006 Infocity Inc. All Rights Reserved.
 */
package info.dragonlady.sso;

import info.dragonlady.sso.message.SSOMessageResource;
import info.dragonlady.sso.util.ResourceParseException;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.xml.security.signature.XMLSignature;
import org.opensaml.SAMLAssertion;
import org.opensaml.SAMLException;
import org.opensaml.SAMLResponse;
import org.opensaml.SAMLSignedObject;
import org.opensaml.artifact.Artifact;

/**
 * アーティファクトプロファイルによる目的Webサイトへの転送処理クラス。
 * 
 * @author Hiroshi.Ebata(INFOCITY)
 * @version $Revision: 1.0 $ $Date: 2006/04/13 22:30:39 $
 */
public class SiteTransfer {

	private HttpServletRequest request = null;

	private HttpServletResponse response = null;

	private final String defaultEncoding = "ISO-2022-JP";

	/**
	 * コンストラクタ
	 * 
	 * @param request 要求オブジェクト
	 * @param response 応答オブジェクト
	 */
	public SiteTransfer(HttpServletRequest request, HttpServletResponse response) {
		super();
		this.request = request;
		this.response = response;
	}

	/**
	 * アーティファクトとパラメータを指定して、目的サイトのアーティファクトレシーバへリダイレクトする。
	 * 
	 * @param artifacts アーティファクト
	 * @param params 目的サイトへ送信する任意のパラメータ
	 * @throws SSOException
	 */
	public void forward(List<Artifact> artifacts, Map<String, String> params) throws SSOException {
		try {
			StringBuffer redirectURL = new StringBuffer();
			String target = request.getParameter(SSOConstants.NAME_TARGET);

			// リダイレクションURLの作成
			String encodeStr = request.getCharacterEncoding();
			if(encodeStr == null || encodeStr.length() < 1) {
				encodeStr = defaultEncoding;
			}
			URL targetUrl = new URL(target);
			redirectURL.append(ReceiverResource.getReceiver(targetUrl.getHost(), getContext(targetUrl)));
			redirectURL.append("?" + SSOConstants.NAME_TARGET + "=" + URLEncoder.encode(target, encodeStr));
			for(int i = 0; i < artifacts.size(); i++) {
				redirectURL.append("&" + SSOConstants.NAME_SAMLART + "=" + URLEncoder.encode(artifacts.get(i).encode(), encodeStr));
			}
			if(params != null) {
				Iterator itr = params.keySet().iterator();
				while(itr.hasNext()) {
					Object key = itr.next();
					Object value = params.get(key);
					redirectURL.append("&" + key + "=" + value);
				}
			}

			response.sendRedirect(redirectURL.toString());
		} catch(Exception e) {
			throw new SSOException(SSOMessageResource.getMessage("1001"), e);
		}
	}

	/**
	 * アサーションとパラメータを指定して、POST用データを生成する。
	 * 
	 * @param assertions アサーション
	 * @param params 目的サイトへ送信する任意のパラメータ
	 * @return Map<String, Object> POST用データ
	 * @throws SSOException
	 */
	public Map<String, Object> getPostData(List<SAMLAssertion> assertions, Map<String, String> params) throws SSOException {
		try {
			// 署名（必須）
			for(SAMLAssertion assertion : assertions) {
				if(!assertion.isSigned()) {
					sign(assertion);
				}
			}
			String target = request.getParameter(SSOConstants.NAME_TARGET);
			URL targetUrl = new URL(target);
			String consumerUrl = ConsumerResource.getConsumer(targetUrl.getHost(), getContext(targetUrl));

			SAMLException samlException = new SAMLException(SAMLException.SUCCESS);
			SAMLResponse samlResponse = new SAMLResponse(null, consumerUrl, assertions, samlException);
			sign(samlResponse);

			// POST用のデータを格納
			Map<String, Object> data = new HashMap<String, Object>();
			data.put(SSOConstants.NAME_SAMLRESPONSE, samlResponse);
			data.put(SSOConstants.NAME_TARGET, target);
			data.put(SSOConstants.NAME_CONSUMER, consumerUrl);
			if(params != null) {
				data.put(SSOConstants.NAME_POSTPARAM, params);
			}
			return data;
		} catch (Exception e) {
			throw new SSOException(SSOMessageResource.getMessage("1002"), e);
		}
	}

	private String getContext(URL targetUrl) {
		String context = "";
		String[] s = targetUrl.getPath().split("/");
		for(int i = 0; i < s.length; i++) {
			if(s[i].length() > 0) {
				context = s[i];
				break;
			}
		}
		return context;
	}

	private void sign(SAMLSignedObject object) throws ResourceParseException, CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException, SAMLException {
		String certPath = SSOConfig.getCertPath();
		FileInputStream fis = new FileInputStream(certPath);
		BufferedInputStream bis = new BufferedInputStream(fis);

		CertificateFactory cf = CertificateFactory.getInstance("X.509");

		Certificate cert = null;
		while(bis.available() > 0) {
			cert = cf.generateCertificate(bis);
		}
		fis.close();
		bis.close();

		String storePath = SSOConfig.getKeyStorePath();
		fis = new FileInputStream(storePath);
		bis = new BufferedInputStream(fis);
		String password = SSOConfig.getKeyStorePassword();
		String alias = SSOConfig.getKeyStoreAlias();

		KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
		while(bis.available() > 0) {
			ks.load(bis, password.toCharArray());
		}
		Key key = ks.getKey(alias, password.toCharArray());
		fis.close();
		bis.close();
		object.sign(XMLSignature.ALGO_ID_SIGNATURE_DSA, key, Arrays.asList(new Certificate[]{cert}));
	}

}
