org.etudes.component.app.melete.MeleteSecurityServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.etudes.component.app.melete.MeleteSecurityServiceImpl.java

Source

/**********************************************************************************
 *
 * $URL: https://source.sakaiproject.org/contrib/etudes/melete/tags/2.8.2/melete-impl/src/java/org/etudes/component/app/melete/MeleteSecurityServiceImpl.java $
 * $Id: MeleteSecurityServiceImpl.java 69897 2010-08-24 22:44:41Z mallika@etudes.org $
 ***********************************************************************************
 *
 * Copyright (c) 2008, 2009 Etudes, Inc.
 *
 * Portions completed before September 1, 2008 Copyright (c) 2004, 2005, 2006, 2007, 2008 Foothill College, ETUDES Project
 *
 * Licensed 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.etudes.component.app.melete;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;

import org.etudes.api.app.melete.MeleteExportService;
import org.etudes.api.app.melete.MeleteSecurityService;
import org.etudes.api.app.melete.ModuleService;
import org.etudes.api.app.melete.MeleteImportfromSiteService;
import org.etudes.api.app.melete.MeleteImportService;
import org.sakaiproject.authz.cover.FunctionManager;
import org.sakaiproject.tool.cover.ToolManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Attribute;
import org.dom4j.DocumentHelper;
import org.dom4j.Namespace;
import org.dom4j.QName;
import org.sakaiproject.authz.api.SecurityAdvisor;
import org.sakaiproject.authz.cover.SecurityService;
import org.sakaiproject.authz.cover.AuthzGroupService;
import org.sakaiproject.content.api.ContentHostingService;
import org.sakaiproject.content.api.ContentResource;
import org.sakaiproject.entity.api.Entity;
import org.sakaiproject.entity.api.EntityAccessOverloadException;
import org.sakaiproject.entity.api.EntityCopyrightException;
import org.sakaiproject.entity.cover.EntityManager;
import org.sakaiproject.entity.api.EntityNotDefinedException;
import org.sakaiproject.entity.api.EntityPermissionException;
import org.sakaiproject.entity.api.EntityProducer;
import org.sakaiproject.entity.api.EntityTransferrer;
import org.sakaiproject.entity.api.HttpAccess;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.tool.cover.SessionManager;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.util.StringUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.DOMException;
import org.w3c.dom.Node;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.util.Xml;
import org.imsglobal.basiclti.BasicLTIUtil;
import org.etudes.simpleti.SakaiSimpleLTI;
import org.etudes.basiclti.SakaiBLTIUtil;
import org.sakaiproject.thread_local.api.ThreadLocalManager;
import org.sakaiproject.util.ResourceLoader;

/*
 * MeleteSecurityService is the implementation of MeleteSecurityService
 * that provides the access permissions to the melete
 *
 * @author Foot hill college
 * @version $Revision: 69897 $
 * Mallika - 5/15/07 - Adding code to enable import from site
 */
public class MeleteSecurityServiceImpl implements MeleteSecurityService, EntityProducer, EntityTransferrer {

    private ModuleService moduleService;
    private MeleteImportfromSiteService meleteImportfromSiteService;
    private MeleteImportService meleteImportService;
    private MeleteExportService meleteExportService;

    // Keep both of these here for upwards compatibility - Chuck
    public static final String MIME_TYPE_SLTI = "ims/simplelti";
    public static final String MIME_TYPE_BLTI = "ims/basiclti";

    private static ResourceLoader rb = new ResourceLoader("security_svc");

    // Note: security needs a proper Resource reference

    /*******************************************************************************
    * Dependencies and their setter methods
    *******************************************************************************/

    /** Dependency: a logger component. */
    private Log logger = LogFactory.getLog(MeleteSecurityServiceImpl.class);
    private ThreadLocalManager threadLocalManager = org.sakaiproject.thread_local.cover.ThreadLocalManager
            .getInstance();

    /**
        * Setup a security advisor.
        */
    public void pushAdvisor() {
        // setup a security advisor
        SecurityService.pushAdvisor(new SecurityAdvisor() {
            public SecurityAdvice isAllowed(String userId, String function, String reference) {
                return SecurityAdvice.ALLOWED;
            }
        });
    }

