org.jtalks.poulpe.security.AclAwareDecisionVoter.java Source code

Java tutorial

Introduction

Here is the source code for org.jtalks.poulpe.security.AclAwareDecisionVoter.java

Source

/**
 * Copyright (C) 2011  JTalks.org Team
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * This library 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
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.jtalks.poulpe.security;

import com.google.common.annotations.VisibleForTesting;
import org.jtalks.common.model.entity.ComponentType;
import org.jtalks.poulpe.service.UserService;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.access.expression.WebExpressionVoter;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Collection;

import static org.springframework.web.context.request.RequestAttributes.SCOPE_SESSION;

/**
 * A custom SpringSecurity Voter that checks whether user has enough rights to the Admin Panel component: <ul><li>If
 * User is not a part of any group that has granting permissions to the Poulpe component, then she is not
 * authorized.</li><li>If User is in a group that has administration permissions on the Poulpe component, then she is
 * authorized</li><li>If User is in the group that is restricted to have access to Poulpe component, then she is not
 * allowed to log in even if she is in another group that is allowed. Restricted permission is stronger than granting
 * one.</li></ul><br/>Note, that if a Poulpe component does not exist, then no user is authorized to log in, thus people
 * are forced to change database manually to create the component. But when Poulpe is deployed first time, the
 * respective component is created and a user with predefined credentials is also created, which is described <a
 * href="http://jira.jtalks.org/browse/POULPE-335">here</a>.
 *
 * @author dionis
 * @see <a href="http://jira.jtalks.org/browse/POULPE-296">Related JIRA</a>
 */
public class AclAwareDecisionVoter implements AccessDecisionVoter {
    private static final String AUTHORIZED = "authorizedPoulpeUser";
    private final AccessDecisionVoter baseVoter;
    private final UserService userService;

    /** Constructor used for initialization from Spring IoC */
    public AclAwareDecisionVoter(UserService userService) {
        this(new WebExpressionVoter(), userService);
    }

    /** Constructor used for initialization from JUnit */
    @VisibleForTesting
    AclAwareDecisionVoter(AccessDecisionVoter baseVoter, UserService userService) {
        this.baseVoter = baseVoter;
        this.userService = userService;
    }

    /** {@inheritDoc} */
    @Override
    public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
        int baseVoterVoteResult = baseVoter.vote(authentication, object, attributes);
        if (baseVoterVoteResult == ACCESS_GRANTED && authenticatedAsUser(authentication)) {
            return authorizeAndSaveDecisionIntoSession(authentication);
        } else {
            return baseVoterVoteResult;
        }
    }

    /** Authorize and save result in session attributes */
    private int authorizeAndSaveDecisionIntoSession(Authentication authentication) {
        if (alreadyAuthorized()) {
            return ACCESS_GRANTED;
        }
        boolean allow = userService.accessAllowedToComponentType(usernameOf(authentication),
                ComponentType.ADMIN_PANEL);
        getRequestAttributes().setAttribute(AUTHORIZED, allow, ServletRequestAttributes.SCOPE_SESSION);
        return allow ? ACCESS_GRANTED : ACCESS_DENIED;
    }

    /**
     * Checks whether the user was already authorized or not yet. This information is kept in the HTTP session.
     *
     * @return true if the user was authorized before and the authorization was successful, otherwise returns false
     */
    private boolean alreadyAuthorized() {
        Boolean authorized = (Boolean) getRequestAttributes().getAttribute(AUTHORIZED, SCOPE_SESSION);
        return authorized != null && authorized;
    }

    /** @return user name from {@link Authentication} token credentials*/
    private String usernameOf(Authentication authentication) {
        return ((UserDetails) authentication.getPrincipal()).getUsername();
    }

    /**
     * Since SpringSecurity by default thinks of anonymous user as the one that is authenticated, we need to check a bit
     * more. First we check whether she's authenticated by SpringSecurity per se, and then we check whether {@link
     * UserDetails} is created which indicates that it's not an anonymous user.
     *
     * @param authentication to check whether it's an authenticated user and that it's not anonymous
     * @return true if the user is authenticated by SpringSecurity as an existing user, false if it's an anonymous or
     *         not authenticated user
     */
    private boolean authenticatedAsUser(Authentication authentication) {
        return authentication.isAuthenticated() && authentication.getPrincipal() instanceof UserDetails;
    }

    /** {@inheritDoc} */
    @Override
    public boolean supports(ConfigAttribute attribute) {
        return baseVoter.supports(attribute);
    }

    /** {@inheritDoc} */
    @Override
    public boolean supports(Class<?> clazz) {
        return baseVoter.supports(clazz);
    }

    /**
     * @return current request attributes*/
    @VisibleForTesting
    RequestAttributes getRequestAttributes() {
        return RequestContextHolder.currentRequestAttributes();
    }
}