org.eclipse.skalli.core.permit.PermitComponent.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.skalli.core.permit.PermitComponent.java

Source

/*******************************************************************************
 * Copyright (c) 2010-2014 SAP AG and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     SAP AG - initial API and implementation
 *******************************************************************************/
package org.eclipse.skalli.core.permit;

import java.security.Principal;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.commons.lang.StringUtils;
import org.eclipse.skalli.model.Group;
import org.eclipse.skalli.model.Member;
import org.eclipse.skalli.model.Project;
import org.eclipse.skalli.model.User;
import org.eclipse.skalli.services.configuration.ConfigurationService;
import org.eclipse.skalli.services.configuration.EventConfigUpdate;
import org.eclipse.skalli.services.event.EventListener;
import org.eclipse.skalli.services.event.EventService;
import org.eclipse.skalli.services.group.GroupService;
import org.eclipse.skalli.services.permit.Permit;
import org.eclipse.skalli.services.permit.PermitService;
import org.eclipse.skalli.services.permit.PermitSet;
import org.eclipse.skalli.services.role.RoleProvider;
import org.eclipse.skalli.services.role.RoleService;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class PermitComponent implements PermitService, EventListener<EventConfigUpdate> {

    private static final Logger LOG = LoggerFactory.getLogger(PermitComponent.class);

    private static final String PERMITS_ATTRIBUTE = "PERMITS"; //$NON-NLS-1$
    private static final String PERMITS_TIMESTAMP_ATTRIBUTE = "PERMITS_TIMESTAMP"; //$NON-NLS-1$
    private static final String PERMITS_PROJECT_ATTRIBUTE = "PERMITS_PROJECT"; //$NON-NLS-1$

    private static final String PROPERTY_PROJECTID = StringUtils.substringBetween(Permit.PROJECT_WILDCARD, "{", //$NON-NLS-1$
            "}"); //$NON-NLS-1$
    private static final String USER_WILDCARD = StringUtils.substringBetween(Permit.USER_WILDCARD, "{", "}"); //$NON-NLS-1$ //$NON-NLS-2$

    private static final PermitSet DEFAULT_PERMITS = new PermitSet(Permit.FORBID_ALL);
    private static final PermitSet DEFAULT_ADMIN_PERMITS = new PermitSet(Permit.ALLOW_ALL);

    private static final String PATH_PROJECTS = "projects"; //$NON-NLS-1$

    private static ThreadLocal<String> threadUserId = new InheritableThreadLocal<String>();
    private static ThreadLocal<Project> threadProject = new InheritableThreadLocal<Project>();
    private static ThreadLocal<PermitSet> threadPermits = new InheritableThreadLocal<PermitSet>();

    private ComponentContext context;
    private ConfigurationService configService;
    private Set<RoleProvider> roleProviders = new HashSet<RoleProvider>();

    // ensure that threads are properly synchronized when accessing this instance variable
    private volatile long permitsConfigChangedTimestamp;

    protected void activate(ComponentContext context) {
        this.context = context;
        LOG.info(MessageFormat.format("[PermitService] {0} : activated", //$NON-NLS-1$
                (String) context.getProperties().get(ComponentConstants.COMPONENT_NAME)));
    }

    protected void deactivate(ComponentContext context) {
        this.context = null;
        LOG.info(MessageFormat.format("[PermitService] {0} : deactivated", //$NON-NLS-1$
                (String) context.getProperties().get(ComponentConstants.COMPONENT_NAME)));
    }

    protected void bindConfigurationService(ConfigurationService configService) {
        LOG.info(MessageFormat.format("bindConfigurationService({0})", configService)); //$NON-NLS-1$
        this.configService = configService;
        logoutAll();
    }

    protected void unbindConfigurationService(ConfigurationService configService) {
        LOG.info(MessageFormat.format("unbindConfigurationService({0})", configService)); //$NON-NLS-1$
        this.configService = null;
        logoutAll();
    }

    protected void bindEventService(EventService eventService) {
        LOG.info(MessageFormat.format("bindEventService({0})", eventService)); //$NON-NLS-1$
        eventService.registerListener(EventConfigUpdate.class, this);
    }

    protected void unbindEventService(EventService eventService) {
        LOG.info(MessageFormat.format("unbindEventService({0})", eventService)); //$NON-NLS-1$
    }

    protected void bindRoleProvider(RoleProvider roleProvider) {
        roleProviders.add(roleProvider);
        logoutAll();
    }

    protected void unbindRoleProvider(RoleProvider roleProvider) {
        roleProviders.remove(roleProvider);
        logoutAll();
    }

    private GroupService getGroupService() {
        if (context != null) {
            return (GroupService) context.locateService("GroupService"); //$NON-NLS-1$
        }
        return null;
    }

    private RoleService getRoleService() {
        if (context != null) {
            return (RoleService) context.locateService("RoleService"); //$NON-NLS-1$
        }
        return null;
    }

    @Override
    public boolean hasPermit(Permit permit) {
        return hasPermit(permit.getLevel(), permit.getAction(), permit.getSegments());
    }

    @Override
    public boolean hasPermit(int level, String action, String... segments) {
        PermitSet permits = threadPermits.get();
        if (permits == null) {
            permits = DEFAULT_PERMITS;
            threadPermits.set(permits);
        }
        return Permit.match(permits, level, action, segments);
    }

    @Override
    public boolean hasPermit(int level, String action, Project project) {
        String projectId = project.getProjectId();
        return StringUtils.isNotBlank(projectId) && hasPermit(level, action, PATH_PROJECTS, projectId)
                || hasPermit(level, action, PATH_PROJECTS, project.getUuid().toString());
    }

    @Override
    public boolean hasPermit(int level, String action, Project project, String... segments) {
        if (segments == null || segments.length == 0) {
            return hasPermit(level, action, project);
        }
        String[] amendedSegments = new String[segments.length + 2];
        amendedSegments[0] = PATH_PROJECTS;
        System.arraycopy(segments, 0, amendedSegments, 2, segments.length);
        String projectId = project.getProjectId();
        return StringUtils.isNotBlank(projectId) ? hasProjectPermit(level, action, projectId, amendedSegments)
                : hasProjectPermit(level, action, project.getUuid().toString(), amendedSegments);
    }

    private boolean hasProjectPermit(int level, String action, String id, String... segments) {
        segments[1] = id;
        return StringUtils.isNotBlank(id) && hasPermit(level, action, segments);
    }

    @Override
    public String login(HttpServletRequest request, Project project) {
        String userId = null;
        Principal userPrincipal = request.getUserPrincipal();
        if (userPrincipal != null) {
            userId = userPrincipal.getName();
            if (StringUtils.isNotBlank(userId)) {
                userId = userId.toLowerCase(Locale.ENGLISH);
            }
        }
        HttpSession session = request.getSession();
        PermitSet permits = getSessionPermits(session, userId, project);
        if (permits == null) {
            permits = getPermits(userId, project);
            setSessionPermits(session, permits, project);
        }
        threadUserId.set(userId);
        threadProject.set(project);
        threadPermits.set(permits);
        return userId;
    }

    @Override
    public void switchProject(Project project) {
        String userId = threadUserId.get();
        PermitSet permits = getPermits(userId, project);
        threadProject.set(project);
        threadPermits.set(permits);
    }

    @Override
    public void logout(HttpServletRequest request) {
        removeSessionPermits(request.getSession());
        threadUserId.set(null);
        threadProject.set(null);
        threadPermits.set(null);
    }

    @Override
    public void logoutAll() {
        permitsConfigChangedTimestamp = System.currentTimeMillis();
    }

    @Override
    public String getLoggedInUser() {
        return threadUserId.get();
    }

    @Override
    public void onEvent(EventConfigUpdate event) {
        // we do not know which configurations affect permissions,
        // so throw away all login sessions just to be sure
        logoutAll();
    }

    private PermitSet getSessionPermits(HttpSession session, String userId, Project project) {
        if (session == null) {
            return null;
        }
        if (StringUtils.isBlank(userId)) {
            // anonymous users have never a session
            return null;
        }
        if (project != null) {
            // ensure that session permits apply to the same project
            String projectId = (String) session.getAttribute(PERMITS_PROJECT_ATTRIBUTE);
            if (!project.getProjectId().equals(projectId)) {
                return null;
            }
        }
        // ensure that session permits are still valid, i.e. that no configuration
        // change has occurred in the meantime
        Long timestamp = (Long) session.getAttribute(PERMITS_TIMESTAMP_ATTRIBUTE);
        if (timestamp == null || timestamp <= permitsConfigChangedTimestamp) {
            return null;
        }
        return (PermitSet) session.getAttribute(PERMITS_ATTRIBUTE);
    }

    private void setSessionPermits(HttpSession session, PermitSet permits, Project project) {
        if (session != null) {
            session.setAttribute(PERMITS_TIMESTAMP_ATTRIBUTE, System.currentTimeMillis());
            session.setAttribute(PERMITS_ATTRIBUTE, permits);
            if (project != null) {
                session.setAttribute(PERMITS_PROJECT_ATTRIBUTE, project.getProjectId());
            } else {
                session.removeAttribute(PERMITS_PROJECT_ATTRIBUTE);
            }
        }
    }

    private void removeSessionPermits(HttpSession session) {
        if (session != null) {
            session.removeAttribute(PERMITS_TIMESTAMP_ATTRIBUTE);
            session.removeAttribute(PERMITS_ATTRIBUTE);
            session.removeAttribute(PERMITS_PROJECT_ATTRIBUTE);
        }
    }

    @Override
    public PermitSet getPermits(String userId, Project project) {
        PermitSet permits = new PermitSet();
        if (configService != null) {
            userId = StringUtils.isNotBlank(userId) ? userId : User.UNKNOWN;

            String templateId = project != null ? project.getProjectTemplateId() : null;

            List<String> groups = getGroupNames(userId);
            List<String> groupRoles = getRoles(groups);
            List<String> roles = getRoles(userId);
            List<String> projectRoles = getProjectRoles(userId, project);

            PermitsConfig permitsConfig = configService.readConfiguration(PermitsConfig.class);
            if (permitsConfig != null) {
                Map<String, String> properties = new HashMap<String, String>();
                properties.put(USER_WILDCARD, userId);
                String projectId = "?"; //$NON-NLS-1$
                if (project != null) {
                    projectId = project.getProjectId();
                    if (StringUtils.isBlank(projectId)) {
                        projectId = project.getUuid().toString();
                    }
                }
                properties.put(PROPERTY_PROJECTID, projectId);

                Map<String, List<PermitConfig>> permitsByType = permitsConfig.getByType();
                collectGlobalCommits(properties, permitsByType, permits);
                if (templateId != null) {
                    collectTemplatePermits(templateId, properties, permitsByType, permits);
                }
                if (groupRoles.size() > 0) {
                    collectRolePermits(groupRoles, properties, permitsByType, permits);
                }
                if (roles.size() > 0) {
                    collectRolePermits(roles, properties, permitsByType, permits);
                }
                if (projectRoles.size() > 0) {
                    collectRolePermits(projectRoles, properties, permitsByType, permits);
                }
                if (groups.size() > 0) {
                    collectGroupPermits(groups, properties, permitsByType, permits);
                }
                collectUserPermits(userId, properties, permitsByType, permits);
            } else {
                // special handling for bootstrapping of a new instance or for
                // instances where authorization is not relevant
                permits.addAll(DEFAULT_ADMIN_PERMITS);
            }
        }
        if (permits.isEmpty()) {
            permits.addAll(DEFAULT_PERMITS);
        }
        return permits;
    }

    private void collectGlobalCommits(Map<String, String> properties, Map<String, List<PermitConfig>> permitsConfig,
            PermitSet permits) {
        List<PermitConfig> globalPermits = permitsConfig.get(PermitsConfig.GLOBAL_PERMIT);
        if (globalPermits != null) {
            for (PermitConfig globalPermit : globalPermits) {
                permits.add(globalPermit.asPermit(properties));
            }
        }
    }

    private void collectUserPermits(String userId, Map<String, String> properties,
            Map<String, List<PermitConfig>> permitsConfig, PermitSet permits) {
        List<PermitConfig> userPermits = permitsConfig.get(PermitsConfig.USER_PERMIT);
        if (userPermits != null) {
            for (PermitConfig userPermit : userPermits) {
                if (userId.equals(userPermit.getOwner())) {
                    permits.add(userPermit.asPermit(properties));
                }
            }
        }
    }

    private void collectGroupPermits(List<String> groups, Map<String, String> properties,
            Map<String, List<PermitConfig>> permitsConfig, PermitSet permits) {
        List<PermitConfig> groupPermits = permitsConfig.get(PermitsConfig.GROUP_PERMIT);
        if (groupPermits != null) {
            for (PermitConfig groupPermit : groupPermits) {
                if (groups.contains(groupPermit.getOwner())) {
                    permits.add(groupPermit.asPermit(properties));
                }
            }
        }
    }

    private void collectRolePermits(List<String> roles, Map<String, String> properties,
            Map<String, List<PermitConfig>> permitsConfig, PermitSet permits) {
        List<PermitConfig> rolePermits = permitsConfig.get(PermitsConfig.ROLE_PERMIT);
        if (rolePermits != null) {
            for (PermitConfig rolePermit : rolePermits) {
                if (roles.contains(rolePermit.getOwner())) {
                    permits.add(rolePermit.asPermit(properties));
                }
            }
        }
    }

    private void collectTemplatePermits(String templateId, Map<String, String> properties,
            Map<String, List<PermitConfig>> permitsConfig, PermitSet permits) {
        List<PermitConfig> templatePermits = permitsConfig.get(PermitsConfig.TEMPLATE_PERMIT);
        if (templatePermits != null) {
            for (PermitConfig templatePermit : templatePermits) {
                if (templateId.equals(templatePermit.getOwner())) {
                    permits.add(templatePermit.asPermit(properties));
                }
            }
        }
    }

    private List<String> getRoles(List<String> groups) {
        RoleService roleService = getRoleService();
        if (roleService == null) {
            return Collections.emptyList();
        }
        return roleService.getRoles(groups.toArray(new String[groups.size()]));
    }

    private List<String> getRoles(String userId) {
        List<String> roles = new ArrayList<String>();
        RoleService roleService = getRoleService();
        if (roleService != null) {
            roles.addAll(roleService.getRoles(userId));
        }
        return roles;
    }

    private List<String> getProjectRoles(String userId, Project project) {
        List<String> roles = new ArrayList<String>();
        if (project != null) {
            Member member = new Member(userId);
            for (RoleProvider roleProvider : roleProviders) {
                Map<String, SortedSet<Member>> membersByRole = roleProvider.getMembersByRole(project);
                for (String role : membersByRole.keySet()) {
                    if (membersByRole.get(role).contains(member)) {
                        roles.add(role);
                    }
                }
            }
        }
        return roles;
    }

    private List<String> getGroupNames(String userId) {
        List<String> groupNames = new ArrayList<String>();
        GroupService groupService = getGroupService();
        if (groupService != null) {
            for (Group group : groupService.getGroups(userId)) {
                groupNames.add(group.getGroupId());
            }
        }
        return groupNames;
    }
}