com.edgenius.wiki.security.service.SecurityServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.edgenius.wiki.security.service.SecurityServiceImpl.java

Source

/* 
 * =============================================================
 * Copyright (C) 2007-2011 Edgenius (http://www.edgenius.com)
 * =============================================================
 * License Information: http://www.edgenius.com/licensing/edgenius/2.0/
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2.0
 * as published by the Free Software Foundation.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 * http://www.gnu.org/licenses/gpl.txt
 *  
 * ****************************************************************
 */
package com.edgenius.wiki.security.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.edgenius.core.SecurityValues;
import com.edgenius.core.SecurityValues.OPERATIONS;
import com.edgenius.core.SecurityValues.RESOURCE_TYPES;
import com.edgenius.core.SecurityValues.SYSTEM_ROLES;
import com.edgenius.core.dao.PermissionDAO;
import com.edgenius.core.dao.ResourceDAO;
import com.edgenius.core.dao.RoleDAO;
import com.edgenius.core.model.Permission;
import com.edgenius.core.model.Resource;
import com.edgenius.core.model.Role;
import com.edgenius.core.model.User;
import com.edgenius.core.service.UserReadingService;
import com.edgenius.core.util.AuditLogger;
import com.edgenius.wiki.dao.PageDAO;
import com.edgenius.wiki.dao.SpaceDAO;
import com.edgenius.wiki.gwt.client.server.utils.SharedConstants;
import com.edgenius.wiki.model.AbstractPage;
import com.edgenius.wiki.model.Draft;
import com.edgenius.wiki.model.Page;
import com.edgenius.wiki.model.Space;
import com.edgenius.wiki.model.Widget;
import com.edgenius.wiki.security.Policy;
import com.edgenius.wiki.security.WikiSecurityValues.WikiOPERATIONS;
import com.edgenius.wiki.security.strategy.PatternStrategy;
import com.edgenius.wiki.util.ProxyLoginUtil;

/**
 * @author Dapeng.Ni
 */
