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

import info.dragonlady.sso.servlet.ResponderServlet;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilderFactory;

import info.dragonlady.sso.AssertionManager;
import info.dragonlady.sso.ReceiverResource;
import info.dragonlady.sso.SSOConfig;
import info.dragonlady.sso.SSOConstants;
import info.dragonlady.sso.SiteTransfer;
import info.dragonlady.sso.util.AssertionGenerator;
import info.dragonlady.sso.util.ResourceParseException;

import org.apache.cactus.ServletTestCase;
import org.apache.cactus.WebRequest;
import org.apache.cactus.WebResponse;
import org.opensaml.SAMLAssertion;
import org.opensaml.SAMLAuthenticationStatement;
import org.opensaml.SAMLSubject;
import org.opensaml.artifact.Artifact;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * ResponderServlet クラステストケース。
 * 
 * @author Hiroshi.Ebata(INFOCITY)
 * @version $Revision: 1.0 $ $Date: 2006/11/24 18:40:00 $
 */
public class ResponderServletTest extends ServletTestCase {


	private String userId = "testuser";

	/**
	 * @param arg0
	 */
	public ResponderServletTest(String arg0) {
		super(arg0);
	}

	/* (non-Javadoc)
	 * @see junit.framework.TestCase#setUp()
	 */
	protected void setUp() throws Exception {
		super.setUp();
	}

	/* (non-Javadoc)
	 * @see junit.framework.TestCase#tearDown()
	 */
	protected void tearDown() throws Exception {
		super.tearDown();
	}

	/**
	 * {@link info.dragonlady.sso.servlet.ResponderServlet#ResponderServlet()} のためのテスト・メソッド。
	 */
	public final void testResponderServlet() {
		try {
			new ResponderServlet();
		} catch(Exception e) {
			assertTrue(e.toString(), false);
		}
	}

	/**
	 * サーバにアサーションを設定しておく為の処理
	 * ※SiteTransferサーブレット呼び出し
	 */
	public final void beginSetAssertion(WebRequest theRequest) {
		// ターゲットサイトを指定
		theRequest.addParameter(SSOConstants.NAME_TARGET, "http://172.16.3.74:8080/sso_sample/target.html", WebRequest.GET_METHOD);
	}

	/**
	 * サーバにアサーションを設定しておく為の処理
	 * ※SiteTransferサーブレット呼び出し
	 */
	public final void testSetAssertion() {
		try {
			SAMLAssertion assertion1 = AssertionGenerator.generateAuthenticationAssertion(userId, SAMLSubject.CONF_ARTIFACT, SAMLAuthenticationStatement.AuthenticationMethod_Unspecified);
			Artifact artifact1 = AssertionGenerator.generateArtifact(AssertionManager.getInstance().add(assertion1));
			SAMLAssertion assertion2 = AssertionGenerator.generateAttributeAssertion(userId, getAttributes());
			Artifact artifact2 = AssertionGenerator.generateArtifact(AssertionManager.getInstance().add(assertion2));
			SAMLAssertion assertion3 = AssertionGenerator.generateAuthenticationAssertion(userId, SAMLSubject.CONF_ARTIFACT, SAMLAuthenticationStatement.AuthenticationMethod_Unspecified);
			Artifact artifact3 = AssertionGenerator.generateArtifact(AssertionManager.getInstance().add(assertion3));
			SAMLAssertion assertion4 = AssertionGenerator.generateAttributeAssertion(userId, getAttributes());
			Artifact artifact4 = AssertionGenerator.generateArtifact(AssertionManager.getInstance().add(assertion4));
			SAMLAssertion assertion5 = AssertionGenerator.generateAuthenticationAssertion(userId, SAMLSubject.CONF_ARTIFACT, SAMLAuthenticationStatement.AuthenticationMethod_Unspecified);
			Artifact artifact5 = AssertionGenerator.generateArtifact(AssertionManager.getInstance().add(assertion5));
			SAMLAssertion assertion6 = AssertionGenerator.generateAttributeAssertion(userId, getAttributes());
			Artifact artifact6 = AssertionGenerator.generateArtifact(AssertionManager.getInstance().add(assertion6));

			SiteTransfer st = new SiteTransfer(request, response);
			st.forward(Arrays.asList(new Artifact[]{artifact1, artifact2, artifact3, artifact4, artifact5, artifact6}), null);
		} catch(Exception e) {
			assertTrue(e.toString(), false);
		}
	}

