Consolidate SWF security tags with SEC taglibs

Before this change, SWF had its own faclets security tag library with
hierarchy root in AbstractAuthorizeTag.

As of this change SWF facelets security tag library inherits Spring SEC
AbstractAuthorizeTag instead, consolidating the two frameworks making
it easier to maintain, extend and reuse. This is made possible by
adding Spring security taglibs module as optional SWF facelets
dependecy. Because the dependency is optional, existing SWF facelets
security tag library users will have to explicitly add Spring Security
taglibs module as dependency.

Issue: SWF-1557
This commit is contained in:
Stevo Slavic
2013-02-03 15:02:02 +01:00
committed by Rossen Stoyanchev
parent ec83f86d9f
commit c296298b01
3 changed files with 4 additions and 337 deletions

View File

@@ -210,6 +210,7 @@ project("spring-faces") {
optional("com.sun.facelets:jsf-facelets:1.1.14")
optional("org.springframework.security:spring-security-core:$springSecurityVersion")
optional("org.springframework.security:spring-security-web:$springSecurityVersion")
optional("org.springframework.security:spring-security-taglibs:$springSecurityVersion")
optional("org.springframework:spring-webmvc-portlet:$springVersion")
testCompile("log4j:log4j:$log4jVersion") {
exclude group: "javax.mail", module: "mail"

View File

@@ -1,336 +0,0 @@
/*
* Copyright 2004-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.faces.security;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.context.ApplicationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ParseException;
import org.springframework.security.access.expression.ExpressionUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.expression.WebSecurityExpressionHandler;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
* <p>
* A base class for an &lt;authorize&gt; tag used to make Spring Security based authorization decisions.
* </p>
*
* <p>
* This class is independent of tag rendering technology (JSP, Facelets). It treats tag attributes as simple strings
* (with the notable exception of the "access" attribute, which is always expected to contain a Spring EL expression).
* Therefore subclasses are expected to extract tag attribute values from the specific rendering technology, evaluate
* them as expressions if necessary, and use the result to set the String-based attributes of this class.
* </p>
*
* @author Francois Beausoleil
* @author Luke Taylor
* @author Rossen Stoyanchev
*
* @since 2.2.0
*/
public abstract class AbstractAuthorizeTag {
private String access;
private String url;
private String method;
private String ifAllGranted;
private String ifAnyGranted;
private String ifNotGranted;
/**
* This method allows subclasses to provide a way to access the ServletRequest according to the rendering
* technology.
*/
protected abstract ServletRequest getRequest();
/**
* This method allows subclasses to provide a way to access the ServletResponse according to the rendering
* technology.
*/
protected abstract ServletResponse getResponse();
/**
* This method allows subclasses to provide a way to access the ServletContext according to the rendering
* technology.
*/
protected abstract ServletContext getServletContext();
/**
* Make an authorization decision by considering all &lt;authorize&gt; tag attributes. The following are valid
* combinations of attributes:
* <ul>
* <li>access</li>
* <li>url, method</li>
* <li>ifAllGranted, ifAnyGranted, ifNotGranted</li>
* </ul>
* The above combinations are mutually exclusive and evaluated in the given order.
*
* @return the result of the authorization decision
*
* @throws IOException
*/
public boolean authorize() throws IOException {
boolean isAuthorized = false;
if (StringUtils.hasText(getAccess())) {
isAuthorized = authorizeUsingAccessExpression();
} else if (StringUtils.hasText(getUrl())) {
isAuthorized = authorizeUsingUrlCheck();
} else {
isAuthorized = authorizeUsingGrantedAuthorities();
}
return isAuthorized;
}
/**
* Make an authorization decision by considering ifAllGranted, ifAnyGranted, and ifNotGranted. All 3 or any
* combination can be provided. All provided attributes must evaluate to true.
*
* @return the result of the authorization decision
*/
public boolean authorizeUsingGrantedAuthorities() {
boolean hasTextAllGranted = StringUtils.hasText(getIfAllGranted());
boolean hasTextAnyGranted = StringUtils.hasText(getIfAnyGranted());
boolean hasTextNotGranted = StringUtils.hasText(getIfNotGranted());
if ((!hasTextAllGranted) && (!hasTextAnyGranted) && (!hasTextNotGranted)) {
return false;
}
final Collection<? extends GrantedAuthority> granted = getPrincipalAuthorities();
if (hasTextAllGranted) {
if (!granted.containsAll(parseAuthoritiesString(getIfAllGranted()))) {
return false;
}
}
if (hasTextAnyGranted) {
Set<? extends GrantedAuthority> grantedCopy = retainAll(granted, parseAuthoritiesString(getIfAnyGranted()));
if (grantedCopy.isEmpty()) {
return false;
}
}
if (hasTextNotGranted) {
Set<GrantedAuthority> grantedCopy = retainAll(granted, parseAuthoritiesString(getIfNotGranted()));
if (!grantedCopy.isEmpty()) {
return false;
}
}
return true;
}
/**
* Make an authorization decision based on a Spring EL expression. See the "Expression-Based Access Control" chapter
* in Spring Security for details on what expressions can be used.
*
* @return the result of the authorization decision
*
* @throws IOException
*/
public boolean authorizeUsingAccessExpression() throws IOException {
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
if (currentUser == null) {
return false;
}
WebSecurityExpressionHandler handler = getExpressionHandler();
Expression accessExpression;
try {
accessExpression = handler.getExpressionParser().parseExpression(getAccess());
} catch (ParseException e) {
IOException ioException = new IOException();
ioException.initCause(e);
throw ioException;
}
FilterInvocation f = new FilterInvocation(getRequest(), getResponse(), new FilterChain() {
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
throw new UnsupportedOperationException();
}
});
return ExpressionUtils.evaluateAsBoolean(accessExpression, handler.createEvaluationContext(currentUser, f));
}
/**
* Make an authorization decision based on the URL and HTTP method attributes. True is returned if the user is
* allowed to access the given URL as defined.
*
* @return the result of the authorization decision
*
* @throws IOException
*/
public boolean authorizeUsingUrlCheck() throws IOException {
String contextPath = ((HttpServletRequest) getRequest()).getContextPath();
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
return getPrivilegeEvaluator().isAllowed(contextPath, getUrl(), getMethod(), currentUser);
}
public String getAccess() {
return this.access;
}
public void setAccess(String access) {
this.access = access;
}
public String getUrl() {
return this.url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethod() {
return this.method;
}
public void setMethod(String method) {
this.method = (method != null) ? method.toUpperCase() : null;
}
public String getIfAllGranted() {
return this.ifAllGranted;
}
public void setIfAllGranted(String ifAllGranted) {
this.ifAllGranted = ifAllGranted;
}
public String getIfAnyGranted() {
return this.ifAnyGranted;
}
public void setIfAnyGranted(String ifAnyGranted) {
this.ifAnyGranted = ifAnyGranted;
}
public String getIfNotGranted() {
return this.ifNotGranted;
}
public void setIfNotGranted(String ifNotGranted) {
this.ifNotGranted = ifNotGranted;
}
/*------------- Private helper methods -----------------*/
private Collection<? extends GrantedAuthority> getPrincipalAuthorities() {
Authentication currentUser = SecurityContextHolder.getContext().getAuthentication();
if (null == currentUser) {
return Collections.emptyList();
}
return currentUser.getAuthorities();
}
private Set<GrantedAuthority> parseAuthoritiesString(String authorizationsString) {
final Set<GrantedAuthority> requiredAuthorities = new HashSet<GrantedAuthority>();
requiredAuthorities.addAll(AuthorityUtils.commaSeparatedStringToAuthorityList(authorizationsString));
return requiredAuthorities;
}
private Set<GrantedAuthority> retainAll(final Collection<? extends GrantedAuthority> granted,
final Set<GrantedAuthority> required) {
Set<String> grantedRoles = authoritiesToRoles(granted);
Set<String> requiredRoles = authoritiesToRoles(required);
grantedRoles.retainAll(requiredRoles);
return rolesToAuthorities(grantedRoles, granted);
}
private Set<String> authoritiesToRoles(Collection<? extends GrantedAuthority> c) {
Set<String> target = new HashSet<String>();
for (GrantedAuthority authority : c) {
if (null == authority.getAuthority()) {
throw new IllegalArgumentException(
"Cannot process GrantedAuthority objects which return null from getAuthority() - attempting to process "
+ authority.toString());
}
target.add(authority.getAuthority());
}
return target;
}
private Set<GrantedAuthority> rolesToAuthorities(Set<String> grantedRoles, Collection<? extends GrantedAuthority> granted) {
Set<GrantedAuthority> target = new HashSet<GrantedAuthority>();
for (String role : grantedRoles) {
for (GrantedAuthority authority : granted) {
if (authority.getAuthority().equals(role)) {
target.add(authority);
break;
}
}
}
return target;
}
private WebSecurityExpressionHandler getExpressionHandler() throws IOException {
ApplicationContext appContext = WebApplicationContextUtils
.getRequiredWebApplicationContext(getServletContext());
Map<String, WebSecurityExpressionHandler> expressionHandlres = appContext
.getBeansOfType(WebSecurityExpressionHandler.class);
if (expressionHandlres.size() == 0) {
throw new IOException("No visible WebSecurityExpressionHandler instance could be found in the application "
+ "context. There must be at least one in order to support expressions in JSP 'authorize' tags.");
}
return (WebSecurityExpressionHandler) expressionHandlres.values().toArray()[0];
}
private WebInvocationPrivilegeEvaluator getPrivilegeEvaluator() throws IOException {
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
Map<String, WebInvocationPrivilegeEvaluator> wipes = ctx.getBeansOfType(WebInvocationPrivilegeEvaluator.class);
if (wipes.size() == 0) {
throw new IOException(
"No visible WebInvocationPrivilegeEvaluator instance could be found in the application "
+ "context. There must be at least one in order to support the use of URL access checks in 'authorize' tags.");
}
return (WebInvocationPrivilegeEvaluator) wipes.values().toArray()[0];
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2010 the original author or authors.
* Copyright 2004-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,6 +24,8 @@ import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.security.taglibs.authz.AbstractAuthorizeTag;
/**
* A concrete implementation of {@link AbstractAuthorizeTag} for use with standard Facelets rendering technology.
*