/*
 ******************************************************************************
 *                                                                            *
 *          (C) COPYRIGHT by AEB GmbH 2010                                    *
 *                                                                            *
 ******************************************************************************
 */

/**
 * Creation 02.11.2010 - 13:47:34
 * 
 * Actual Version
 * ==============
 * $Revision$
 * $Author$
 *
 * For a detailed history of this file see bottom !
 */
package de.aeb.xnsg.archive.cr.jackrabbit;

import java.io.File;
import java.security.AccessControlException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.jcr.AccessDeniedException;
import javax.jcr.Node;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlList;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.AccessControlPolicy;
import javax.jcr.security.AccessControlPolicyIterator;
import javax.jcr.security.Privilege;

import junit.framework.TestCase;

import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.jackrabbit.core.TransientRepository;



/**
 * Checks the difference between {@link Session#checkPermission(String, String)}
 * and {@link Session#hasPermission(String, String)}.
 *
 * @author cech
 */
public class CheckPermissionTest extends TestCase {

	private static final String REPO_PATH = "C:/temp";
	
	protected void setUp() throws Exception {
		super.setUp();
	}

	protected void tearDown() throws Exception {
		super.tearDown();
	}

	public void test() throws Exception {
		Session session = null;
		Session session2 = null;
		Repository repo = null;
		Node n = null;
		try {
			repo =
					new TransientRepository(new File(getClass().getResource(
							"repository_fs.xml").toURI()), new File(REPO_PATH));
			session =
					repo.login(new SimpleCredentials("admin", "admin"
							.toCharArray()));
			n = session.getRootNode().addNode("level1");
			n = n.addNode("level2");
			n = n.addNode("level3");

			// create test-user
			User testUser = createUser(session, "newUser", "newPassword");
			setFullAccess(session, n, testUser);
			session.save();

			// read and print the privileges
			AccessControlManager acm = session.getAccessControlManager();
			AccessControlList acl = getList(acm, "/level1/level2/level3");
			Principal principal = testUser.getPrincipal();
			List privileges = new ArrayList();
			AccessControlEntry[] entries = acl.getAccessControlEntries();
			for (int i = 0; i < entries.length; i++) {
				AccessControlEntry ace = entries[i];
				if (principal.equals(ace.getPrincipal())) {
					privileges.addAll(Arrays.asList(ace.getPrivileges()));
				}
			}
			for (Object p : privileges) {
				System.out.println(((Privilege) p).getName());
			}
			session.logout();

			// login with added user
			assertTrue(privileges.size() > 0);
			session2 =
					repo.login(new SimpleCredentials("newUser", "newPassword"
							.toCharArray()));
			assertNotNull(session2);
			Node nodeUser = session2.getNode("/level1/level2/level3");

			// print the privileges
			principal = getUser(session2).getPrincipal();
			privileges = new ArrayList();
			entries = acl.getAccessControlEntries();
			for (int i = 0; i < entries.length; i++) {
				AccessControlEntry ace = entries[i];
				if (principal.equals(ace.getPrincipal())) {
					privileges.addAll(Arrays.asList(ace.getPrivileges()));
				}
			}
			for (Object p : privileges) {
				System.out.println(((Privilege) p).getName());
			}

			// check the permissions with Session.hasPermission (SUCCESS)
			session2.hasPermission(nodeUser.getPath(), Session.ACTION_READ);
			session2.hasPermission(nodeUser.getPath(), Session.ACTION_ADD_NODE);
			session2.hasPermission(nodeUser.getPath(),
					Session.ACTION_SET_PROPERTY);
			session2.hasPermission(nodeUser.getPath(), Session.ACTION_REMOVE);

			// check the permissions with Session.checkPermission (FAIL!)
			session2.checkPermission(nodeUser.getPath(), Session.ACTION_READ);
			session2.checkPermission(nodeUser.getPath(),
					Session.ACTION_SET_PROPERTY);

			// fails here!!!!
			try {
				session2.checkPermission(nodeUser.getPath(),
						Session.ACTION_ADD_NODE);
				fail("'add_node' permission should fail.");
			} catch (AccessControlException acex) {
				acex.printStackTrace();
			}
			try {
				session2.checkPermission(nodeUser.getPath(),
						Session.ACTION_REMOVE);
				fail("'remove' permission should fail.");
			} catch (AccessControlException acex) {
				acex.printStackTrace();
			}
		} finally {
			if (session != null) {
				session.logout();
			}
			if (session2 != null) {
				session2.logout();
			}
		}

	}

	private User createUser(Session session, String userId, String password)
			throws RepositoryException {
		UserManager um = ((JackrabbitSession) session).getUserManager();
		Authorizable auth = um.getAuthorizable(userId);
		if (auth != null) {
			return (User) auth;
		} else {
			User newUser = um.createUser(userId, password);
			return newUser;
		}
	}

	private void setFullAccess(Session session, Node node, User user)
			throws RepositoryException {
		AccessControlManager acm = session.getAccessControlManager();
		AccessControlPolicyIterator it =
				acm.getApplicablePolicies(node.getPath());
		while (it.hasNext()) {
			AccessControlPolicy acp = it.nextAccessControlPolicy();
			Privilege[] privileges =
					acm.privilegeFromName(Privilege.JCR_ALL)
							.getAggregatePrivileges();
			boolean ok =
					((AccessControlList) acp).addAccessControlEntry(user
							.getPrincipal(), privileges);
			if (!ok) {
				throw new RepositoryException(
						"Cannot set ACL (JCR_ALL) to node <" + node.getPath()
								+ ">.");
			}
			acm.setPolicy(node.getPath(), acp);
		}
		session.save();
	}

	private User getUser(Session session) throws RepositoryException {
		String userId = session.getUserID();
		UserManager um = getUserManager(session);
		Authorizable auth = um.getAuthorizable(userId);
		if (auth instanceof User) {
			return (User) um.getAuthorizable(userId);
		} else {
			throw new RepositoryException("The userid <" + userId
					+ "> is no valid repository user.");
		}
	}

	private UserManager getUserManager(Session session)
			throws RepositoryException {
		if (session instanceof JackrabbitSession) {
			return ((JackrabbitSession) session).getUserManager();
		} else {
			throw new RepositoryException(
					"This JCR implementation does not have a UserManager.");
		}
	}

	private AccessControlList getList(AccessControlManager acMgr,
			String path) throws AccessDeniedException, RepositoryException {
		for (AccessControlPolicyIterator it = acMgr.getApplicablePolicies(path); it
				.hasNext();) {
			AccessControlPolicy acp = it.nextAccessControlPolicy();
			if (acp instanceof AccessControlList) {
				return (AccessControlList) acp;
			}
		}
		AccessControlPolicy[] acps = acMgr.getPolicies(path);
		for (int i = 0; i < acps.length; i++) {
			if (acps[i] instanceof AccessControlList) {
				return (AccessControlList) acps[i];
			}
		}
		throw new RepositoryException("No AccessControlList at " + path);
	}

}
