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

import java.util.Iterator;
import java.util.List;
import java.util.Map;

import info.dragonlady.sso.authority.AuthorityException;
import info.dragonlady.sso.authority.EnforcementException;
import info.dragonlady.sso.authority.PolicyEnforcementPoint;
import info.dragonlady.sso.authority.SAMLAuthorityProvider;
import info.dragonlady.sso.message.SSOMessageResource;
import info.dragonlady.sso.util.AssertionParser;
import info.dragonlady.sso.util.ResourceParseException;

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

import org.opensaml.SAMLAction;
import org.opensaml.SAMLAssertion;
import org.opensaml.SAMLAuthenticationStatement;
import org.opensaml.SAMLBrowserProfile;
import org.opensaml.SAMLBrowserProfileFactory;
import org.opensaml.SAMLException;
import org.opensaml.SAMLRequest;
import org.opensaml.SAMLResponse;
import org.opensaml.SAMLSubject;
import org.opensaml.SAMLBrowserProfile.BrowserProfileRequest;
import org.opensaml.SAMLBrowserProfile.BrowserProfileResponse;

/**
 * アサーション消費者。
 * 
 * @author Hiroshi.Ebata(INFOCITY)
 * @version $Revision: 1.0 $ $Date: 2007/04/19 17:38:34 $
 */
public class AssertionConsumer implements PolicyEnforcementPoint {

	private HttpServletRequest request = null;

	private HttpServletResponse response = null;

	private String resource = null;

	private String authId = null;

	private Map<String, List<String>> attributes = null;

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

	/* (non-Javadoc)
	 * @see info.dragonlady.sso.authority.PolicyEnforcementPoint#isEnforceable()
	 */
	public boolean isEnforceable() throws EnforcementException {
		try {
			// 受取人（自身）
			String recipient = ConsumerResource.getConsumer(request.getServerName(), request.getContextPath().replace("/", ""));
			SAMLBrowserProfile profile = SAMLBrowserProfileFactory.getInstance();
			BrowserProfileRequest bpReq = profile.receive(request);
			BrowserProfileResponse bpRes = profile.receive(null, bpReq, recipient, SSOContext.getInstance().getReplayCache(), null, 1);

			// アサーション解析
			boolean valid = false;
			if(bpRes != null && bpRes.response != null) {
				Iterator<SAMLAssertion> itr = bpRes.response.getAssertions();
				while(itr.hasNext()) {
					SAMLAssertion assertion = itr.next();
					AssertionParser parser = new AssertionParser(assertion);
					if(parser.containsAuthenticationStatement()) {
						// 署名確認
						if(assertion.isSigned()) {
							assertion.verify();
						}
						valid = verify(assertion, resource);
						if(valid) {
							break;
						}
					}
				}
			}
			return valid;
		} catch(Exception e) {
			throw new EnforcementException(SSOMessageResource.getMessage("1301"), e);
		}
	}

	private boolean verify(SAMLAssertion assertion, String resource) throws SAMLException, AuthorityException, ResourceParseException, InstantiationException, IllegalAccessException, ClassNotFoundException, CloneNotSupportedException {
		// 認証オーソリティ問い合わせ
		SAMLRequest samlRequest = Requester.createAuthenticationRequest(null, null, new AssertionParser(assertion).getName(), SAMLSubject.CONF_BEARER, assertion.toString(), SAMLAuthenticationStatement.AuthenticationMethod_XML_DSig);
		SAMLResponse samlResponse = SAMLAuthorityProvider.getAuthenticationAuthority().query(samlRequest);
		SAMLException status = samlResponse.getStatus();
		if(!status.getCodes().next().equals(SAMLException.SUCCESS)) {
			throw new AuthorityException(status.getMessage(), status.getException());
		}
		SAMLAssertion authAssertion = (SAMLAssertion)samlResponse.getAssertions().next();
		// 属性オーソリティ問い合わせ
		samlRequest = Requester.createAttributeRequest(authAssertion, SSOConfig.getSAMLAttributeList());
		samlResponse = SAMLAuthorityProvider.getAttributeAuthority().query(samlRequest);
		status = samlResponse.getStatus();
		if(!status.getCodes().next().equals(SAMLException.SUCCESS)) {
			throw new AuthorityException(status.getMessage(), status.getException());
		}
		SAMLAssertion attrAssertion = (SAMLAssertion)samlResponse.getAssertions().next();
		// ポリシー決定点問い合わせ
		samlRequest = Requester.createAuthorizationDecisionRequest((SAMLAssertion)authAssertion.clone(), (SAMLAssertion)attrAssertion.clone(), new SAMLAction(SAMLAction.SAML_ACTION_NAMESPACE_GHPP, SSOConstants.ACTION_GET), resource);
		samlResponse = SAMLAuthorityProvider.getPolicyDecisionPoint().query(samlRequest);
		status = samlResponse.getStatus();
		if(!status.getCodes().next().equals(SAMLException.SUCCESS)) {
			throw new AuthorityException(status.getMessage(), status.getException());
		}

		AssertionParser parser = new AssertionParser((SAMLAssertion)samlResponse.getAssertions().next());
		authId = parser.getName();
		attributes = parser.getAttribute();
		return parser.isPermitted();
	}

	/* (non-Javadoc)
	 * @see info.dragonlady.sso.authority.PolicyEnforcementPoint#enforce()
	 */
	public void enforce() throws EnforcementException {
		try {
			response.addCookie(new Cookie(SSOConstants.NAME_SESSIONID.toUpperCase(), request.getSession().getId()));
			response.sendRedirect(resource);
		} catch(Exception e) {
			throw new EnforcementException(SSOMessageResource.getMessage("1302"), e);
		}
	}

	/**
	 * アサーションから抽出した認証IDを取得。
	 * 
	 * @return String 認証ID
	 */
	public String getAuthId() {
		return authId;
	}

	/**
	 * アサーションから抽出した属性情報を取得。
	 * 
	 * @return Map<String, List<String>> 属性情報
	 */
	public Map<String, List<String>> getAttributes() {
		return attributes;
	}

}