	/**
	 * サーバにアサーションを設定しておく為の処理
	 * ※SiteTransferサーブレット呼び出し
	 */
	public final void endSetAssertion(WebResponse theResponse) {
		try {
			// URL確認
			String expectedReceiver = ReceiverResource.getReceiver("172.16.3.74", "sso_sample");
			String receiver = theResponse.getConnection().getHeaderField("Location");
			String[] str = receiver.split("\\?");
			assertTrue(expectedReceiver.equals(str[0]));
			String[] query = str[1].split("&");
			// TARGET,SAMLartの２パラメータ
			assertTrue(query.length == 7);
			List<String> artifacts = new ArrayList<String>();
			for(int i = 0; i < query.length; i++) {
				String[] s = query[i].split("=");
				if(s[0].equals(SSOConstants.NAME_SAMLART)) {
					// artifact文字列存在設定
					artifacts.add(URLDecoder.decode(s[1], "MS932"));
				}
			}
			ArtifactManager.getInstance().setArtifact((String[])artifacts.toArray(new String[artifacts.size()]));
		} catch(Exception e) {
			assertTrue(e.toString(), false);
		}
	}

	private final Map<String, List<String>> getAttributes() throws ResourceParseException {
		Map<String, List<String>> attributes = new HashMap<String, List<String>>();
		List keyList = SSOConfig.getSAMLAttributeList();
		for(int i = 0; i < keyList.size(); i++) {
			String key = keyList.get(i).toString();
			List<String> list = new ArrayList<String>();
			list.add("val_" + key);
			attributes.put(key, list);
		}
		return attributes;
	}

	private String createSoapMessage() {
		StringBuffer sb = new StringBuffer();
		sb.append("<SOAP-ENV:Envelope\n");
		sb.append("xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">\n");
		sb.append("<SOAP-ENV:Body>\n");
		sb.append("<samlp:Request\n");
		sb.append("xmlns=\"urn:oasis:names:tc:SAML:1.0:protocol\"\n");
		sb.append("xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\"\n");
		sb.append("xmlns:samlp=\"urn:oasis:names:tc:SAML:1.0:protocol\"\n");
		sb.append("xmlns:xsd=\"http://www.w3.org/2001/XMLSchem\"\n");
		sb.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
		sb.append("IssueInstant=\"2005-11-24T09:59:41.546Z\"\n");
		sb.append("MajorVersion=\"1\"\n");
		sb.append("MinorVersion=\"1\"\n");
		sb.append("RequestID=\"_503927df0a1dfd7a34855aead48d932183f1a12b\">\n");
		sb.append("<samlp:AssertionArtifact>\n");
		sb.append(ArtifactManager.getInstance().getArtifact()[0]+"\n");
//		sb.append("AAHyzDGlMaGfj50IThXR1yE00SD6tbnFfZuaqGNAHk/isHzPHXqJEirV\n");
		sb.append("</samlp:AssertionArtifact>\n");
		sb.append("</samlp:Request>\n");
		sb.append("</SOAP-ENV:Body>\n");
		sb.append("</SOAP-ENV:Envelope>\n");
		return sb.toString();
	}

	public final void beginServiceHttpServletRequestHttpServletResponse(WebRequest theRequest) {
		try {
			theRequest.setContentType("text/xml");
			theRequest.addParameter("SOAPAction", "http://www.oasis-open.org/committees/security", WebRequest.POST_METHOD);
			theRequest.setUserData(new BufferedInputStream(
					new ByteArrayInputStream(createSoapMessage().getBytes())));
		} catch(Exception e) {
			assertTrue(e.toString(), false);
		}
	}

	/**
	 * 成功
	 * 
	 * {@link info.dragonlady.sso.servlet.ResponderServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)} のためのテスト・メソッド。
	 */
	public final void testServiceHttpServletRequestHttpServletResponse() {
		try {
			ResponderServlet responder = new ResponderServlet();
			responder.service(request, response);
		} catch(Exception e) {
			assertTrue(e.toString(), true);
		}
	}

	public final void endServiceHttpServletRequestHttpServletResponse(WebResponse theResponse) {
		try {
			// SAML応答ステータスがSUCCESSならOK
			InputStream is = theResponse.getInputStream();
			byte[] b = new byte[is.available()];
			is.read(b);
			String content = new String(b, "UTF-8");
			content = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + content;
			Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(content.getBytes()));
			assertTrue(checkSuccessStatus(dom));
		} catch(Exception e) {
			assertTrue(e.toString(), false);
		}
	}