    /**
     * Remove our security advisor.
     */
    public void popAdvisor() {
        SecurityService.popAdvisor();
    }

    /**
     * Check security for this entity.
     *
     * @param ref
     *        The Reference to the entity.
     * @return true if allowed, false if not.
     */
    protected boolean checkSecurity(Reference ref) {

        //Need to add additional code here to make sure the section corresponding to the
        //resource is visible, not deleted or inactivated
        boolean result = false;
        try {
            result = allowAuthor(ref.getContext()) || allowStudent(ref.getContext());
        } catch (Exception e) {
            logger.error(e.toString());
        }
        return result;
    }

    /*******************************************************************************
    * Init and Destroy
    *******************************************************************************/

    /**
     * Final initialization, once all dependencies are set.
     */
    public void init() {

        logger.info(this + ".init()");
        // setup a security advisor
        pushAdvisor();

        try {
            // register as an entity producer
            EntityManager.registerEntityProducer(this, REFERENCE_ROOT);

            //register melete functions
            FunctionManager.registerFunction(SECURE_AUTHOR);
            FunctionManager.registerFunction(SECURE_STUDENT);

        } catch (Throwable t) {
            logger.warn("init(): ", t);
        }

        finally {
            // clear the security advisor
            popAdvisor();
        }
    }

    /**
     * Final cleanup.
     */
    public void destroy() {
        logger.info(this + ".destroy()");
    }

    /**
     *
     */
    public MeleteSecurityServiceImpl() {
        super();

    }

    /**
     * {@inheritDoc}
     */
    public boolean allowAuthor(String reference) throws Exception {

        try {
            return SecurityService.unlock(SECURE_AUTHOR, getContextSiteId(reference));
        } catch (Exception e) {
            throw new Exception(this.getClass().getName() + " : allowAuthor(reference) : " + e.toString());
        }
    }

    public boolean allowStudent(String reference) throws Exception {

        try {
            return SecurityService.unlock(SECURE_STUDENT, getContextSiteId(reference));
        } catch (Exception e) {
            throw new Exception(this.getClass().getName() + " : allowStudent(reference) : " + e.toString());
        }
    }

    public boolean allowAuthor() throws Exception {

        try {
            return SecurityService.unlock(SECURE_AUTHOR,
                    getContextSiteId(ToolManager.getCurrentPlacement().getContext()));
        } catch (Exception e) {
            throw new Exception(this.getClass().getName() + " : allowAuthor() : " + e.toString());
        }
    }

    public boolean allowStudent() throws Exception {

        try {
            return SecurityService.unlock(SECURE_STUDENT,
                    getContextSiteId(ToolManager.getCurrentPlacement().getContext()));
        } catch (Exception e) {
            throw new Exception(this.getClass().getName() + " : allowStudent() : " + e.toString());
        }
    }

    public boolean isSuperUser(String userId) {
        return SecurityService.isSuperUser(userId);
    }

    /**
     * @return siteId
     */
    private String getContextSiteId(String reference) {
        return ("/site/" + reference);
    }

    /*******************************************************************************************************************************
     * EntityProducer
     ******************************************************************************************************************************/

    /**
     * {@inheritDoc}
     */
    public boolean parseEntityReference(String reference, Reference ref) {
        if (reference.startsWith(REFERENCE_ROOT)) {
            // we will get null, sampleAccess, content, private, sampleAccess, <context>, test.txt
            // we will store the context, and the ContentHosting reference in our id field.
            String id = null;
            String context = null;
            String[] parts = StringUtil.split(reference, Entity.SEPARATOR);

            if (parts.length > 5) {
                context = parts[5];
                //Should the slashes below be entityseparator
                id = "/" + StringUtil.unsplit(parts, 2, parts.length - 2, "/");
            }

            ref.set(APPLICATION_ID, null, id, null, context);

            return true;
        }

        return false;
    }