@Transactional
public class SecurityServiceImpl implements SecurityService, InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(SecurityServiceImpl.class);
    //
    private static final String PRIVATE_SPACE_PREFIX = "private_";
    private static final String ANONYMOUS_KEY = "changeThis";
    private static final String ANONYMOUS_PASSWORD = "anonymousUser";

    private PatternStrategy patternStrategy;
    private ProviderManager authenticationManager;

    private boolean initialSpaceReadingCacheAtStart;
    private RoleDAO roleDAO;
    private UserReadingService userReadingService;
    private PageDAO pageDAO;
    private SpaceDAO spaceDAO;
    private ResourceDAO resourceDAO;
    private PermissionDAO permissionDAO;

    private Cache spaceReadingCache;
    private Cache pageReadingCache;

    //********************************************************************
    //               Implement SecurityService methods
    //********************************************************************
    public Policy findUrlPolicy(String url) {
        WikiOPERATIONS wikiType = patternStrategy.findURLRuntimePattern(url);

        //TODO: so far only support INSTANCE/SPACE scope url matching
        if (wikiType != null) {
            List<Policy> policies = null;
            if (wikiType.type == RESOURCE_TYPES.INSTANCE) {
                policies = patternStrategy.getPolicies(RESOURCE_TYPES.INSTANCE);

            } else if (wikiType.type == RESOURCE_TYPES.SPACE) {
                policies = patternStrategy.getPolicies(RESOURCE_TYPES.SPACE,
                        wikiType.values.get(RESOURCE_TYPES.SPACE));
            }
            //get all resources in given RESOURCE_TYPES and OPERATION
            //now, need check policy in cache to find out if this type/operation policy exist. 
            //For example, it may not exist if page permission is not defined.
            if (policies != null) {
                for (Policy policy : policies) {
                    //match: resource Type, name and operation
                    if (policy.getOperation() == wikiType.operation && policy.getType() == wikiType.type) {
                        return policy;
                    }
                }
            }
        }

        return null;

    }

    public Policy findBeforeMethodPolicy(String clz, String method, Object[] args) {
        return findMethodPolicy(clz, method, args, PatternStrategy.BEFORE_METHOD);
    }

    public Policy findAfterMethodPolicy(String clz, String method, Object returnedObject) {
        return findMethodPolicy(clz, method, new Object[] { returnedObject }, PatternStrategy.AFTER_METHOD);
    }

    public void fillPageWikiOperations(User user, AbstractPage page) {
        String spaceUname = page.getSpace().getUnixName();

        String pageUuid = page.getPageUuid();

        //this will return space and also page policy in this space
        List<Policy> policyList = patternStrategy.getPolicies(RESOURCE_TYPES.SPACE, spaceUname);
        List<String> roleUserList = getRoleUserNameList(user);

        //page policy prior to space's,e.g., if page policy exist, and not include this user. Then this user won't have this policy's operaion
        //even this page's space give user this policy. 
        int[] p1 = new int[10];
        int[] s1 = new int[10];
        for (Policy policy : policyList) {
            if (RESOURCE_TYPES.PAGE.equals(policy.getType())) {
                if (StringUtils.equals(policy.getResourceName(), pageUuid)) {
                    //it is this page's policy, need check if this policy allow current user
                    //for page policy, if this policy does not contain user, means not allow!!! 
                    operMatrix(p1, roleUserList, policy);
                }
            } else if (RESOURCE_TYPES.SPACE.equals(policy.getType())) {
                if (StringUtils.equals(policy.getResourceName(), spaceUname)) {
                    operMatrix(s1, roleUserList, policy);
                }
            }
        }

        //get page permission according to new page, draft or existed page
        List<WikiOPERATIONS> wikiOpers = new ArrayList<WikiOPERATIONS>();
        if (page instanceof Draft) {
            //TODO does it need special configure? it will confuse if this page has exist page and draft 
        } else {
            //         exist page/create new page permission
            //get all policies for this page for current user, 
            if ((p1[OPERATIONS.READ.ordinal()] == 0 && s1[OPERATIONS.READ.ordinal()] == 1)
                    || p1[OPERATIONS.READ.ordinal()] == 1) {
                wikiOpers.add(WikiOPERATIONS.PAGE_READ);
            }
            if ((p1[OPERATIONS.WRITE.ordinal()] == 0 && s1[OPERATIONS.WRITE.ordinal()] == 1)
                    || p1[OPERATIONS.WRITE.ordinal()] == 1) {
                wikiOpers.add(WikiOPERATIONS.PAGE_WRITE);
            }
            if ((p1[OPERATIONS.REMOVE.ordinal()] == 0 && s1[OPERATIONS.REMOVE.ordinal()] == 1)
                    || p1[OPERATIONS.REMOVE.ordinal()] == 1) {
                wikiOpers.add(WikiOPERATIONS.PAGE_REMOVE);
            }
            if ((p1[OPERATIONS.COMMENT_READ.ordinal()] == 0 && s1[OPERATIONS.COMMENT_READ.ordinal()] == 1)
                    || p1[OPERATIONS.COMMENT_READ.ordinal()] == 1) {
                wikiOpers.add(WikiOPERATIONS.PAGE_COMMENT_READ);
            }
            if ((p1[OPERATIONS.COMMENT_WRITE.ordinal()] == 0 && s1[OPERATIONS.COMMENT_WRITE.ordinal()] == 1)
                    || p1[OPERATIONS.COMMENT_WRITE.ordinal()] == 1) {
                wikiOpers.add(WikiOPERATIONS.PAGE_COMMENT_WRITE);
            }
            if ((p1[OPERATIONS.OFFLINE.ordinal()] == 0 && s1[OPERATIONS.OFFLINE.ordinal()] == 1)
                    || p1[OPERATIONS.COMMENT_WRITE.ordinal()] == 1) {
                wikiOpers.add(WikiOPERATIONS.PAGE_OFFLINE);
            }
            //space level page permission:
            if (s1[OPERATIONS.RESTRICT.ordinal()] == 1)
                wikiOpers.add(WikiOPERATIONS.SPACE_PAGE_RESTRICT);
            if (s1[OPERATIONS.ADMIN.ordinal()] == 1)
                wikiOpers.add(WikiOPERATIONS.SPACE_ADMIN);
            if (s1[OPERATIONS.EXPORT.ordinal()] == 1)
                wikiOpers.add(WikiOPERATIONS.SPACE_EXPORT);
        }

        page.setWikiOperations(wikiOpers);
    }

    public void fillSpaceWikiOperations(User user, Space space) {
        List<String> roleUserList = getRoleUserNameList(user);
        String spaceUname = space.getUnixName();
        List<Policy> policyList = patternStrategy.getPolicies(RESOURCE_TYPES.SPACE, spaceUname);
        int[] s1 = new int[10];
        for (Policy policy : policyList) {
            if (RESOURCE_TYPES.SPACE.equals(policy.getType())
                    && StringUtils.equals(policy.getResourceName(), spaceUname)) {
                operMatrix(s1, roleUserList, policy);
            }
        }
        //get all policies for this page for current user, 
        List<WikiOPERATIONS> wikiOpers = new ArrayList<WikiOPERATIONS>();
        if (s1[OPERATIONS.READ.ordinal()] == 1)
            wikiOpers.add(WikiOPERATIONS.SPACE_PAGE_READ);
        if (s1[OPERATIONS.WRITE.ordinal()] == 1)
            wikiOpers.add(WikiOPERATIONS.SPACE_PAGE_WRITE);
        if (s1[OPERATIONS.REMOVE.ordinal()] == 1)
            wikiOpers.add(WikiOPERATIONS.SPACE_PAGE_REMOVE);
        if (s1[OPERATIONS.COMMENT_READ.ordinal()] == 1)
            wikiOpers.add(WikiOPERATIONS.SPACE_COMMENT_READ);
        if (s1[OPERATIONS.COMMENT_WRITE.ordinal()] == 1)
            wikiOpers.add(WikiOPERATIONS.SPACE_COMMENT_WRITE);
        if (s1[OPERATIONS.RESTRICT.ordinal()] == 1)
            wikiOpers.add(WikiOPERATIONS.SPACE_PAGE_RESTRICT);
        if (s1[OPERATIONS.EXPORT.ordinal()] == 1)
            wikiOpers.add(WikiOPERATIONS.SPACE_EXPORT);
        if (s1[OPERATIONS.ADMIN.ordinal()] == 1)
            wikiOpers.add(WikiOPERATIONS.SPACE_ADMIN);
        if (s1[OPERATIONS.OFFLINE.ordinal()] == 1)
            wikiOpers.add(WikiOPERATIONS.SPACE_OFFLINE);

        space.setWikiOperations(wikiOpers);

    }

    //JDK1.6 @Override
    public void initResourcePermission(Widget widget) {
        Role adminRole = roleDAO.getByName(SYSTEM_ROLES.ADMIN.getName());
        Role registerRole = roleDAO.getByName(SYSTEM_ROLES.USERS.getName());
        Role anonymousRole = roleDAO.getByName(SYSTEM_ROLES.ANONYMOUS.getName());
        Set<Role> anonyUsersAdmin = new HashSet<Role>();
        anonyUsersAdmin.add(adminRole);
        anonyUsersAdmin.add(registerRole);
        anonyUsersAdmin.add(anonymousRole);

        Set<Role> adminRoles = new HashSet<Role>();
        adminRoles.add(adminRole);

        //create resource
        Resource resource = new Resource();
        resource.setType(RESOURCE_TYPES.WIDGET);
        resource.setResource(widget.getUuid());

        //create default permission as well
        Set<User> users = new HashSet<User>();
        if (widget.getCreator() != null)
            users.add(widget.getCreator());

        //give space create all permission:
        Permission read = new Permission();
        read.setOperation(OPERATIONS.READ);
        read.setResource(resource);
        //create permission
        read.setUsers(users);
        if (widget.isShared()) { // EVEN disable system admin???
            //give admin/register/anonymous user role "READ" permission
            read.setRoles(anonyUsersAdmin);
        }
        permissionDAO.saveOrUpdate(read);

        Permission write = new Permission();
        write.setOperation(OPERATIONS.WRITE);
        write.setResource(resource);
        write.setUsers(users);
        if (widget.isShared()) { // EVEN disable system admin???
            //      give admin user role "WRITE" permission
            write.setRoles(adminRoles);
        }
        permissionDAO.saveOrUpdate(write);

        //also need add permissions to resource here, although it is not necessary for persist
        //but it is necessay when initCache() when using resource.getPermssions() in PatternFactory
        Set<Permission> permissions = new HashSet<Permission>();
        permissions.add(read);
        permissions.add(write);
        resource.setPermissions(permissions);
        resourceDAO.saveOrUpdate(resource);
    }

    //JDK1.6 @Override
    public void initResourcePermission(Space space) {
        Role adminRole = roleDAO.getByName(SYSTEM_ROLES.ADMIN.getName());
        Role registerRole = roleDAO.getByName(SYSTEM_ROLES.USERS.getName());
        Role anonymousRole = roleDAO.getByName(SYSTEM_ROLES.ANONYMOUS.getName());
        Set<Role> anonyUsersAdmin = new HashSet<Role>();
        anonyUsersAdmin.add(adminRole);
        anonyUsersAdmin.add(registerRole);
        anonyUsersAdmin.add(anonymousRole);

        Set<Role> regsiterUsersAdmin = new HashSet<Role>();
        regsiterUsersAdmin.add(adminRole);
        regsiterUsersAdmin.add(registerRole);
        Set<Role> adminRoles = new HashSet<Role>();
        adminRoles.add(adminRole);

        //create resource
        Resource resource = new Resource();
        resource.setType(RESOURCE_TYPES.SPACE);
        resource.setResource(space.getUnixName());

        //create default permission as well
        Set<User> users = new HashSet<User>();
        if (space.getCreator() != null)
            users.add(space.getCreator());

        //give space create all permission:
        Permission read = new Permission();
        read.setOperation(OPERATIONS.READ);
        read.setResource(resource);
        //create permission
        read.setUsers(users);
        if (space.isPrivate())
            //private, only admin ROLE has read perm 
            read.setRoles(adminRoles);
        else
            //give admin/register/anonymous user role "READ" permission
            read.setRoles(anonyUsersAdmin);
        permissionDAO.saveOrUpdate(read);

        Permission write = new Permission();
        write.setOperation(OPERATIONS.WRITE);
        write.setResource(resource);
        write.setUsers(users);
        //      give admin user role "WRITE" permission
        write.setRoles(adminRoles);
        permissionDAO.saveOrUpdate(write);

        Permission remove = new Permission();
        remove.setOperation(OPERATIONS.REMOVE);
        remove.setResource(resource);
        remove.setUsers(users);
        //      give admin user role "REMOVE" permission
        remove.setRoles(adminRoles);
        permissionDAO.saveOrUpdate(remove);

        Permission restrict = new Permission();
        restrict.setOperation(OPERATIONS.RESTRICT);
        restrict.setResource(resource);
        restrict.setUsers(users);
        //      give admin user role "REMOVE" permission
        restrict.setRoles(adminRoles);
        permissionDAO.saveOrUpdate(restrict);

        //      Permission export = new Permission();
        //      export.setOperation(OPERATIONS.EXPORT);
        //      export.setResource(resource);
        //      export.setUsers(users);
        //      //give admin/register user role "READ" permission
        //      export.setRoles(regsiterUsersAdmin);
        //      permissionDAO.saveOrUpdate(export);

        Permission admin = new Permission();
        admin.setOperation(OPERATIONS.ADMIN);
        admin.setResource(resource);
        admin.setUsers(users);
        //give admin user role "admin" permission
        admin.setRoles(adminRoles);
        permissionDAO.saveOrUpdate(admin);

        Permission commentRead = new Permission();
        commentRead.setOperation(OPERATIONS.COMMENT_READ);
        commentRead.setResource(resource);
        commentRead.setUsers(users);
        //      give any user role "READ comment" permission
        commentRead.setRoles(anonyUsersAdmin);
        permissionDAO.saveOrUpdate(commentRead);

        Permission commentWrite = new Permission();
        commentWrite.setOperation(OPERATIONS.COMMENT_WRITE);
        commentWrite.setResource(resource);
        commentWrite.setUsers(users);
        //      give any role "WRITE comment" permission
        commentWrite.setRoles(anonyUsersAdmin);
        permissionDAO.saveOrUpdate(commentWrite);

        Permission offline = new Permission();
        offline.setOperation(OPERATIONS.OFFLINE);
        offline.setResource(resource);
        offline.setUsers(users);
        //      give any role "WRITE comment" permission
        offline.setRoles(anonyUsersAdmin);
        permissionDAO.saveOrUpdate(offline);

        //also need add permissions to resource here, although it is not necessary for persist
        //but it is necessay when initCache() when using resource.getPermssions() in PatternFactory
        Set<Permission> permissions = new HashSet<Permission>();
        permissions.add(read);
        permissions.add(write);
        permissions.add(remove);
        permissions.add(restrict);
        //      permissions.add(export);
        permissions.add(admin);
        permissions.add(commentRead);
        permissions.add(commentWrite);
        permissions.add(offline);
        resource.setPermissions(permissions);
        resourceDAO.saveOrUpdate(resource);

    }

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public Resource getResourceByName(String resourceName, RESOURCE_TYPES resourceType) {

        return resourceDAO.getByName(resourceName);
    }

    public Resource saveResource(String resourceName, RESOURCE_TYPES resourceType) {
        Resource resource;
        //create resource
        resource = new Resource();
        resource.setType(resourceType);
        resource.setResource(resourceName);
        resourceDAO.saveOrUpdate(resource);
        return resource;
    }

    public Permission getPermissionByOperationResource(OPERATIONS operation, String resourceName) {
        return permissionDAO.getByOperationResource(operation, resourceName);
    }

    public String getResourceMasks(String resourceName) {
        //currently, only page has masks feature: mask operations that any role/user which added new user in space level 
        Resource res = resourceDAO.getByName(resourceName);
        if (res != null)
            return res.getMasks();
        else
            return "";
    }

    /**
     * This method gets matrix of user/role and operations of specific resource.
     */
    //FIXME: annotation not work!!! in readonly true, any change in permission, will persist
    @Transactional(readOnly = true, propagation = Propagation.REQUIRED)
    public List<Permission> getResourcePermission(RESOURCE_TYPES resourceType, String resourceName) {

        if (!RESOURCE_TYPES.PAGE.equals(resourceType)) {
            List<Policy> policies = patternStrategy.getPolicies(resourceType, resourceName);
            List<Permission> permList = permissionDAO.getByResource(resourceName);
            //         mark dead policy: e.g., space A allow R1 to read, but INSTANCE does not allow R1 read, so, policy on R1 read space A is dead
            for (Policy policy : policies) {
                //find this resource's policies in cache: which already finish "purge", "conflict" handle etc.
                //possible resource are instance and space
                if (resourceType.equals(policy.getType())
                        && StringUtils.equalsIgnoreCase(policy.getResourceName(), resourceName)) {
                    //check this policies role/user if remove from Permission, if so, put such role/user into Permission.deadRoleUserList
                    List<String> nameList = new ArrayList<String>();
                    for (Iterator<ConfigAttribute> iter = policy.getMutableAttributeDefinition().iterator(); iter
                            .hasNext();) {
                        nameList.add(iter.next().getAttribute());
                    }
                    findDeadRoleUser(permList, policy.getOperation(), nameList, false);
                }
            }

            return permList;
        }
        //For page, it inherit permission from its space, so do following
        //1. retrieve policyCache get its space policies,  create a permission object according to this policy
        //2. get page permission from DB(NOT from cache, cache maybe be modified because space READ permission will interfere to other policies!). 
        //3. then, reset user/role permission created on 1 step by page policy, and put any removed user/role into Permission.deadRoleUserList
        // actually, current Permission.deadRoleUserList becomes a liveRoleUserList(live in space level, but dead by page permission)

        List<Permission> pagePermList = new ArrayList<Permission>();
        Resource res = resourceDAO.getByName(resourceName);

        Set<Permission> perms = new HashSet<Permission>();
        //for page resource, resource may not exist if user never set permission for this page
        if (res != null)
            perms = res.getPermissions();

        Page page = pageDAO.getCurrentByUuid(resourceName);
        if (page == null) {
            log.error("Could not find page by uuid:" + resourceName);
            return new ArrayList<Permission>();
        }

        String spaceUname = page.getSpace().getUnixName();
        List<Policy> policies = patternStrategy.getPolicies(RESOURCE_TYPES.SPACE, spaceUname);
        for (Policy policy : policies) {
            if (RESOURCE_TYPES.SPACE.equals(policy.getType())
                    && StringUtils.equalsIgnoreCase(policy.getResourceName(), spaceUname)) {
                //get this page's space policy
                Permission perm = new Permission();
                Set<Role> roles = new HashSet<Role>();
                Set<User> users = new HashSet<User>();
                for (Iterator<ConfigAttribute> iter = policy.getMutableAttributeDefinition().iterator(); iter
                        .hasNext();) {
                    ConfigAttribute att = iter.next();
                    if (att.getAttribute().startsWith(Role.ROLE_PREFIX)) {
                        Role role = roleDAO.getByName(att.getAttribute());
                        roles.add(role);
                    } else {
                        User user = userReadingService
                                .getUserByName(att.getAttribute().substring(Role.USER_PREFIX.length()));
                        if (user != null)
                            users.add(user);
                    }
                }
                perm.setRoles(roles);
                perm.setUsers(users);
                perm.setOperation(policy.getOperation());
                //just create a proxy resource to hold type
                Resource resource = new Resource();
                resource.setType(RESOURCE_TYPES.PAGE);
                perm.setResource(resource);
                //not necessary fields in permission, don't set others

                pagePermList.add(perm);
            }
        }

        for (Permission permission : perms) {
            List<String> nameList = new ArrayList<String>();
            Set<User> users = permission.getUsers();
            if (users != null) {
                for (User user : users) {
                    nameList.add(Role.USER_PREFIX + user.getUsername());
                }
            }
            Set<Role> roles = permission.getRoles();
            if (roles != null) {
                for (Role role : roles) {
                    nameList.add(role.getName());
                }
            }
            findDeadRoleUser(pagePermList, permission.getOperation(), nameList, true);
        }

        return pagePermList;
        //      COMMENTS: don't use database read, it won't check space live conditions

    }

    public void updateResource(Resource res) {
        resourceDAO.saveOrUpdate(res);
    }

    public void saveUpdatePermission(Permission newPerm) {
        permissionDAO.saveOrUpdate(newPerm);

    }

    public void login(String username, String confirmPassword) {
        // log user in automatically
        Authentication auth = new UsernamePasswordAuthenticationToken(username, confirmPassword);
        SecurityContextHolder.getContext().setAuthentication(authenticationManager.authenticate(auth));
    }

    public void resetPolicyCache(RESOURCE_TYPES type, String resourceName) {
        log.info("Resource " + resourceName + " of type " + type.name() + "  policy cache is reset.");
        patternStrategy.resetCache(type, resourceName);

    }

    public void refreshResource(Resource res) {
        resourceDAO.refresh(res);
    }

    public boolean isAllowWidget(OPERATIONS operation, String widgetUuid, User user) {

        List<Policy> policyList = patternStrategy.getPolicies(RESOURCE_TYPES.WIDGET, widgetUuid);
        List<String> readPerm = new ArrayList<String>();
        if (policyList != null) {
            for (Policy policy : policyList) {
                if (policy.getOperation() == operation) {
                    Iterator<ConfigAttribute> iter = policy.getMutableAttributeDefinition().iterator();
                    while (iter.hasNext()) {
                        ConfigAttribute att = iter.next();
                        readPerm.add(att.getAttribute());
                    }
                }
            }
        }
        List<String> roleUserList = getRoleUserNameList(user);
        return isAllow(roleUserList, readPerm);
    }

    public boolean isAllowInstanceReading(User user) {
        if (!user.isEnabled())
            return false;

        List<Policy> policyList = patternStrategy.getPolicies(RESOURCE_TYPES.INSTANCE);
        List<String> readPerm = new ArrayList<String>();
        if (policyList != null) {
            for (Policy policy : policyList) {
                if (policy.getOperation() == OPERATIONS.READ) {
                    Iterator<ConfigAttribute> iter = policy.getMutableAttributeDefinition().iterator();
                    while (iter.hasNext()) {
                        ConfigAttribute att = iter.next();
                        readPerm.add(att.getAttribute());
                    }
                }
            }
        }
        List<String> roleUserList = getRoleUserNameList(user);
        return isAllow(roleUserList, readPerm);
    }

    public boolean isPrivateSpace(String spaceUname) {
        Element perm = getSpacePrivate(spaceUname);
        //just for failure tolerance
        if (perm == null)
            return false;

        Boolean pri = (Boolean) perm.getValue();
        return pri.booleanValue();
    }

    @SuppressWarnings("unchecked")
    public boolean isAllowSpaceReading(String spaceUname, User user) {
        if (isAllowResourceAdmin(SharedConstants.INSTANCE_NAME, SecurityValues.RESOURCE_TYPES.INSTANCE, user)) {
            //quick check, if system admin, always allow
            return true;
        }
        if (!isAllowInstanceReading(user))
            return false;

        Element perm = getSpaceReadPermission(spaceUname);
        //just for failure tolerance
        if (perm == null) {
            if (!SharedConstants.SYSTEM_SPACEUNAME.equals(spaceUname))
                AuditLogger.error("Unexpected case:Unable find space reading cache for space " + spaceUname);
            return false;
        }

        List<String> roleUserList = getRoleUserNameList(user);

        List<String> readPerm = (List<String>) perm.getValue();

        return isAllow(roleUserList, readPerm);

    }

    @SuppressWarnings("unchecked")
    public boolean isAllowPageReading(String spaceUname, String pageUuid, User user) {
        if (isAllowResourceAdmin(SharedConstants.INSTANCE_NAME, SecurityValues.RESOURCE_TYPES.INSTANCE, user)) {
            //quick check, if system admin, always allow
            return true;
        }

        if (!isAllowInstanceReading(user))
            return false;

        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        //here, check spaceScop reading permission, does not user isAllowSpaceReading(), 
        // just for roleUserList will resue in page reading check
        Element spacePerm = getSpaceReadPermission(spaceUname);
        List<String> roleUserList = getRoleUserNameList(user);
        //just for failure tolerance
        if (spacePerm == null) {
            AuditLogger.warn("Unexpected case: Unable to get space " + spaceUname + " reading permission!");
            return false;
        }

        List<String> readPerm = (List<String>) spacePerm.getValue();
        //if the space does not allow reading, then no necessary to check further.
        if (!isAllow(roleUserList, readPerm)) {
            //while for RSS function happens in MQ receiver side, user information is null.
            log.info("Space " + spaceUname + " does not allow " + (user == null ? "anonymous" : user.getUsername())
                    + " reading page " + pageUuid);
            return false;
        }
        //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

        //NOW, check page permission.
        //         Note, page permission is forbidden list. It means only users/roles 
        //         who has NO permission will appear in list. This list will work together with its space permission.
        Element pagePerm = getPageReadPermission(pageUuid);
        //if page no extra permission, then its permission inherit from Space, it should be ALLOW read. 
        if (pagePerm == null || pagePerm.getValue() == null)
            return true;

        List<String> pageForbidPerm = (List<String>) pagePerm.getValue();
        //remove all page forbidden users/roles in space permission, and check again

        //Important: clone readPerm, so that readPerm value does not change, which should be readonly, otherwise, cache value will impact.
        List<String> pageReadPerm = new ArrayList<String>();
        pageReadPerm.addAll(readPerm);
        for (String forbid : pageForbidPerm) {
            pageReadPerm.remove(forbid);
        }

        return isAllow(roleUserList, pageReadPerm);

    }

    public void resetSpacePrivateCache(String spaceUname) {
        log.info("Space " + spaceUname + " private cache is reset.");
        spaceReadingCache.remove(PRIVATE_SPACE_PREFIX + spaceUname);
        getSpacePrivate(spaceUname);
    }

    public void resetSpaceReadingCache(String spaceUname) {
        log.info("Space " + spaceUname + " reading cache is reset.");
        spaceReadingCache.remove(spaceUname);
        getSpaceReadPermission(spaceUname);

    }

    public void removeResource(String resourceName) {

        Resource res = resourceDAO.getByName(resourceName);
        if (res != null) {
            Set<Permission> perList = new HashSet<Permission>();
            Set<Permission> list = res.getPermissions();
            for (Iterator<Permission> iter = list.iterator(); iter.hasNext();) {
                Permission perm = iter.next();
                perm.setResource(null);
                perList.add(perm);
                iter.remove();
            }

            //NOTE: permission will remove by cascade.
            resourceDAO.removeObject(res);

            for (Permission permission : perList) {
                permission.getUsers().clear();
                permission.getRoles().clear();
                permissionDAO.removeObject(permission);
            }

            if (res.getType() == RESOURCE_TYPES.SPACE) {
                //should be spaceUname
                spaceReadingCache.remove(resourceName);
            } else if (res.getType() == RESOURCE_TYPES.PAGE) {
                //should be pageUuid
                pageReadingCache.remove(resourceName);
            }
            //   no handle to instance
            //reset corresponding policy cache
            patternStrategy.resetCache(res.getType(), resourceName);
            log.info("Resource type " + res.getType() + " name " + resourceName + " policy cache is reset.");

        }

    }

    public void resetPageReadingCache(String pageUuid) {
        log.info("PageUuid " + pageUuid + " reading cache is reset.");
        pageReadingCache.remove(pageUuid);
        getPageReadPermission(pageUuid);
    }

    public List<Resource> getResourceOfUserHasOperation(User user, OPERATIONS oper) {
        List<Resource> resList = new ArrayList<Resource>();
        if (!user.isEnabled())
            return resList;

        List<Permission> allPerms = new ArrayList<Permission>();

        allPerms.addAll(user.getPermissions());

        Set<Role> roles = user.getRoles();
        for (Role role : roles) {
            //performance consideration: don't use role.getPermission() as admin or user has too many permissions in large space volume case.
            List<Permission> rolePerms = permissionDAO.getByRoleOperation(role.getName(), oper);
            allPerms.addAll(rolePerms);
        }

        for (Permission perm : allPerms) {
            if (perm.getOperation() != oper)
                continue;

            resList.add(perm.getResource());
        }

        return resList;
    }

    public boolean isAllowResourceAdmin(String resourceName, RESOURCE_TYPES resourceType, User user) {

        boolean found = false;
        if (!user.isEnabled())
            return found;

        List<Permission> allPerms = new ArrayList<Permission>();
        allPerms.addAll(user.getPermissions());

        Set<Role> roles = user.getRoles();
        for (Role role : roles) {
            //performance consideration: don't use role.getPermission() as admin or user has too many permissions in large space volume case.
            List<Permission> rolePerms = permissionDAO.getByRoleResource(role.getName(), resourceName);
            allPerms.addAll(rolePerms);
        }

        for (Permission perm : allPerms) {
            if (perm.getOperation() != OPERATIONS.ADMIN)
                continue;
            if (perm.getResource().getType() != resourceType
                    || !StringUtils.equalsIgnoreCase(resourceName, perm.getResource().getResource()))
                continue;

            found = true;
        }

        return found;
    }

    public void proxyLogout() {
        try {
            SecurityContextHolder.getContext().setAuthentication(null);
            SecurityContextHolder.clearContext();
            ProxyLoginUtil.setRequester(null);
        } catch (Exception e) {
            log.error("Proxy logout failed", e);
        }
    }

    public void proxyLogin(String username) {
        Authentication auth;
        User user = userReadingService.getUserByName(username);
        if (user == null || user.isAnonymous()) {
            auth = new AnonymousAuthenticationToken(ANONYMOUS_KEY, ANONYMOUS_PASSWORD,
                    Arrays.asList((GrantedAuthority) roleDAO.getByName(SYSTEM_ROLES.ANONYMOUS.getName())));
        } else {
            //there is no plain password available, so just set Authenticated as true
            auth = new UsernamePasswordAuthenticationToken(user.getUsername(), "", user.getAuthorities());
        }

        ProxyLoginUtil.setRequester(user);
        SecurityContextHolder.getContext().setAuthentication(auth);

    }

    public String proxyLoginAsSystemAdmin() {
        //don't assume system admin is null...
        Set<String> admins = userReadingService.getSystemAdminMailList();
        User admin = userReadingService.getUserByEmail(admins.iterator().next());
        //login proxy as system admin
        this.proxyLogin(admin.getUsername());

        return admin.getUsername();
    }

    public String proxyLoginAsSpaceAdmin(String spaceUname) {
        //don't assume system admin is null...
        Set<String> admins = userReadingService.getSpaceAdminMailList(spaceUname);
        if (admins.size() > 0) {
            User admin = userReadingService.getUserByEmail(admins.iterator().next());
            if (admin != null) {
                //login proxy as system admin
                this.proxyLogin(admin.getUsername());
                return admin.getUsername();
            }
        }
        return null;
    }

    @Transactional(readOnly = true, propagation = Propagation.REQUIRED)
    public void afterPropertiesSet() throws Exception {
        //NOTE: because spring limitation, spaceDAO does not work during spring startup. this method CANNOT be use.
        if (initialSpaceReadingCacheAtStart) {
            //initial space reading permission at once. this may be a very time consuming method if spaces amount is huge.
            //But it is much better let Server startup slow but allow user get quick result during first search(NOTE, 
            //spaceReadingCache is using by search SecurityFilter)
            List<Space> spaces = spaceDAO.getObjects();
            for (Space space : spaces) {
                getSpaceReadPermission(space.getUnixName());

            }
        }

    }

    //********************************************************************
    //               Private methods
    //********************************************************************
    //strategy to check if readable: if any one of values in user has appear in permission list, it will pass
    private boolean isAllow(List<String> roleUserList, List<String> readPerm) {

        boolean allow = false;
        for (String name : roleUserList) {
            if (readPerm.contains(name)) {
                allow = true;
                break;
            }
        }
        return allow;
    }

    /*   
     * Try to get space private flat from cache, if not exist, then initial it from database and cache it.
     * 
     */
    private Element getSpacePrivate(String spaceUname) {
        Element perm = spaceReadingCache.get(PRIVATE_SPACE_PREFIX + spaceUname);
        if (perm != null) {
            return perm;
        }

        Space space = spaceDAO.getByUname(spaceUname);
        if (space == null) {
            log.error("Unable to find space " + spaceUname + " from database.");
            return null;
        }
        log.info("Space private flag is initial in cache " + spaceUname + " status: " + space.isPrivate());
        perm = new Element(PRIVATE_SPACE_PREFIX + spaceUname, space.isPrivate());
        spaceReadingCache.put(perm);

        return perm;
    }

    /*   
     * Try to get space read permission from cache, if not exist, then initial it from database and cache it.
     * IMPORTANT: spaceReadPermissionCache is pure space reading permission list
     * it won't impact by Instance level permission. This means, any users/roles in a space will always cache.  
     * even Instance block them
     * 
     */
    private Element getSpaceReadPermission(String spaceUname) {

        //TODO: so far, it is not thread-safe, for performance reason. So, read DB may happen multiple times form same space
        //when Multiple Search happen.
        Element element = spaceReadingCache.get(spaceUname);
        if (element != null) {
            return element;
        }

        log.info("Initial space reading permission for space " + spaceUname);
        List<String> readPerm = getSpacePerm(spaceUname, OPERATIONS.READ);
        if (readPerm != null) {
            element = new Element(spaceUname, readPerm);
            spaceReadingCache.put(element);
        }
        return element;
    }

    /**
     * Return list of users(username with Role.USER_PREFIX) and roles(role name) who have specified permission on given space.
     * @param spaceUname
     * @param operation
     * @return
     */
    private List<String> getSpacePerm(String spaceUname, OPERATIONS operation) {
        Resource spaceRes = resourceDAO.getByName(spaceUname);
        if (spaceRes == null) {
            //in some case, Index contains some spaces, but they already removed from Database
            log.error("Space does not exist by spaceUname: " + spaceUname);
            return null;
        }
        Set<Permission> perms = spaceRes.getPermissions();
        List<String> readPerm = new ArrayList<String>();

        if (perms != null) {
            for (Permission permission : perms) {
                if (permission.getOperation() != operation)
                    continue;
                Set<Role> roles = permission.getRoles();
                Set<User> users = permission.getUsers();
                if (roles != null) {
                    for (Role role : roles) {
                        log.info("Space " + spaceUname + " allow role  " + operation.name() + " " + role.getName());
                        readPerm.add(role.getName());
                    }
                }
                if (users != null) {
                    for (User u : users) {
                        //put user name attribute as "USER_xxx"
                        log.info("Space " + spaceUname + " allow user " + operation.name() + " " + u.getUsername());
                        readPerm.add(Role.USER_PREFIX + u.getUsername());
                    }
                }
                break;
            }
            if (readPerm.size() == 0) {
                //!!!
                if (!SharedConstants.SYSTEM_SPACEUNAME.equals(spaceUname))
                    AuditLogger.error("Must BUG! space " + spaceUname + " can not find any " + operation.name()
                            + " permission. It is impossible case. Please trace log!");
                return null;
            } else {
                return readPerm;
            }
        } else {
            log.error("Space can not find permission " + spaceUname);
            return null;
        }
    }

    private Element getPageReadPermission(String pageUuid) {
        Element perm = pageReadingCache.get(pageUuid);
        if (perm != null) {
            return perm;
        }

        log.debug("Initial page reading permission for page " + pageUuid);
        Resource pageRes = resourceDAO.getByName(pageUuid);
        if (pageRes == null) {
            //Most page has no permission setting, so it is better put a dummy page permission element into cache
            //otherwise, pageReadingCache is useless - most page have to read database(resourceDAO.getByName()) again
            //to check this page has not permission.
            pageReadingCache.put(new Element(pageUuid, null));
            log.debug(
                    "Given page resource does not exist " + pageUuid + ". Any permission will inherit from space.");
            return null;
        }

        Set<Permission> perms = pageRes.getPermissions();
        List<String> readPerm = new ArrayList<String>();

        if (perms != null) {
            for (Permission permission : perms) {
                if (permission.getOperation() != OPERATIONS.READ)
                    continue;
                Set<Role> roles = permission.getRoles();
                Set<User> users = permission.getUsers();
                if (roles != null) {
                    for (Role role : roles) {
                        log.info("Page " + pageUuid + " forbidden role reading " + role.getName());
                        readPerm.add(role.getName());
                    }
                }
                if (users != null) {
                    for (User u : users) {
                        //put user name attribute as "USER_xxx"
                        log.info("Page " + pageUuid + " forbidden user reading " + u.getUsername());
                        readPerm.add(Role.USER_PREFIX + u.getUsername());
                    }
                }
                break;
            }
            perm = new Element(pageUuid, readPerm);
            pageReadingCache.put(perm);
        } else {
            log.info("Page can not find permission " + pageUuid);
        }
        return perm;
    }

    /*
     * Find method policy: 
     * <li>find out RESOURCE_TYPES and OPERATION according to class+method name</li>
     * <li>retrieve policies to find out policy which has same RESOURCE_TYPES, OPERATION and same ResourceName</li>
     * 
     * The return should be policy if there is match or null if no match.
     */
    private Policy findMethodPolicy(String clz, String method, Object[] args, int beforeAfter) {
        //try to get by resource (page -> space -> instance) level policy for this resource: spaceUname,PageUuid, Instance
        //find out wikiOperation according to pattern.
        WikiOPERATIONS wikiType = patternStrategy.findMethodRuntimePattern(clz, method, args, beforeAfter);
        //if class method has no mapping pattern, skip authentication: return null

        if (wikiType != null) {
            //get all resources for special resource in given RESOURCE_TYPES: 
            List<Policy> policies = patternStrategy.getPolicies(wikiType.type,
                    wikiType.values.get(RESOURCE_TYPES.SPACE), wikiType.values.get(RESOURCE_TYPES.PAGE));
            //now, need check policy in cache to find out if this type/operation policy exist. 
            //For example, it may not exist if page permission is not defined.
            for (Policy policy : policies) {
                //match: resource Type, name and operation
                if (StringUtils.equalsIgnoreCase(policy.getResourceName(), wikiType.values.get(wikiType.type))
                        && policy.getOperation() == wikiType.operation && policy.getType() == wikiType.type) {
                    return policy;
                }
            }
        }

        return null;
    }

    /*
     * What is dead role/user? For example, A user, Instance forbidden page write, but space allow page write. Obviously, this 
     * user has no permission on page write because Instance overrides space permission. This user on page write permission will mark
     * as dead.  
     * 
     * <BR>
     * The reason to find out dead user/list on special resource is, it gives possibility on Client UI to highlight some permission 
     * doesn't work becuase of override reason.(as example, you mark space page write is on, but instance is off).
     */
    private void findDeadRoleUser(Collection<Permission> list, OPERATIONS operation, List<String> nameList,
            boolean deadlist) {
        for (Permission permission : list) {
            if (permission.getOperation().equals(operation)) {
                //the input nameList is just deadlist, no need compare with upper level attributes list
                if (deadlist) {
                    permission.setDeadRoleUserList(nameList);
                    continue;
                }
                //now iterate all
                List<String> dead = new ArrayList<String>();
                Set<Role> roles = permission.getRoles();
                if (roles != null) {
                    for (Iterator<Role> iter = roles.iterator(); iter.hasNext();) {
                        Role role = iter.next();
                        //does this role is live?
                        if (!nameList.contains(role.getName())) {
                            dead.add(role.getName());
                        }
                    }
                }
                Set<User> users = permission.getUsers();
                if (users != null) {
                    for (Iterator<User> iter = users.iterator(); iter.hasNext();) {
                        User user = iter.next();
                        //does this user is live?
                        if (!nameList.contains(Role.USER_PREFIX + user.getUsername())) {
                            dead.add(Role.USER_PREFIX + user.getUsername());
                        }
                    }
                }

                permission.setDeadRoleUserList(dead);
            }
        }
    }

    private List<String> getRoleUserNameList(User user) {
        List<String> roleUserList = new ArrayList<String>();
        if (user == null || user.isAnonymous()) {
            roleUserList.add(SYSTEM_ROLES.ANONYMOUS.getName());
        } else {
            roleUserList.add(Role.USER_PREFIX + user.getUsername());
            Set<Role> roles = user.getRoles();
            for (Role role : roles) {
                roleUserList.add(role.getName());
            }
        }

        return roleUserList;
    }

    private void operMatrix(int[] p1, List<String> roleUserList, Policy policy) {
        boolean contain = false;
        for (Iterator<ConfigAttribute> iter = policy.getMutableAttributeDefinition().iterator(); iter.hasNext();) {
            ConfigAttribute attr = iter.next();
            //if this (attribute)role is not included in live condition(policy), then remove this role
            if (roleUserList.contains(attr.getAttribute())) {
                contain = true;
                p1[policy.getOperation().ordinal()] = 1;
                break;
            }
        }
        //this policy does not contain any role/user for current user's, so remove it
        if (!contain) {
            p1[policy.getOperation().ordinal()] = 2;
        }

    }

    //********************************************************************
    //                       Set / Get
    //********************************************************************
    public void setResourceDAO(ResourceDAO resourceDAO) {
        this.resourceDAO = resourceDAO;
    }

    public void setPermissionDAO(PermissionDAO permissionDAO) {
        this.permissionDAO = permissionDAO;
    }

    public void setRoleDAO(RoleDAO roleDAO) {
        this.roleDAO = roleDAO;
    }

    public void setPatternStrategy(PatternStrategy wikiPrivilegeStrategy) {
        this.patternStrategy = wikiPrivilegeStrategy;
    }

    public void setPageDAO(PageDAO pageDAO) {
        this.pageDAO = pageDAO;
    }

    public void setAuthenticationManager(ProviderManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    public void setSpaceReadingCache(Cache spaceReadPermissionCache) {
        this.spaceReadingCache = spaceReadPermissionCache;
    }

    public void setInitialSpaceReadingCacheAtStart(boolean initialSpaceReadingCache) {
        this.initialSpaceReadingCacheAtStart = initialSpaceReadingCache;
    }

    public void setSpaceDAO(SpaceDAO spaceDAO) {
        this.spaceDAO = spaceDAO;
    }

    public void setPageReadingCache(Cache pageReadingCache) {
        this.pageReadingCache = pageReadingCache;
    }

    public void setUserReadingService(UserReadingService userReadingService) {
        this.userReadingService = userReadingService;
    }

}