org.betaconceptframework.astroboa.context.SecurityContext.java Source code

Java tutorial

Introduction

Here is the source code for org.betaconceptframework.astroboa.context.SecurityContext.java

Source

/*
 * Copyright (C) 2005-2008 BetaCONCEPT LP.
 *
 * This file is part of Astroboa.
 *
 * Astroboa is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Astroboa 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 Astroboa.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.betaconceptframework.astroboa.context;

import java.io.Serializable;
import java.security.Principal;
import java.security.acl.Group;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.security.auth.Subject;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.betaconceptframework.astroboa.api.security.AstroboaPrincipalName;
import org.betaconceptframework.astroboa.api.security.IdentityPrincipal;
import org.betaconceptframework.astroboa.security.CmsGroup;
import org.betaconceptframework.astroboa.security.CmsPrincipal;

/**
 * This class contains all necessary security information about
 * an authenticated user or entity, both referred as "subject".
 * 
 * Its methods should be used to perform role-based authorization.
 * 
 * An instance of this class is generated each time a subject 
 * successfully logs in Astroboa and is accessible from method
 * {@link AstroboaClientContextHolder#getActiveSecurityContext()}.
 * 
 * 
 * @author Gregory Chomatas (gchomatas@betaconcept.com)
 * @author Savvas Triantafyllou (striantafyllou@betaconcept.com)
 * 
 */
