/*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see .
 */
package org.alfresco.repo.rendition;
import java.io.File;
import java.io.Serializable;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.model.RenditionModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.rendition.executer.ImageRenderingEngine;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseAlfrescoSpringTest;
import org.alfresco.util.PropertyMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
 * @author Neil McErlean
 * @since 3.3
 */
public class RenditionServicePermissionsTest extends BaseAlfrescoSpringTest
{
    /** Logger */
    private static Log logger = LogFactory.getLog(RenditionServicePermissionsTest.class);
    private final static QName RESCALE_RENDER_DEFN_NAME = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
            ImageRenderingEngine.NAME + System.currentTimeMillis());
    private NodeRef nodeWithImageContent;
    private NodeRef testFolder;
    private PermissionService permissionService;
    private PersonService personService;
    private RenditionService renditionService;
    private Repository repositoryHelper;
    private RetryingTransactionHelper transactionHelper;
    private String testFolderName;
    
    @SuppressWarnings("deprecation")
    @Override
    protected void onSetUpInTransaction() throws Exception
    {
        super.onSetUpInTransaction();
        this.permissionService = (PermissionService)this.applicationContext.getBean("PermissionService");
        this.personService = (PersonService)this.applicationContext.getBean("PersonService");
        this.renditionService = (RenditionService) this.applicationContext.getBean("renditionService");
        this.repositoryHelper = (Repository) this.applicationContext.getBean("repositoryHelper");
        this.transactionHelper = (RetryingTransactionHelper) this.applicationContext
                    .getBean("retryingTransactionHelper");
        
        NodeRef companyHome = this.repositoryHelper.getCompanyHome();
        // Create the test folder used for these tests
        this.testFolderName= "Test-folder-"+ System.currentTimeMillis();
        this.testFolder = nodeService.createNode(companyHome,
                    ContentModel.ASSOC_CONTAINS,
                    QName.createQName(NamespaceService.APP_MODEL_1_0_URI,testFolderName),
                                ContentModel.TYPE_FOLDER).getChildRef();
        nodeService.setProperty(testFolder, ContentModel.PROP_NAME, testFolderName);
        // Create the node used as a content supplier for tests
        Map props = new HashMap();
        props.put(ContentModel.PROP_NAME, "Test-image-node-" + System.currentTimeMillis());
        String testImageNodeName = "testImageNode" + System.currentTimeMillis();
        this.nodeWithImageContent = nodeService.createNode(companyHome, ContentModel.ASSOC_CONTAINS,
                    QName.createQName(NamespaceService.APP_MODEL_1_0_URI, testImageNodeName),
                    ContentModel.TYPE_CONTENT, props).getChildRef();
        // Stream some well-known image content into the node.
        URL url = RenditionServicePermissionsTest.class.getClassLoader().getResource("images/gray21.512.png");
        assertNotNull("url of test image was null", url);
        File imageFile = new File(url.getFile());
        assertTrue(imageFile.exists());
        nodeService.setProperty(nodeWithImageContent, ContentModel.PROP_CONTENT, new ContentData(null,
                    MimetypeMap.MIMETYPE_IMAGE_PNG, 0L, null));
        ContentWriter writer = contentService.getWriter(nodeWithImageContent, ContentModel.PROP_CONTENT, true);
        writer.setMimetype(MimetypeMap.MIMETYPE_IMAGE_PNG);
        writer.setEncoding("UTF-8");
        writer.putContent(imageFile);
    }
    
    @Override
    protected void onTearDownInTransaction() throws Exception
    {
        nodeService.deleteNode(nodeWithImageContent);
        nodeService.deleteNode(testFolder);
    }
    /**
     * This test method uses the RenditionService to render a test document and place the
     * rendition under a folder for which the user does not have write permissions.
     * This should be allowed as all renditions are performed as system.
     */
    @SuppressWarnings("deprecation")
    public void testRenditionAccessPermissions() throws Exception
    {
        this.setComplete();
        this.endTransaction();
        
        final String normalUser = "renditionUser";
        // As admin, create a user who has read-only access to the testFolder
        transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback()
        {
            public Void execute() throws Throwable
            {
                // Set the current security context as admin
                AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
                assertEquals("admin", authenticationService.getCurrentUserName());
                if (authenticationService.authenticationExists(normalUser) == false)
                {
                    authenticationService.createAuthentication(normalUser, "PWD".toCharArray());
                    
                    PropertyMap personProperties = new PropertyMap();
                    personProperties.put(ContentModel.PROP_USERNAME, normalUser);
                    personProperties.put(ContentModel.PROP_AUTHORITY_DISPLAY_NAME, "title" + normalUser);
                    personProperties.put(ContentModel.PROP_FIRSTNAME, "firstName");
                    personProperties.put(ContentModel.PROP_LASTNAME, "lastName");
                    personProperties.put(ContentModel.PROP_EMAIL, "email@email.com");
                    personProperties.put(ContentModel.PROP_JOBTITLE, "jobTitle");
                    
                    personService.createPerson(personProperties);
                }
                
                // Restrict write access to the test folder
                permissionService.setPermission(testFolder, normalUser, PermissionService.CONSUMER, true);
                
                return null;
            }
        });
        // As the user, render a piece of content with the rendition going to testFolder
        final NodeRef rendition = transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback()
        {
            public NodeRef execute() throws Throwable
            {
                // Set the current security context as a rendition user
                AuthenticationUtil.setFullyAuthenticatedUser(normalUser);
                assertEquals(normalUser, authenticationService.getCurrentUserName());
                
                assertFalse("Source node has unexpected renditioned aspect.", nodeService.hasAspect(nodeWithImageContent,
                            RenditionModel.ASPECT_RENDITIONED));
                String path = testFolderName+"/testRendition.png";
                // Create the rendering action.
                RenditionDefinition action = makeRescaleImageAction();
                action.setParameterValue(RenditionService.PARAM_DESTINATION_PATH_TEMPLATE, path);
                // Perform the action with an explicit destination folder
                logger.debug("Creating rendition of: " + nodeWithImageContent);
                ChildAssociationRef renditionAssoc = renditionService.render(nodeWithImageContent, action);
                logger.debug("Created rendition: " + renditionAssoc.getChildRef());
                
                NodeRef renditionNode = renditionAssoc.getChildRef();
                
                assertEquals("The parent node was not correct", nodeWithImageContent, renditionAssoc.getParentRef());
                logger.debug("rendition's primary parent: " + nodeService.getPrimaryParent(renditionNode));
                assertEquals("The parent node was not correct", testFolder, nodeService.getPrimaryParent(renditionNode).getParentRef());
                // Now the source content node should have the rn:renditioned aspect
                assertTrue("Source node is missing renditioned aspect.", nodeService.hasAspect(nodeWithImageContent,
                            RenditionModel.ASPECT_RENDITIONED));
                
                return renditionNode;
            }
        });
        transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback()
                {
                    public Void execute() throws Throwable
                    {
                        // Set the current security context as admin
                        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
                        final Serializable renditionCreator = nodeService.getProperty(rendition, ContentModel.PROP_CREATOR);
                        assertEquals("Incorrect creator", normalUser, renditionCreator);
                        return null;
                    }
                });
        }
    /**
     * Creates a RenditionDefinition for the RescaleImageActionExecutor.
     * 
     * @return A new RenderingAction.
     */
    private RenditionDefinition makeRescaleImageAction()
    {
        RenditionDefinition result = renditionService.createRenditionDefinition(RESCALE_RENDER_DEFN_NAME,
                    ImageRenderingEngine.NAME);
        result.setParameterValue(ImageRenderingEngine.PARAM_RESIZE_WIDTH, 42);
        return result;
    }
}