/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.jetspeed.page;

import java.io.File;
import java.io.FileFilter;
import java.security.CodeSource;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import javax.security.auth.Subject;

import junit.framework.TestCase;

import net.sf.ehcache.CacheManager;

import org.apache.jetspeed.JetspeedActions;
import org.apache.jetspeed.cache.file.FileCache;
import org.apache.jetspeed.idgenerator.IdGenerator;
import org.apache.jetspeed.idgenerator.JetspeedIdGenerator;
import org.apache.jetspeed.om.common.SecurityConstraint;
import org.apache.jetspeed.om.common.SecurityConstraints;
import org.apache.jetspeed.om.folder.Folder;
import org.apache.jetspeed.om.folder.FolderNotFoundException;
import org.apache.jetspeed.om.folder.psml.FolderMetaDataImpl;
import org.apache.jetspeed.om.page.BaseFragmentElement;
import org.apache.jetspeed.om.page.DynamicPage;
import org.apache.jetspeed.om.page.Fragment;
import org.apache.jetspeed.om.page.FragmentDefinition;
import org.apache.jetspeed.om.page.Link;
import org.apache.jetspeed.om.page.Page;
import org.apache.jetspeed.om.page.PageSecurity;
import org.apache.jetspeed.om.page.PageTemplate;
import org.apache.jetspeed.om.page.SecurityConstraintsDef;
import org.apache.jetspeed.om.page.psml.DynamicPageImpl;
import org.apache.jetspeed.om.page.psml.FragmentDefinitionImpl;
import org.apache.jetspeed.om.page.psml.LinkImpl;
import org.apache.jetspeed.om.page.psml.PageImpl;
import org.apache.jetspeed.om.page.psml.PageSecurityImpl;
import org.apache.jetspeed.om.page.psml.PageTemplateImpl;
import org.apache.jetspeed.page.document.DocumentHandler;
import org.apache.jetspeed.page.document.DocumentHandlerFactory;
import org.apache.jetspeed.page.document.FolderHandler;
import org.apache.jetspeed.page.document.psml.CastorFileSystemDocumentHandler;
import org.apache.jetspeed.page.document.psml.DocumentHandlerFactoryImpl;
import org.apache.jetspeed.page.document.psml.FileSystemFolderHandler;
import org.apache.jetspeed.page.psml.CastorXmlPageManager;
import org.apache.jetspeed.security.JSSubject;
import org.apache.jetspeed.security.JetspeedPermission;
import org.apache.jetspeed.security.JetspeedPrincipal;
import org.apache.jetspeed.security.JetspeedPrincipalType;
import org.apache.jetspeed.security.Group;
import org.apache.jetspeed.security.PermissionFactory;
import org.apache.jetspeed.security.PrincipalsSet;
import org.apache.jetspeed.security.Role;
import org.apache.jetspeed.security.SecurityAttributeType;
import org.apache.jetspeed.security.SecurityAttributeTypes;
import org.apache.jetspeed.security.User;
import org.apache.jetspeed.security.impl.TransientJetspeedPrincipal;
import org.apache.jetspeed.security.spi.impl.FolderPermission;
import org.apache.jetspeed.security.spi.impl.FragmentPermission;
import org.apache.jetspeed.security.spi.impl.JetspeedPermissionFactory;
import org.apache.jetspeed.security.spi.impl.PagePermission;
import org.apache.jetspeed.cache.impl.EhCacheImpl;

/**
 * PageManagerTestShared
 * 
 * @author <a href="rwatler@apache.org">Randy Watler</a>
 * @version $Id: $
 *          
 */