    /**
     * {@inheritDoc}
     */
    public HttpAccess getHttpAccess() {
        return new HttpAccess() {
            public void handleAccess(HttpServletRequest req, HttpServletResponse res, Reference ref,
                    Collection copyrightAcceptedRefs) throws EntityPermissionException, EntityNotDefinedException,
                    EntityAccessOverloadException, EntityCopyrightException {
                // decide on security
                if (!checkSecurity(ref)) {
                    throw new EntityPermissionException(SessionManager.getCurrentSessionUserId(), "meleteDocs",
                            ref.getReference());
                }

                boolean handled = false;
                // Find the site we are coming from
                String contextId = ref.getContext();
                // isolate the ContentHosting reference
                Reference contentHostingRef = EntityManager.newReference(ref.getId());

                // setup a security advisor
                pushAdvisor();
                try {
                    // make sure we have a valid ContentHosting reference with an entity producer we can talk to
                    EntityProducer service = contentHostingRef.getEntityProducer();
                    if (service == null)
                        throw new EntityNotDefinedException(ref.getReference());

                    if (service instanceof ContentHostingService) {
                        ContentHostingService chService = (ContentHostingService) service;
                        try {
                            ContentResource content = chService.getResource(contentHostingRef.getId());
                            if (MIME_TYPE_SLTI.equals(content.getContentType())
                                    || MIME_TYPE_BLTI.equals(content.getContentType())) {
                                byte[] bytes = content.getContent();
                                ResourceProperties resprops = content.getProperties();
                                String str = new String(bytes);
                                String postData = null;
                                if (BasicLTIUtil.validateDescriptor(str) != null) {
                                    try {
                                        popAdvisor();
                                        // Leave ResourceBundle off for now
                                        String[] retval = SakaiBLTIUtil.postLaunchHTML(str, contextId, ref.getId(),
                                                resprops, rb);
                                        if (retval != null)
                                            postData = retval[0];
                                    } catch (Exception e) {
                                        logger.info("Exception e " + e.getMessage());
                                        e.printStackTrace();
                                    } finally {
                                        pushAdvisor();
                                    }
                                } else // Attempt SimpleLTI
                                {
                                    Properties props = null;
                                    // We must remove our advisor while we do the launch and then put it back
                                    // Otherwise the launch will give the user too much power
                                    try {
                                        popAdvisor();
                                        props = SakaiSimpleLTI.doLaunch(str, ref.getContext(), ref.getId());
                                    } catch (Exception e) {
                                        logger.info("Exception e " + e.getMessage());
                                        e.printStackTrace();
                                    } finally {
                                        pushAdvisor();
                                    }
                                    if (props != null) {
                                        postData = props.getProperty("htmltext");
                                    }
                                }

                                if (postData == null) {
                                    String msg = rb.getString("not.configured", "Not configured.");
                                    postData = "<p>" + msg + "</p>\n<!--\n" + str + "-->\n";
                                }

                                if (postData != null) {
                                    res.setContentType("text/html");
                                    ServletOutputStream out = res.getOutputStream();
                                    out.println(postData);
                                    handled = true;
                                }

                            }
                        } catch (Exception e) {
                            //   logger.info("Exception e "+e.getMessage());
                            //   e.printStackTrace();
                        }
                    }
                    if (!handled) {
                        // get the producer's HttpAccess helper, it might not support one
                        HttpAccess access = service.getHttpAccess();
                        if (access == null)
                            throw new EntityNotDefinedException(ref.getReference());

                        // let the helper do the work
                        access.handleAccess(req, res, contentHostingRef, copyrightAcceptedRefs);
                    }
                } finally {
                    // clear the security advisor
                    popAdvisor();
                }
            }
        };
    }

    /**
     * {@inheritDoc}
     */
    public Entity getEntity(Reference ref) {
        // decide on security
        if (!checkSecurity(ref))
            return null;

        // isolate the ContentHosting reference
        Reference contentHostingRef = EntityManager.newReference(ref.getId());

        // setup a security advisor
        pushAdvisor();
        try {
            // make sure we have a valid ContentHosting reference with an entity producer we can talk to
            EntityProducer service = ref.getEntityProducer();
            if (service == null)
                return null;

            // pass on the request
            return service.getEntity(contentHostingRef);
        } finally {
            // clear the security advisor
            popAdvisor();
        }
    }