	private final boolean checkSuccessStatus(Node n) {
		NodeList nList = n.getChildNodes();
		for(int i = 0; i < nList.getLength(); i++) {
			Node node = nList.item(i);
			if(node.getNodeName().equals("StatusCode")) {
				NamedNodeMap nMap = node.getAttributes();
				Node attrNode = nMap.getNamedItem("Value");
				return attrNode.getNodeValue().equals("samlp:Success");
//				NodeList nl = attrNode.getChildNodes();
//				for(int j = 0; j < nl.getLength(); j++) {
//					Node item = nl.item(j);
//					if(item.getNodeType() == Node.TEXT_NODE) {
//						return item.getNodeValue().equals("samlp:Success");
//					}
//				}
			} else {
				boolean res = checkSuccessStatus(node);
				if(res) {
					return res;
				}
			}
		}
		return false;
	}

	public final void beginServiceHttpServletRequestHttpServletResponse2(WebRequest theRequest) {
		try {
			theRequest.setContentType("text/xml");
			theRequest.addParameter("SOAPAction", "http://www.oasis-open.org/committees/security", WebRequest.POST_METHOD);
			theRequest.setUserData(new BufferedInputStream(
					new ByteArrayInputStream(createSoapMessage().getBytes())));
		} catch(Exception e) {
			assertTrue(e.toString(), false);
		}
	}

	/**
	 * 失敗（対応アサーションがない）
	 * 
	 */
	public final void testServiceHttpServletRequestHttpServletResponse2() {
		try {
			// 対応するアサーションを削除しておく
			AssertionManager.getInstance().clear();

			ResponderServlet responder = new ResponderServlet();
			responder.service(request, response);
		} catch(Exception e) {
			assertTrue(e.toString(), true);
		}
	}

	public final void endServiceHttpServletRequestHttpServletResponse2(WebResponse theResponse) {
		try {
			InputStream is = theResponse.getInputStream();
			byte[] b = new byte[is.available()];
			is.read(b);
			String content = new String(b, "UTF-8");
			content = "<?xml version=\"1.0\" encoding=\"Shift_JIS\" ?>\n" + content;
			Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(content.getBytes()));
			assertTrue(checkFaultStatus(dom));
		} catch(Exception e) {
			assertTrue(e.toString(), false);
		}
	}

	private final boolean checkFaultStatus(Node n) {
		NodeList nList = n.getChildNodes();
		for(int i = 0; i < nList.getLength(); i++) {
			Node node = nList.item(i);
			if(node.getNodeName().equals("faultcode")) {
				NodeList nl = node.getChildNodes();
				for(int j = 0; j < nl.getLength(); j++) {
					Node item = nl.item(j);
					if(item.getNodeType() == Node.TEXT_NODE) {
						return item.getNodeValue().equals("soap:Server");
					}
				}
			} else {
				boolean res = checkFaultStatus(node);
				if(res) {
					return res;
				}
			}
		}
		return false;
	}

	public final void beginServiceHttpServletRequestHttpServletResponse3(WebRequest theRequest) {
		try {
			theRequest.setContentType("text/xml");
			theRequest.addParameter("SOAPAction", "http://www.oasis-open.org/committees/security", WebRequest.GET_METHOD);
			// XMLデータ送信なし
		} catch(Exception e) {
			assertTrue(e.toString(), false);
		}
	}

	/**
	 * GET要求ではSOAPバインディングで毎回エラー
	 * 
	 */
	public final void testServiceHttpServletRequestHttpServletResponse3() {
		try {
			ResponderServlet responder = new ResponderServlet();
			responder.service(request, response);
		} catch(Exception e) {
			assertTrue(e.toString(), true);
		}
	}

	public final void endServiceHttpServletRequestHttpServletResponse3(WebResponse theResponse) {
		try {
			int code = theResponse.getStatusCode();
			assertTrue(code == 403 || code == 302);
		} catch(Exception e) {
			assertTrue(e.toString(), false);
		}
	}

}