public interface PageManagerTestShared
{
    public class Shared
    {
        /**
         * makeCastorXMLPageManager
         *
         * Create and configure a Castor XML PageManager.
         *
         * @param pagesDirName
         * @param permissionsEnabled
         * @param constraintsEnabled
         * @return page manager instance
         */
        static CastorXmlPageManager makeCastorXMLPageManager(String baseDir, String pagesDirName, boolean permissionsEnabled, boolean constraintsEnabled)
            throws Exception
        {
            Map extensionsToXslt = new HashMap();
            extensionsToXslt.put("psml",baseDir+"src/main/resources/stripIds.xslt");
                
            File pagesDirFile = new File(baseDir+"target/testdata/" + pagesDirName);
            
            
            DirectoryXMLTransform dirHelper = new DirectoryXMLTransform(pagesDirFile,extensionsToXslt);
            FileFilter noCVSorSVNorBackups = new FileFilter()
                {
                    public boolean accept( File pathname )
                    {
                        return !pathname.getName().equals("CVS") && !pathname.getName().equals(".svn") && !pathname.getName().endsWith("~");
                    }
                };
            dirHelper.copyFrom(new File(baseDir+"src/test/testdata/" + pagesDirName), noCVSorSVNorBackups);
            
            // copy documents under webapp/pages folder and strip fragment Ids
            File webappDestDirFile = new File(baseDir+"target/testdata/" + pagesDirName+"/webapp-no-ids");
            dirHelper.setBaseDirectory(webappDestDirFile);
            File webappPagesDirFile = new File(baseDir+"src/test/testdata/pages");
            dirHelper.copyFromAndTransform(webappPagesDirFile, noCVSorSVNorBackups);

            // copy documents under webapp/pages folder without transforming them
            webappDestDirFile = new File(baseDir+"target/testdata/" + pagesDirName+"/webapp-ids");
            dirHelper.setBaseDirectory(webappDestDirFile);
            dirHelper.copyFrom(webappPagesDirFile, noCVSorSVNorBackups);

            IdGenerator idGen = new JetspeedIdGenerator(65536,"P-","");
            FileCache cache = new FileCache(new EhCacheImpl( CacheManager.getInstance().getEhcache("pageFileCache")), 10);
            
            DocumentHandler psmlHandler = new CastorFileSystemDocumentHandler(idGen, "/JETSPEED-INF/castor/page-mapping.xml", Page.DOCUMENT_TYPE, PageImpl.class, baseDir + "target/testdata/" + pagesDirName, cache);
            DocumentHandler tpsmlHandler = new CastorFileSystemDocumentHandler(idGen, "/JETSPEED-INF/castor/page-mapping.xml", PageTemplate.DOCUMENT_TYPE, PageTemplateImpl.class, baseDir + "target/testdata/" + pagesDirName, cache);
            DocumentHandler dpsmlHandler = new CastorFileSystemDocumentHandler(idGen, "/JETSPEED-INF/castor/page-mapping.xml", DynamicPage.DOCUMENT_TYPE, DynamicPageImpl.class, baseDir + "target/testdata/" + pagesDirName, cache);
            DocumentHandler fpsmlHandler = new CastorFileSystemDocumentHandler(idGen, "/JETSPEED-INF/castor/page-mapping.xml", FragmentDefinition.DOCUMENT_TYPE, FragmentDefinitionImpl.class, baseDir + "target/testdata/" + pagesDirName, cache);
            DocumentHandler linkHandler = new CastorFileSystemDocumentHandler(idGen, "/JETSPEED-INF/castor/page-mapping.xml", Link.DOCUMENT_TYPE, LinkImpl.class, baseDir + "target/testdata/" + pagesDirName, cache);
            DocumentHandler folderMetaDataHandler = new CastorFileSystemDocumentHandler(idGen, "/JETSPEED-INF/castor/page-mapping.xml", FolderMetaDataImpl.DOCUMENT_TYPE, FolderMetaDataImpl.class, baseDir + "target/testdata/" + pagesDirName, cache);
            DocumentHandler pageSecurityHandler = new CastorFileSystemDocumentHandler(idGen, "/JETSPEED-INF/castor/page-mapping.xml", PageSecurityImpl.DOCUMENT_TYPE, PageSecurity.class, baseDir + "target/testdata/" + pagesDirName, cache);
            
            DocumentHandlerFactory handlerFactory = new DocumentHandlerFactoryImpl();
            handlerFactory.registerDocumentHandler(psmlHandler);
            handlerFactory.registerDocumentHandler(tpsmlHandler);
            handlerFactory.registerDocumentHandler(dpsmlHandler);
            handlerFactory.registerDocumentHandler(fpsmlHandler);
            handlerFactory.registerDocumentHandler(linkHandler);
            handlerFactory.registerDocumentHandler(folderMetaDataHandler);
            handlerFactory.registerDocumentHandler(pageSecurityHandler);
            FolderHandler folderHandler = new FileSystemFolderHandler(idGen, baseDir+"target/testdata/" + pagesDirName, handlerFactory, cache);

            CastorXmlPageManager pageManager = new CastorXmlPageManager(idGen, handlerFactory, folderHandler, cache, permissionsEnabled, constraintsEnabled);
            pageManager.init();
            return pageManager;
        }
        