    /**
     * {@inheritDoc}
     */
    public Collection getEntityAuthzGroups(Reference ref, String userId) {
        // Since we handle security ourself, we won't support anyone else asking
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public String getEntityDescription(Reference ref) {
        // decide on security
        if (!checkSecurity(ref))
            return null;

        // isolate the ContentHosting reference
        Reference contentHostingRef = EntityManager.newReference(ref.getId());

        // setup a security advisor
        pushAdvisor();
        try {
            // make sure we have a valid ContentHosting reference with an entity producer we can talk to
            EntityProducer service = ref.getEntityProducer();
            if (service == null)
                return null;

            // pass on the request
            return service.getEntityDescription(contentHostingRef);
        } finally {
            // clear the security advisor
            popAdvisor();
        }
    }

    /**
     * {@inheritDoc}
     */
    public ResourceProperties getEntityResourceProperties(Reference ref) {
        // decide on security
        if (!checkSecurity(ref))
            return null;

        // isolate the ContentHosting reference
        Reference contentHostingRef = EntityManager.newReference(ref.getId());

        // setup a security advisor
        pushAdvisor();
        try {
            // make sure we have a valid ContentHosting reference with an entity producer we can talk to
            EntityProducer service = ref.getEntityProducer();
            if (service == null)
                return null;

            // pass on the request
            return service.getEntityResourceProperties(contentHostingRef);
        } finally {
            // clear the security advisor
            popAdvisor();
        }
    }

    /**
     * {@inheritDoc}
     */
    public String getEntityUrl(Reference ref) {
        return ServerConfigurationService.getAccessUrl() + ref.getReference();
    }

    /**
     * {@inheritDoc}
     */
    public String getLabel() {
        return "melete";
    }

    /**
     * {@inheritDoc}
     */
    public String merge(String siteId, Element root, String archivePath, String fromSiteId, Map attachmentNames,
            Map userIdTrans, Set userListAllowImport) {
        logger.debug("merge of melete" + siteId + "," + fromSiteId + "," + root.toString());
        int count = 0;
        try {
            org.w3c.dom.Document w3doc = Xml.createDocument();
            org.w3c.dom.Element w3root = (org.w3c.dom.Element) w3doc.importNode(root, true);
            w3doc.appendChild(w3root);

            //convert to dom4j doc
            org.dom4j.io.DOMReader domReader = new org.dom4j.io.DOMReader();
            org.dom4j.Document domDoc = domReader.read(w3doc);
            logger.debug("archive str " + archivePath + archivePath.lastIndexOf(File.separator));
            archivePath = archivePath.substring(0, archivePath.lastIndexOf("/"));
            count = getMeleteImportService().mergeAndBuildModules(domDoc, archivePath, siteId);
        } catch (Exception e) {
            e.printStackTrace();
            return "error on merging modules content";
        }
        return "merging modules content: (" + count + ") modules \n";
    }

    private String createdom4jtree(org.dom4j.Element oneelement) {
        org.dom4j.Document document4jmelete = DocumentHelper.createDocument();
        org.dom4j.Element document4jmeleteRoot = document4jmelete.getRootElement();
        org.dom4j.Element organizationNewElement = oneelement.createCopy();
        organizationNewElement.setParent(document4jmeleteRoot);
        document4jmelete.add(organizationNewElement);
        return document4jmelete.asXML();
    }

    /**
     * {@inheritDoc}
     */
    public String archive(String siteId, Document doc, Stack stack, String archivePath, List attachments) {
        logger.debug("siteid as arg in archive function is " + siteId);
        int count = 0;
        try {
            Element modulesElement = doc.createElement(MeleteSecurityService.class.getName());

            if (siteId != null && siteId.length() > 0) {
                List<Module> selectList = getModuleService().getModules(siteId);
                count = selectList.size();
                File basePackDir = new File(archivePath);
                List orgResElements = getMeleteExportService().generateOrganizationResourceItems(selectList, true,
                        basePackDir, SiteService.getSite(siteId).getTitle(), siteId);

                if (orgResElements != null && orgResElements.size() > 0) {

                    String xmlstr = createdom4jtree((org.dom4j.Element) (org.dom4j.Element) orgResElements.get(0));
                    // read organizations 4j document as w3c document
                    org.w3c.dom.Document meletew3cDocument = Xml.readDocumentFromString(xmlstr);
                    org.w3c.dom.Element meletew3cElement = (org.w3c.dom.Element) meletew3cDocument.getFirstChild();
                    org.w3c.dom.Element meletew3cNewElement = (org.w3c.dom.Element) ((Element) stack.peek())
                            .getOwnerDocument().importNode(meletew3cElement, true);
                    modulesElement.appendChild(meletew3cNewElement);

                    // now resources document
                    xmlstr = createdom4jtree((org.dom4j.Element) (org.dom4j.Element) orgResElements.get(1));

                    org.w3c.dom.Document meletew3cResDocument = Xml.readDocumentFromString(xmlstr);
                    org.w3c.dom.Element meletew3cElement1 = (org.w3c.dom.Element) meletew3cResDocument
                            .getFirstChild();
                    org.w3c.dom.Element meletew3cNewElement1 = (org.w3c.dom.Element) ((Element) stack.peek())
                            .getOwnerDocument().importNode(meletew3cElement1, true);
                    modulesElement.appendChild(meletew3cNewElement1);

                    ((Element) stack.peek()).appendChild(modulesElement);
                    stack.push(modulesElement);
                }
            }
            //      stack.pop();
        } catch (IdUnusedException iue) {
            logger.debug("error in melete during site archive");
            return "error archiving modules";
        } catch (Exception ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("error in melete during site archive" + ex.toString());
                ex.printStackTrace();
            }
            return "error archiving modules";
        }
        return "archiving modules: (" + count + ") modules archived successfully. \n";
    }

