Java tutorial
/** * 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(); } }