        /**
         * shutdownCastorXmlPageManager
         * 
         * shutdown page manager and free cache between test invocations
         */
        static void shutdownCastorXMLPageManager(CastorXmlPageManager pageManager)
        {
            // reset to clear cache
            pageManager.reset();
            // shutdown page manager and handlers
            pageManager.shutdown();
            // destroy page manager
            pageManager.destroy();
        }

        /**
         * makeListFromCSV
         *
         * Create List of String values from CSV String for principals/permissions.
         * 
         * @param csv CSV string
         * @return values list
         */
        static List makeListFromCSV(String csv)
        {
            if (csv != null)
            {
                List csvList = new ArrayList();
                if (csv.indexOf(',') != -1)
                {
                    StringTokenizer csvTokens = new StringTokenizer(csv, ",");
                    while (csvTokens.hasMoreTokens())
                    {
                        csvList.add(csvTokens.nextToken().trim());
                    }
                }
                else
                {
                    csvList.add(csv);
                }
                return csvList;
            }
            return null;        
        }

        /**
         * makeCSVFromList
         *
         * Create CSV String for principals/permissions from List of String values
         * 
         * @param list values list
         * @return CSV string
         */
        static String makeCSVFromList(List list)
        {
            if ((list != null) && !list.isEmpty())
            {
                StringBuffer csv = new StringBuffer();
                Iterator listIter = list.iterator();
                while (listIter.hasNext())
                {
                    if (csv.length() > 0)
                    {
                        csv.append(",");
                    }
                    csv.append((String)listIter.next());
                }
                return csv.toString();
            }
            return null;
        }

