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

import info.dragonlady.sso.message.SSOMessageResource;
import info.dragonlady.sso.util.AssertionGenerator;
import info.dragonlady.sso.util.AssertionParser;

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

import org.opensaml.SAMLAction;
import org.opensaml.SAMLAssertion;
import org.opensaml.SAMLAuthorizationDecisionQuery;
import org.opensaml.SAMLDecision;
import org.opensaml.SAMLException;
import org.opensaml.SAMLRequest;
import org.opensaml.SAMLResponse;

/**
 * ポリシー決定点抽象クラス。
 * 
 * @author Hiroshi.Ebata(INFOCITY)
 * @version $Revision: 1.0 $ $Date: 2007/04/25 17:44:33 $
 */
public abstract class PolicyDecisionPoint implements SAMLAuthority {

	/* (non-Javadoc)
	 * @see info.dragonlady.sso.authority.SAMLAuthority#query(org.opensaml.SAMLRequest)
	 */
	public final SAMLResponse query(SAMLRequest request) throws AuthorityException {
		try {
			SAMLResponse response = new SAMLResponse(request.getId(), null, null, null);
			SAMLException samlException = null;
			String authId = null;
			Map<String, List<String>> attributes = Collections.emptyMap();
			Iterator<SAMLAction> requestedAction = null;
			String resource = null;
			try {
				SAMLAuthorizationDecisionQuery query = (SAMLAuthorizationDecisionQuery)request.getQuery();
				Iterator<SAMLAssertion> evidence = query.getEvidence();
				while(evidence.hasNext()) {
					AssertionParser parser = new AssertionParser(evidence.next());
					if(parser.containsAuthenticationStatement()) {
						authId = parser.getName();
					}
					if(parser.containsAttributeStatement()) {
						attributes = parser.getAttribute();
					}
				}
				requestedAction = query.getActions();
				resource = query.getResource();
			} catch(Exception e) {
				samlException = new SAMLException(SAMLException.REQUESTER, SSOMessageResource.getMessage("2201"), e);
			}

			if(samlException == null) {
				try {
					List<SAMLAction> action = getPermittedAction(authId, attributes, resource);
					String decision = decide(requestedAction, action);
					SAMLAssertion assertion = AssertionGenerator.generateAuthorizationDecisionAssertion(authId, resource, decision, action);
					response.addAssertion(assertion);
					samlException = new SAMLException(SAMLException.SUCCESS);
				} catch(Exception e) {
					samlException = new SAMLException(SAMLException.RESPONDER, SSOMessageResource.getMessage("2202"), e);
				}
			}
			response.setStatus(samlException);
			return response;
		} catch(Exception e) {
			throw new AuthorityException(SSOMessageResource.getMessage("2203"), e);
		}
	}

	private String decide(Iterator<SAMLAction> requestedAction, List<SAMLAction> permittedAction) {
		// 拒否（デフォルト）
		String decision = SAMLDecision.DENY;
		int countTrue = 0;
		int countFalse = 0;
		while(requestedAction.hasNext()) {
			boolean contained = false;
			SAMLAction requested = requestedAction.next();
			for(SAMLAction permitted : permittedAction) {
				if(requested.getNamespace().equals(permitted.getNamespace())
						&& requested.getData().equals(permitted.getData())) {
					contained = true;
					break;
				}
			}
			if(contained) {
				countTrue++;
			} else {
				countFalse++;
			}
		}
		if(countTrue > 0 && countFalse == 0) {
			// 許可
			decision = SAMLDecision.PERMIT;
		} else if(countTrue > 0 && countFalse > 0) {
			// 不確定
			decision = SAMLDecision.INDETERMINATE;
		}
		return decision;
	}

	/**
	 * 許可されたアクションを取得する。
	 * 
	 * @param authId 認証ID
	 * @param attributes 属性情報
	 * @param resource リソース
	 * @return List<SAMLAction> 許可されたアクション
	 * @throws AuthorityException
	 */
	protected abstract List<SAMLAction> getPermittedAction(
			String authId,
			Map<String, List<String>> attributes,
			String resource) throws AuthorityException;

}