public final class SecurityContext implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 4368086721315850622L;

    private final String identity;
    private final String authenticationToken;
    private final Subject subject;
    private final int authenticationTokenTimeout;

    //List containing all repositories, authenticated user or entity is authorized to access
    private List<String> authorizedRepositories;

    //List containing all user's roles.
    private Set<String> roles;

    public SecurityContext(String authenticationToken, Subject subject, int authenticationTokenTimeout,
            List<String> availableRepositories) {
        this.authenticationToken = authenticationToken;
        this.subject = subject;
        this.authenticationTokenTimeout = authenticationTokenTimeout;

        this.identity = retrieveIdentityFromSubject();

        this.roles = retrieveRolesFromSubject();

        this.authorizedRepositories = retrieveAuthorizedRepositoriesFromSubject(availableRepositories);
    }

    private List<String> retrieveAuthorizedRepositoriesFromSubject(List<String> availableRepositories) {

        List<String> authorizedRepositories = new ArrayList<String>();

        boolean foundAuthorizedRepositoriesPrincipal = false;

        if (subject != null) {
            Set<Group> subjectGroups = subject.getPrincipals(Group.class);

            if (subjectGroups != null) {
                for (Group group : subjectGroups) {
                    if (group.getName() != null
                            && AstroboaPrincipalName.AuthorizedRepositories.toString().equals(group.getName())) {
                        foundAuthorizedRepositoriesPrincipal = true;
                        Enumeration groupMembers = group.members();
                        while (groupMembers.hasMoreElements()) {
                            Principal groupPrincipal = (Principal) groupMembers.nextElement();
                            authorizedRepositories.add(groupPrincipal.getName());
                        }

                        break;
                    }
                }
            }
        }

        //In cases where no information about authorized repositories
        //is provided in Subject, a PERMIT ALL policy is enforced, 
        //thus available repositories must be known during initialization of this 
        //context
        if (!foundAuthorizedRepositoriesPrincipal) {
            if (CollectionUtils.isNotEmpty(availableRepositories)) {
                authorizedRepositories.addAll(availableRepositories);
            }
        }

        return authorizedRepositories;
    }

    private Set<String> retrieveRolesFromSubject() {
        Set<String> roles = new HashSet<String>();

        if (subject != null) {
            Set<Group> groups = subject.getPrincipals(Group.class);

            if (groups != null) {
                for (Group group : groups) {
                    if (group.getName() != null && AstroboaPrincipalName.Roles.toString().equals(group.getName())) {

                        addGroupMembersToRoles(group, roles);

                        break;
                    }
                }
            }
        }

        return roles;

    }

    private String retrieveIdentityFromSubject() {
        if (subject != null) {
            Set<IdentityPrincipal> userPrincipals = subject.getPrincipals(IdentityPrincipal.class);

            if (CollectionUtils.isEmpty(userPrincipals)) {
                return "";
            }

            //Retrieve the first one. Normally it should not have more than one
            return userPrincipals.iterator().next().getName();
        }

        return "";
    }

    /**
     * Convenient method to obtain authenticated user id (usually the username) which normally is provided
     * as a {@link IdentityPrincipal principal} in {@link #getSubject() subject}.
     * 
     * @return
     *    The authenticated user name
     */
    public String getIdentity() {
        return identity;
    }

    /** 
     * Upon successfully connecting to a repository, an authentication
     * token is created. This token can be used for further use of Astroboa services
     * in order to avoid to authenticate every time.
     * 
     * @return
     *       Authentication token created upon successful connection to an Astroboa repository
     */
    public String getAuthenticationToken() {
        return authenticationToken;
    }

    /**
     * Return an instance of authenticated user or entity along with 
     * its {@link Principal identities}, its roles and any other security related
     * information.
     * 
     * <p>
     * THIS INSTANCE MUST NOT BE ALTERED IN ANY WAY. If you want to perform changes
     * in user's roles or principals, you have to use methods provided by this class.
     * To obtain a reference to this class you can call {@link AstroboaClientContextHolder#getActiveSecurityContext()}
     * at any point at your code.
     * </p>
     * 
     * @return
     *    Security related information for authenticated user>
     */
    public Subject getSubject() {
        return subject;
    }

    /**
     * Authentication Token timeout in minutes.
     * 
     * <p>
     * It represents the amount of the idle time 
     * that authentication token is considered valid. 
     * After that time authentication token is removed.
     * </p>
     * 
     * 
     * @return
     */
    public int getAuthenticationTokenTimeout() {
        return authenticationTokenTimeout;
    }

    /**
     * Convenient method to retrieve all roles for authenticated user.
     * 
     * This method returns all Roles found under the FIRST principal
     * found in Subject with the name {@link AstroboaPrincipalName#Roles}. 
     * 
     * If roles are provided in a tree, then this tree is traversed as well
     * 
     * @return
     */
    public List<String> getAllRoles() {
        return new ArrayList<String>(roles);
    }

    private void addGroupMembersToRoles(Group group, Set<String> roles) {
        Enumeration groupMembers = group.members();
        while (groupMembers.hasMoreElements()) {
            Principal role = (Principal) groupMembers.nextElement();

            roles.add(role.getName());

            if (role instanceof Group) {
                addGroupMembersToRoles((Group) role, roles);
            }
        }
    }

    /**
     * Retrieve a list of all repositories which authenticated user or entity 
     * is authorized to access.
     * 
     * @return
     */
    public List<String> getAuthorizedRepositories() {
        return new ArrayList<String>(authorizedRepositories);
    }

    public boolean hasRole(String role) {
        return StringUtils.isNotBlank(role) && roles.contains(role);
    }

    /*
     * This method adds a role to an already authenticated user. 
     * It should be used in rare cases only.
     * 
     * Normally, user's roles should not be altered this way. 
     */
    public boolean addRole(String role) {

        if (StringUtils.isBlank(role)) {
            return false;
        }

        Set<Group> groups = subject.getPrincipals(Group.class);

        boolean roleGroupFound = false;

        boolean roleAdded = false;

        String nameOfGroupWhichContainsTheRoles = AstroboaPrincipalName.Roles.toString();

        if (groups != null) {

            for (Group group : groups) {
                if (StringUtils.equals(nameOfGroupWhichContainsTheRoles, group.getName())) {
                    roleGroupFound = true;

                    final CmsPrincipal rolePrincipal = new CmsPrincipal(role);
                    if (!group.isMember(rolePrincipal)) {
                        group.addMember(rolePrincipal);
                        roleAdded = true;
                    }

                    break;
                }
            }
        }

        if (!roleGroupFound) {
            Group rolesPrincipal = new CmsGroup(nameOfGroupWhichContainsTheRoles);
            rolesPrincipal.addMember(new CmsPrincipal(role));
            subject.getPrincipals().add(rolesPrincipal);
            roleAdded = true;
        }

        if (roleAdded) {
            this.roles.add(role);
        }

        return roleAdded;
    }

    /*
     * This method removes a role to an already authenticated user. 
     * It should be used in rare cases only.
     * 
     * Normally, user's roles should not be altered this way. 
     */
    public boolean removeRole(String role) {

        if (StringUtils.isBlank(role)) {
            return false;
        }

        boolean roleHasBeenRemoved = false;

        Set<Group> groups = subject.getPrincipals(Group.class);

        if (groups != null) {

            String nameOfGroupWhichContainsTheRoles = AstroboaPrincipalName.Roles.toString();

            for (Group group : groups) {
                if (StringUtils.equals(nameOfGroupWhichContainsTheRoles, group.getName())) {
                    final CmsPrincipal rolePrincipal = new CmsPrincipal(role);

                    if (group.isMember(rolePrincipal)) {
                        roleHasBeenRemoved = group.removeMember(rolePrincipal);
                        break;
                    }
                }
            }
        }

        //remove role from the list as well
        if (roleHasBeenRemoved && this.roles.contains(role)) {
            this.roles.remove(role);
        }

        return roleHasBeenRemoved;

    }

    public String toString() {
        return " SecurityContext authToken : " + authenticationToken
                + (subject != null ? " Subject : " + subject.toString() : "");
    }
}