/*
 * Copyright (C) 2005 Alfresco, Inc.
 *
 * Licensed under the Mozilla Public License version 1.1 
 * with a permitted attribution clause. You may obtain a
 * copy of the License at
 *
 *   http://www.alfresco.org/legal/license.txt
 *
 * 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.alfresco.repo.audit;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.alfresco.model.ContentModel;
import org.alfresco.repo.audit.model.AuditEntry;
import org.alfresco.repo.audit.model.TrueFalseUnset;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.MutableAuthenticationDao;
import org.alfresco.repo.security.permissions.PermissionServiceSPI;
import org.alfresco.repo.security.permissions.impl.ModelDAO;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.audit.AuditInfo;
import org.alfresco.service.cmr.audit.AuditService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AuthenticationService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest;

public class AuditServiceTest extends BaseSpringTest
{

    private NodeService nodeService;

    private DictionaryService dictionaryService;

    private PermissionServiceSPI permissionService;

    private NamespacePrefixResolver namespacePrefixResolver;

    private AuthenticationService authenticationService;

    private AuthenticationComponent authenticationComponent;

    private ServiceRegistry serviceRegistry;

    private ModelDAO permissionModelDAO;

    private PersonService personService;

    private AuthorityService authorityService;

    private MutableAuthenticationDao authenticationDAO;

    private NodeRef rootNodeRef;

    private NodeRef systemNodeRef;

    private AuditService auditService;

    private AuditEntry auditEntry;

    private NodeRef typesNodeRef;

    private QName children;

    private QName system;

    private QName container;

    private QName types;

    public AuditServiceTest()
    {
        super();
    }

    protected void onSetUpInTransaction() throws Exception
    {
        nodeService = (NodeService) applicationContext.getBean("nodeService");
        dictionaryService = (DictionaryService) applicationContext.getBean(ServiceRegistry.DICTIONARY_SERVICE
                .getLocalName());
        permissionService = (PermissionServiceSPI) applicationContext.getBean("permissionService");
        namespacePrefixResolver = (NamespacePrefixResolver) applicationContext
                .getBean(ServiceRegistry.NAMESPACE_SERVICE.getLocalName());
        authenticationService = (AuthenticationService) applicationContext.getBean("authenticationService");
        authenticationComponent = (AuthenticationComponent) applicationContext.getBean("authenticationComponent");
        serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY);
        permissionModelDAO = (ModelDAO) applicationContext.getBean("permissionsModelDAO");
        personService = (PersonService) applicationContext.getBean("personService");
        authorityService = (AuthorityService) applicationContext.getBean("authorityService");

        authenticationComponent.setCurrentUser(authenticationComponent.getSystemUserName());
        authenticationDAO = (MutableAuthenticationDao) applicationContext.getBean("alfDaoImpl");

        auditService = (AuditService) applicationContext.getBean("AuditService");
        auditEntry = (AuditEntry) applicationContext.getBean("auditModel");

        StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.nanoTime());
        rootNodeRef = nodeService.getRootNode(storeRef);

        children = ContentModel.ASSOC_CHILDREN;
        system = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "system");
        container = ContentModel.TYPE_CONTAINER;
        types = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "people");

        systemNodeRef = nodeService.createNode(rootNodeRef, children, system, container).getChildRef();
        typesNodeRef = nodeService.createNode(systemNodeRef, children, types, container).getChildRef();
        Map<QName, Serializable> props = createPersonProperties("andy");
        nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, props).getChildRef();
        props = createPersonProperties("lemur");
        nodeService.createNode(typesNodeRef, children, ContentModel.TYPE_PERSON, container, props).getChildRef();

        // create an authentication object e.g. the user
        if (authenticationDAO.userExists("andy"))
        {
            authenticationService.deleteAuthentication("andy");
        }
        authenticationService.createAuthentication("andy", "andy".toCharArray());

        if (authenticationDAO.userExists("lemur"))
        {
            authenticationService.deleteAuthentication("lemur");
        }
        authenticationService.createAuthentication("lemur", "lemur".toCharArray());

        if (authenticationDAO.userExists("admin"))
        {
            authenticationService.deleteAuthentication("admin");
        }
        authenticationService.createAuthentication("admin", "admin".toCharArray());

        authenticationComponent.clearCurrentSecurityContext();
    }

    public void testApplicationAudit()
    {
        AuthenticationUtil.setSystemUserAsCurrentUser();
        try
        {

            NodeRef nodeRef = new NodeRef(new StoreRef("test", "audit"), "id");
            int start = auditService.getAuditTrail(nodeRef).size();
            int increment = auditEntry.getEnabled() == TrueFalseUnset.TRUE ? 1 : 0;
            auditService.audit("AuditedApp", "First");
            assertEquals(start, auditService.getAuditTrail(nodeRef).size());
            auditService.audit("AuditedApp", "Second", nodeRef);
            assertEquals(start + (1 * increment), auditService.getAuditTrail(nodeRef).size());
            auditService.audit("AuditedApp", "Third", new Object[] { "one", "two", "three" });
            assertEquals(start + (1 * increment), auditService.getAuditTrail(nodeRef).size());
            auditService.audit("AuditedApp", "Fourth", nodeRef, new Object[] { "one", "two", "three" });
            assertEquals(start + (2 * increment), auditService.getAuditTrail(nodeRef).size());
            auditService.audit("UnAuditedApp", "First");
            assertEquals(start + (2 * increment), auditService.getAuditTrail(nodeRef).size());
            auditService.audit("UnAuditedApp", "Second", nodeRef);
            assertEquals(start + (3 * increment), auditService.getAuditTrail(nodeRef).size());
            auditService.audit("UnAuditedApp", "Third", new Object[] { "one", "two", "three" });
            assertEquals(start + (3 * increment), auditService.getAuditTrail(nodeRef).size());
            auditService.audit("UnAuditedApp", "Fourth", nodeRef, new Object[] { "one", "two", "three" });
            assertEquals(start + (4 * increment), auditService.getAuditTrail(nodeRef).size());
        }
        finally
        {
            AuthenticationUtil.clearCurrentSecurityContext();
        }
    }

    public void testNodeServiceAudit()
    {
        AuthenticationUtil.setSystemUserAsCurrentUser();
        try
        {
            int start = auditService.getAuditTrail(typesNodeRef).size();
            int increment = auditEntry.getEnabled() == TrueFalseUnset.TRUE ? 1 : 0;

            // Create

            Map<QName, Serializable> props = createPersonProperties("woof");
            NodeRef created = serviceRegistry.getNodeService().createNode(typesNodeRef, children,
                    ContentModel.TYPE_PERSON, container, props).getChildRef();
            assertEquals(start + (1 * increment), auditService.getAuditTrail(typesNodeRef).size());
            List<AuditInfo> list = auditService.getAuditTrail(typesNodeRef);
            assertEquals((1 * increment), auditService.getAuditTrail(created).size());

            // Update

            serviceRegistry.getNodeService().setProperty(created, ContentModel.PROP_FIRSTNAME, "New First Name");
            assertEquals((2 * increment), auditService.getAuditTrail(created).size());

            // Update

            serviceRegistry.getNodeService().setProperty(created, ContentModel.PROP_FIRSTNAME, "Next First Name");
            assertEquals((3 * increment), auditService.getAuditTrail(created).size());

            // Delete

            serviceRegistry.getNodeService().deleteNode(created);
            assertEquals((4 * increment), auditService.getAuditTrail(created).size());

            list = auditService.getAuditTrail(created);
            assertNotNull(list);
        }
        finally
        {
            AuthenticationUtil.clearCurrentSecurityContext();
        }
    }

    public void xtestCreateStore()
    {

        AuthenticationUtil.setSystemUserAsCurrentUser();
        try
        {
            serviceRegistry.getNodeService()
                    .createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_Audit_" + System.nanoTime());
            // Should have a query to support this - check direct in the DB
        }
        finally
        {
            AuthenticationUtil.clearCurrentSecurityContext();
        }
        setComplete();
    }

    public void xtestAuthenticartionDoesNotReportPasswords()
    {
        // Should have a query to support this - check direct in the DB
        AuthenticationUtil.setSystemUserAsCurrentUser();
        try
        {
            serviceRegistry.getAuthenticationService().createAuthentication("cabbage", "cabbage".toCharArray());
            serviceRegistry.getAuthenticationService().updateAuthentication("cabbage", "cabbage".toCharArray(),
                    "red".toCharArray());
        }
        finally
        {
            AuthenticationUtil.clearCurrentSecurityContext();
        }

        try
        {
            serviceRegistry.getAuthenticationService().authenticate("cabbage", "red".toCharArray());
        }
        finally
        {
            serviceRegistry.getAuthenticationService().clearCurrentSecurityContext();
        }
        setComplete();
    }

    public void xtestAuthenticartionFailure()
    {
        // Should have a query to support this - check direct in the DB
        AuthenticationUtil.setSystemUserAsCurrentUser();

        serviceRegistry.getAuthenticationService().createAuthentication("woof", "cabbage".toCharArray());
        serviceRegistry.getAuthenticationService().authenticate("woof", "red".toCharArray());

    }

    public void testThereIsAnAuditService()
    {
        assertNotNull(serviceRegistry.getAuditService());
    }

    private Map<QName, Serializable> createPersonProperties(String userName)
    {
        HashMap<QName, Serializable> properties = new HashMap<QName, Serializable>();
        properties.put(ContentModel.PROP_USERNAME, userName);
        return properties;
    }

}