    public Set<String> getUsersIsAllowed(String context) {
        // check the cache
        String key = SECURE_STUDENT + "@" + context;

        // form the azGroups for a context-as-implemented-by-site
        Collection azGroups = new Vector(2);
        azGroups.add(SiteService.siteReference(context));
        azGroups.add("!site.helper");

        // get the user ids who can
        Set userIds = AuthzGroupService.getUsersIsAllowed(SECURE_STUDENT, azGroups);
        return userIds;
    }

    /**
     * {@inheritDoc}
     */
    public boolean willArchiveMerge() {
        return true;
    }

    public String[] myToolIds() {
        String[] toolIds = { "sakai.melete" };
        return toolIds;
    }

    public void transferCopyEntities(String fromContext, String toContext, List ids, boolean cleanup) {
        transferCopyEntities(fromContext, toContext, ids);
    }

    public void transferCopyEntities(String fromContext, String toContext, List ids) {
        try {
            logger.debug("transer copy Melete items by transferCopyEntities");
            Set<String> importResources = new HashSet<String>();
            Set<String> addNowResources = new HashSet<String>();
            threadLocalManager.set("MELETE_importResources", importResources);
            threadLocalManager.set("MELETE_addedNowResource", addNowResources);

            getMeleteImportfromSiteService().copyModules(fromContext, toContext);
            logger.debug("importResources: End importing melete data");
        } catch (Exception e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
        }
    }

    public ModuleService getModuleService() {
        return moduleService;
    }

    public void setModuleService(ModuleService moduleService) {
        this.moduleService = moduleService;
    }

    public MeleteImportfromSiteService getMeleteImportfromSiteService() {
        return meleteImportfromSiteService;
    }

    public void setMeleteImportfromSiteService(MeleteImportfromSiteService meleteImportfromSiteService) {
        this.meleteImportfromSiteService = meleteImportfromSiteService;
    }

    /**
     * @return the meleteExportService
     */
    public MeleteExportService getMeleteExportService() {
        return this.meleteExportService;
    }

    /**
     * @param meleteExportService the meleteExportService to set
     */
    public void setMeleteExportService(MeleteExportService meleteExportService) {
        this.meleteExportService = meleteExportService;
    }

    public MeleteImportService getMeleteImportService() {
        return meleteImportService;
    }

    public void setMeleteImportService(MeleteImportService meleteImportService) {
        this.meleteImportService = meleteImportService;
    }
}