        /**
         * testSecurePageManager
         *
         * @param test case
         * @param page manager
         */
        static void testSecurePageManager(final TestCase test, final PageManager pageManager) throws Exception
        {
            // tracking
            final String [] somePortletId = new String[1];

            // reset page manager cache
            pageManager.reset();
            
            // setup test subjects
            Set principals = new PrincipalsSet();
            principals.add(new TestUser("admin"));
            principals.add(new TestRole("admin"));
            Subject adminSubject = new Subject(true, principals, new HashSet(), new HashSet());
            
            principals = new PrincipalsSet();
            principals.add(new TestUser("user"));
            Subject userSubject = new Subject(true, principals, new HashSet(), new HashSet());
            
            principals = new PrincipalsSet();
            principals.add(new TestUser("manager"));
            principals.add(new TestRole("manager"));
            Subject managerSubject = new Subject(true, principals, new HashSet(), new HashSet());

            principals = new PrincipalsSet();
            principals.add(new TestUser("guest"));
            Subject guestSubject = new Subject(true, principals, new HashSet(), new HashSet());

            // setup test as admin user
            Exception setup = (Exception)JSSubject.doAsPrivileged(adminSubject, new PrivilegedAction()
                {
                    public Object run()
                    {
                        try
                        {
                            // reset page manager to initial state
                            try
                            {
                                Folder removeRootFolder = pageManager.getFolder("/");
                                pageManager.removeFolder(removeRootFolder);
                                pageManager.reset();
                            }
                            catch (FolderNotFoundException e)
                            {
                            }

                            // create test documents and folders
                            Folder folder = pageManager.newFolder("/");
                            SecurityConstraints constraints = pageManager.newSecurityConstraints();
                            constraints.setOwner("admin");
                            List constraintsRefs = new ArrayList(1);
                            constraintsRefs.add("public-view");
                            constraints.setSecurityConstraintsRefs(constraintsRefs);
                            folder.setSecurityConstraints(constraints);
                            pageManager.updateFolder(folder);
                        
                            PageSecurity pageSecurity = pageManager.newPageSecurity();
                            List constraintsDefs = new ArrayList(2);
                            SecurityConstraintsDef constraintsDef = pageManager.newSecurityConstraintsDef();
                            constraintsDef.setName("public-view");
                            List defConstraints = new ArrayList(1);
                            SecurityConstraint defConstraint = pageManager.newPageSecuritySecurityConstraint();
                            defConstraint.setUsers(Shared.makeListFromCSV("*"));
                            defConstraint.setPermissions(Shared.makeListFromCSV("view"));
                            defConstraints.add(defConstraint);
                            constraintsDef.setSecurityConstraints(defConstraints);
                            constraintsDefs.add(constraintsDef);
                            constraintsDef = pageManager.newSecurityConstraintsDef();
                            constraintsDef.setName("admin-all");
                            defConstraints = new ArrayList(1);
                            defConstraint = pageManager.newPageSecuritySecurityConstraint();
                            defConstraint.setRoles(Shared.makeListFromCSV("admin"));
                            defConstraint.setPermissions(Shared.makeListFromCSV("view,edit"));
                            defConstraints.add(defConstraint);
                            constraintsDef.setSecurityConstraints(defConstraints);
                            constraintsDefs.add(constraintsDef);
                            pageSecurity.setSecurityConstraintsDefs(constraintsDefs);
                            List globalConstraintsRefs = new ArrayList(1);
                            globalConstraintsRefs.add("admin-all");
                            pageSecurity.setGlobalSecurityConstraintsRefs(globalConstraintsRefs);
                            pageManager.updatePageSecurity(pageSecurity);
                        
                            Page page = pageManager.newPage("/default-page.psml");
                            constraints = pageManager.newSecurityConstraints();
                            constraints.setOwner("admin");
                            List inlineConstraints = new ArrayList(1);
                            SecurityConstraint constraint = pageManager.newPageSecurityConstraint();
                            constraint.setRoles(Shared.makeListFromCSV("manager"));
                            constraint.setPermissions(Shared.makeListFromCSV("edit"));
                            inlineConstraints.add(constraint);
                            constraints.setSecurityConstraints(inlineConstraints);
                            constraintsRefs = new ArrayList(1);
                            constraintsRefs.add("public-view");
                            constraints.setSecurityConstraintsRefs(constraintsRefs);
                            page.setSecurityConstraints(constraints);
                            BaseFragmentElement rootFragmentElement = page.getRootFragment();
                            TestCase.assertTrue(rootFragmentElement instanceof Fragment);
                            Fragment root = (Fragment)rootFragmentElement;
                            root.setName("jetspeed-layouts::VelocityTwoColumns");
                            Fragment portlet = pageManager.newPortletFragment();
                            portlet.setName("security::LoginPortlet");
                            root.getFragments().add(portlet);
                            portlet = pageManager.newPortletFragment();
                            portlet.setName("some-app::SomePortlet");
                            SecurityConstraints fragmentConstraints = pageManager.newSecurityConstraints();
                            fragmentConstraints.setOwner("user");
                            portlet.setSecurityConstraints(fragmentConstraints);
                            root.getFragments().add(portlet);
                            pageManager.updatePage(page);
                            BaseFragmentElement validateRootFragmentElement = page.getRootFragment();
                            TestCase.assertTrue(validateRootFragmentElement instanceof Fragment);
                            Fragment validateRoot = (Fragment)validateRootFragmentElement;                            
                            TestCase.assertNotNull(validateRoot.getFragments());
                            TestCase.assertEquals(2, validateRoot.getFragments().size());
                            BaseFragmentElement validateFragmentElement = (BaseFragmentElement)validateRoot.getFragments().get(1);
                            TestCase.assertTrue(validateFragmentElement instanceof Fragment);
                            Fragment validateFragment = (Fragment)validateFragmentElement;
                            TestCase.assertEquals("some-app::SomePortlet", validateFragment.getName());
                            TestCase.assertFalse("0".equals(validateFragment.getId()));
                            somePortletId[0] = validateFragment.getId();
                        
                            page = pageManager.newPage("/user-page.psml");
                            constraints = pageManager.newSecurityConstraints();
                            inlineConstraints = new ArrayList(1);
                            constraint = pageManager.newPageSecurityConstraint();
                            constraint.setUsers(Shared.makeListFromCSV("user"));
                            constraint.setPermissions(Shared.makeListFromCSV("view,edit"));
                            inlineConstraints.add(constraint);
                            constraints.setSecurityConstraints(inlineConstraints);
                            page.setSecurityConstraints(constraints);
                            pageManager.updatePage(page);

                            Link link = pageManager.newLink("/default.link");
                            link.setUrl("http://www.default.org/");
                            constraints = pageManager.newSecurityConstraints();
                            constraints.setOwner("admin");
                            inlineConstraints = new ArrayList(1);
                            constraint = pageManager.newLinkSecurityConstraint();
                            constraint.setRoles(Shared.makeListFromCSV("manager"));
                            constraint.setPermissions(Shared.makeListFromCSV("edit"));
                            inlineConstraints.add(constraint);
                            constraints.setSecurityConstraints(inlineConstraints);
                            link.setSecurityConstraints(constraints);                        
                            pageManager.updateLink(link);

                            return null;
                        }
                        catch (Exception e)
                        {
                            return e;
                        }
                        finally
                        {
                            JSSubject.clearSubject();
                        }
                    }
                }, null);
            if (setup != null)
            {
                throw setup;
            }

            // reset page manager cache
            pageManager.reset();

            // access test as admin user
            Exception adminAccess = (Exception)JSSubject.doAsPrivileged(adminSubject, new PrivilegedAction()
                {
                    public Object run()
                    {
                        try
                        {
                            // test view access
                            Folder folder = pageManager.getFolder("/");
                            TestCase.assertNotNull(folder.getPageSecurity());
                            TestCase.assertNotNull(folder.getPages());
                            TestCase.assertEquals(2, folder.getPages().size());
                            TestCase.assertNotNull(pageManager.getPages(folder));
                            TestCase.assertEquals(2, pageManager.getPages(folder).size());
                            PageSecurity pageSecurity = pageManager.getPageSecurity();
                            Page page0 = pageManager.getPage("/default-page.psml");
                            BaseFragmentElement validateRootFragmentElement = page0.getRootFragment();
                            TestCase.assertTrue(validateRootFragmentElement instanceof Fragment);
                            Fragment validateRoot = (Fragment)validateRootFragmentElement;                            
                            TestCase.assertNotNull(validateRoot.getFragments());
                            TestCase.assertEquals(2, validateRoot.getFragments().size());
                            TestCase.assertNotNull(page0.getFragmentById(somePortletId[0]));
                            TestCase.assertNotNull(page0.getFragmentsByName("some-app::SomePortlet"));
                            TestCase.assertEquals(1, page0.getFragmentsByName("some-app::SomePortlet").size());
                            Page page1 = pageManager.getPage("/user-page.psml");
                            Link link = pageManager.getLink("/default.link");
                            // test edit access
                            pageManager.updateFolder(folder);
                            pageManager.updatePageSecurity(pageSecurity);
                            pageManager.updatePage(page0);
                            pageManager.updatePage(page1);
                            pageManager.updateLink(link);
                            return null;
                        }
                        catch (Exception e)
                        {
                            return e;
                        }
                        finally
                        {
                            JSSubject.clearSubject();
                        }
                    }
                }, null);
            if (adminAccess != null)
            {
                throw adminAccess;
            }

            // access test as user user
            Exception userAccess = (Exception)JSSubject.doAsPrivileged(userSubject, new PrivilegedAction()
                {
                    public Object run()
                    {
                        try
                        {
                            // test view access
                            Folder folder = pageManager.getFolder("/");
                            TestCase.assertNotNull(folder.getPageSecurity());
                            TestCase.assertNotNull(folder.getPages());
                            TestCase.assertEquals(2, folder.getPages().size());
                            PageSecurity pageSecurity = pageManager.getPageSecurity();
                            Page page0 = pageManager.getPage("/default-page.psml");                            
                            BaseFragmentElement validateRootFragmentElement = page0.getRootFragment();
                            TestCase.assertTrue(validateRootFragmentElement instanceof Fragment);
                            Fragment validateRoot = (Fragment)validateRootFragmentElement;                            
                            TestCase.assertNotNull(validateRoot.getFragments());
                            TestCase.assertEquals(2, validateRoot.getFragments().size());
                            TestCase.assertNotNull(page0.getFragmentById(somePortletId[0]));
                            TestCase.assertNotNull(page0.getFragmentsByName("some-app::SomePortlet"));
                            TestCase.assertEquals(1, page0.getFragmentsByName("some-app::SomePortlet").size());
                            Page page1 = pageManager.getPage("/user-page.psml");
                            Link link = pageManager.getLink("/default.link");
                            // test edit access
                            try
                            {
                                pageManager.updateFolder(folder);
                                TestCase.assertTrue("Folder / not editable for user", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            try
                            {
                                pageManager.updatePageSecurity(pageSecurity);
                                TestCase.assertTrue("PageSecurity not editable for user", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            try
                            {
                                pageManager.updatePage(page0);
                                TestCase.assertTrue("Page /default-page.psml not editable for user", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            pageManager.updatePage(page1);
                            try
                            {
                                pageManager.updateLink(link);
                                TestCase.assertTrue("Page /default.link not editable for user", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            return null;
                        }
                        catch (Exception e)
                        {
                            return e;
                        }
                        finally
                        {
                            JSSubject.clearSubject();
                        }
                    }
                }, null);
            if (userAccess != null)
            {
                throw userAccess;
            }

            // access test as manager user
            Exception managerAccess = (Exception)JSSubject.doAsPrivileged(managerSubject, new PrivilegedAction()
                {
                    public Object run()
                    {
                        try
                        {
                            // test view access
                            Folder folder = pageManager.getFolder("/");
                            TestCase.assertNotNull(folder.getPageSecurity());
                            TestCase.assertNotNull(folder.getPages());
                            TestCase.assertEquals(1, folder.getPages().size());
                            PageSecurity pageSecurity = pageManager.getPageSecurity();
                            Page page0 = pageManager.getPage("/default-page.psml");
                            BaseFragmentElement validateRootFragmentElement = page0.getRootFragment();
                            TestCase.assertTrue(validateRootFragmentElement instanceof Fragment);
                            Fragment validateRoot = (Fragment)validateRootFragmentElement;
                            TestCase.assertNotNull(validateRoot.getFragments());
                            TestCase.assertEquals(1, validateRoot.getFragments().size());
                            TestCase.assertNull(page0.getFragmentById(somePortletId[0]));
                            TestCase.assertTrue(page0.getFragmentsByName("some-app::SomePortlet").isEmpty());
                            Link link = pageManager.getLink("/default.link");
                            try
                            {
                                pageManager.getPage("/user-page.psml");
                                TestCase.assertTrue("Page /user-page.psml not viewable for manager", false);
                            }
                            catch (SecurityException se)
                            {
                            }                        
                            // test edit access
                            try
                            {
                                pageManager.updateFolder(folder);
                                TestCase.assertTrue("Folder / not editable for manager", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            try
                            {
                                pageManager.updatePageSecurity(pageSecurity);
                                TestCase.assertTrue("PageSecurity not editable for manager", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            pageManager.updatePage(page0);
                            pageManager.updateLink(link);
                            return null;
                        }
                        catch (Exception e)
                        {
                            return e;
                        }
                        finally
                        {
                            JSSubject.clearSubject();
                        }
                    }
                }, null);
            if (managerAccess != null)
            {
                throw managerAccess;
            }

            // access test as guest user
            Exception guestAccess = (Exception)JSSubject.doAsPrivileged(guestSubject, new PrivilegedAction()
                {
                    public Object run()
                    {
                        try
                        {
                            // test view access
                            Folder folder = pageManager.getFolder("/");
                            TestCase.assertNotNull(folder.getPageSecurity());
                            TestCase.assertNotNull(folder.getPages());
                            TestCase.assertEquals(1, folder.getPages().size());
                            PageSecurity pageSecurity = pageManager.getPageSecurity();
                            Page page0 = pageManager.getPage("/default-page.psml");
                            BaseFragmentElement validateRootFragmentElement = page0.getRootFragment();
                            TestCase.assertTrue(validateRootFragmentElement instanceof Fragment);
                            Fragment validateRoot = (Fragment)validateRootFragmentElement;                            
                            TestCase.assertNotNull(validateRoot.getFragments());
                            TestCase.assertEquals(1, validateRoot.getFragments().size());
                            TestCase.assertNull(page0.getFragmentById(somePortletId[0]));
                            TestCase.assertTrue(page0.getFragmentsByName("some-app::SomePortlet").isEmpty());
                            Link link = pageManager.getLink("/default.link");
                            try
                            {
                                pageManager.getPage("/user-page.psml");
                                TestCase.assertTrue("Page /user-page.psml not viewable for guest", false);
                            }
                            catch (SecurityException se)
                            {
                            }                        
                            // test edit access
                            try
                            {
                                pageManager.updateFolder(folder);
                                TestCase.assertTrue("Folder / not editable for guest", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            try
                            {
                                pageManager.updatePageSecurity(pageSecurity);
                                TestCase.assertTrue("PageSecurity not editable for guest", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            try
                            {
                                pageManager.updatePage(page0);
                                TestCase.assertTrue("Page /default-page.psml not editable for guest", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            try
                            {
                                pageManager.updateLink(link);
                                TestCase.assertTrue("Page /default.link not editable for guest", false);
                            }
                            catch (SecurityException se)
                            {
                            }
                            return null;
                        }
                        catch (Exception e)
                        {
                            return e;
                        }
                        finally
                        {
                            JSSubject.clearSubject();
                        }
                    }
                }, null);
            if (guestAccess != null)
            {
                throw guestAccess;
            }

            // reset page manager cache
            pageManager.reset();

            // cleanup test as admin user
            Exception cleanup = (Exception)JSSubject.doAsPrivileged(adminSubject, new PrivilegedAction()
                {
                    public Object run()
                    {
                        try
                        {
                            // cleanup by removing root folder
                            try
                            {
                                Folder remove = pageManager.getFolder("/");
                                TestCase.assertEquals("/", remove.getPath());
                                pageManager.removeFolder(remove);
                            }
                            catch (FolderNotFoundException e)
                            {
                                TestCase.assertTrue("Folder / NOT FOUND", false);
                            }

                            return null;
                        }
                        catch (Exception e)
                        {
                            return e;
                        }
                        finally
                        {
                            JSSubject.clearSubject();
                        }
                    }
                }, null);
            if (cleanup != null)
            {
                throw cleanup;
            }
        }
    }

    /**
     * PageManagerPermissionsPolicy
     *
     * Policy implementation for permissions based security
     * tests against testSecurePageManager test case above. 
     */
    static class PageManagerPermissionsPolicy extends Policy
    {
        private Policy defaultPolicy;
        private static PermissionFactory pf = new TestPermissionFactory();
        static 
        {
            org.apache.jetspeed.om.page.psml.AbstractBaseElement.setPermissionsFactory(pf);
            org.apache.jetspeed.om.page.impl.BaseElementImpl.setPermissionsFactory(pf);
            org.apache.jetspeed.om.folder.impl.FolderImpl.setPermissionsFactory(pf);
            org.apache.jetspeed.om.folder.psml.FolderImpl.setPermissionsFactory(pf);
        }

        public PageManagerPermissionsPolicy(Policy defaultPolicy)
        {
            this.defaultPolicy = defaultPolicy;
        }

        public boolean implies(ProtectionDomain domain, Permission permission)
        {
            // classify policy query for local test case; this implementation
            // is not optimized: multiple protection domains exist on the
            // call stack, so this method will be invoked 2-3 times for each
            // access check with the identical principals and permission
            JetspeedPermission j2p = permission instanceof JetspeedPermission ? (JetspeedPermission)permission : null;
            boolean testPermission = j2p != null && (j2p.getType().equals("folder")||j2p.getType().equals("page")||j2p.getType().equals("fragment"));
            Principal[] principals = domain.getPrincipals();
            if ((principals != null) && (principals.length > 0) && (testPermission))
            {
                // check permission using principals if available
                Permissions permissions = new Permissions();
                for (int i = 0; (i < principals.length); i++)
                {
                    if (principals[i] instanceof User)
                    {
                        // get permissions for users
                        String user = principals[i].getName();
                        if (user.equals("admin"))
                        {
                            // owner permissions                            
                            permissions.add((Permission)pf.newPermission("folder", "/", "view, edit"));
                            permissions.add((Permission)pf.newPermission("page", "/default-page.psml", "view, edit"));
                        }
                        else if (user.equals("user"))
                        {
                            // owner permissions
                            permissions.add((Permission)pf.newPermission("fragment", "/default-page.psml/some-app::SomePortlet", "view, edit"));
                            
                            // granted permissions
                            permissions.add((Permission)pf.newPermission("page", "/user-page.psml", "view, edit"));
                            permissions.add((Permission)pf.newPermission("fragment", "/user-page.psml/*", "view"));
                        }
                        
                        // public view permissions
                        permissions.add((Permission)pf.newPermission("folder", "/", "view"));
                        permissions.add((Permission)pf.newPermission("page", "/default-page.psml", "view"));
                        permissions.add((Permission)pf.newPermission("page", "/page.security", "view"));
                        permissions.add((Permission)pf.newPermission("fragment", "security::*", "view"));
                    }
                    else if (principals[i] instanceof Role)
                    {
                        // get permissions for roles
                        String role = principals[i].getName();
                        if (role.equals("admin"))
                        {
                            // global permissions
                            permissions.add((Permission)pf.newPermission("folder", "<<ALL FILES>>", "view, edit"));
                            permissions.add((Permission)pf.newPermission("fragment", "<<ALL FRAGMENTS>>", "view, edit"));
                        }
                        else if (role.equals("manager"))
                        {
                            // granted permissions
                            permissions.add((Permission)pf.newPermission("page", "/default-page.psml", "edit"));
                            permissions.add((Permission)pf.newPermission("page", "/default.link", "edit"));
                        }
                    }
                }
                
                // check permission
                if (permissions.implies(permission))
                {
                    return true;
                }
            }

            // check default permissions
            if (defaultPolicy != null)
            {
                return defaultPolicy.implies(domain, permission);
            }
            return false;
        }

        public PermissionCollection getPermissions(ProtectionDomain domain)
        {
            // return default permissions only since
            // domain and permsission not available
            if (defaultPolicy != null)
            {
                return defaultPolicy.getPermissions(domain);
            }
            return new Permissions();
        }

        public PermissionCollection getPermissions(CodeSource codesource)
        {
            // return default permissions only since
            // domain and permsission not available
            if (defaultPolicy != null)
            {
                return defaultPolicy.getPermissions(codesource);
            }
            return new Permissions();
        }

        public void refresh()
        {
            // propagate refresh
            if (defaultPolicy != null)
            {
                defaultPolicy.refresh();
            }
        }
    }
    
    public static abstract class AbstractTestPrincipal extends TransientJetspeedPrincipal
    {
        private static final SecurityAttributeTypes attributeTypes = new SecurityAttributeTypes()
        {

            public Map<String, SecurityAttributeType> getAttributeTypeMap()
            {
                return Collections.emptyMap();
            }

            public Map<String, SecurityAttributeType> getAttributeTypeMap(String category)
            {
                return Collections.emptyMap();
            }

            public boolean isExtendable()
            {
                return false;
            }

            public boolean isReadOnly()
            {
                return true;
            }
        };
        
        private JetspeedPrincipalType type;
        
        private static final long serialVersionUID = 1L;
        

        public AbstractTestPrincipal(final String type, String name)
        {
            super(type, name);
            this.type = new JetspeedPrincipalType()
            {               
                public SecurityAttributeTypes getAttributeTypes()
                {
                    return attributeTypes;
                }

                public String getClassName()
                {
                    return null;
                }

                public String getName()
                {
                    return type;
                }

                public Class<JetspeedPrincipal> getPrincipalClass()
                {
                    return null;
                }
            };
        }

        public synchronized JetspeedPrincipalType getType()
        {
            return type;
        }
    }
    
    public static class TestUser extends AbstractTestPrincipal implements User
    {
        private static final long serialVersionUID = 1L;

        public TestUser(String name)
        {
            super(JetspeedPrincipalType.USER, name);
        }
    }

    public static class TestGroup extends AbstractTestPrincipal implements Group
    {
        private static final long serialVersionUID = 1L;

        public TestGroup(String name)
        {
            super(JetspeedPrincipalType.GROUP, name);
        }
    }
    
    public static class TestRole extends AbstractTestPrincipal implements Role
    {
        private static final long serialVersionUID = 1L;

        public TestRole(String name)
        {
            super(JetspeedPrincipalType.ROLE, name);
        }
    }
    
    public static class TestPermissionFactory implements PermissionFactory
    {
        private static Map<String, JetspeedPermissionFactory> factories = new HashMap<String, JetspeedPermissionFactory>();
        static
        {
            factories.put("folder", new FolderPermission.Factory());
            factories.put("page", new PagePermission.Factory());
            factories.put("fragment", new FragmentPermission.Factory());
        }

        public JetspeedPermission newPermission(String type, String name, String actions)
        {
            return factories.get(type).newPermission(name, actions);
        }

        public JetspeedPermission newPermission(String type, String name, int mask)
        {
            return factories.get(type).newPermission(name, mask);
        }

        public int parseActions(String actions)
        {
            return JetspeedActions.getContainerActionsMask(actions);
        }
    }
}
