From d9fa61c89292fcd0b631b7a1d0c7ed34dee9252d Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 31 Aug 2010 10:12:15 +0000 Subject: [PATCH] SWF-1333 Add Spring Security facelets tag library --- spring-faces/.classpath | 2 + spring-faces/ivy.xml | 2 + .../faces/security/AbstractAuthorizeTag.java | 334 ++++++++++++++++++ .../faces/security/FaceletsAuthorizeTag.java | 93 +++++ .../security/FaceletsAuthorizeTagHandler.java | 88 +++++ .../security/FaceletsAuthorizeTagUtils.java | 73 ++++ .../security/Jsf12FaceletsAuthorizeTag.java | 95 +++++ .../Jsf12FaceletsAuthorizeTagHandler.java | 90 +++++ .../Jsf12FaceletsAuthorizeTagUtils.java | 73 ++++ .../security/SpringSecurityTagLibrary.java | 61 ++++ .../support/RequestLoggingPhaseListener.java | 12 +- .../META-INF/springsecurity.taglib.xml | 7 + 12 files changed, 924 insertions(+), 6 deletions(-) create mode 100644 spring-faces/src/main/java/org/springframework/faces/security/AbstractAuthorizeTag.java create mode 100644 spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTag.java create mode 100644 spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTagHandler.java create mode 100644 spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTagUtils.java create mode 100644 spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTag.java create mode 100644 spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTagHandler.java create mode 100644 spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTagUtils.java create mode 100644 spring-faces/src/main/java/org/springframework/faces/security/SpringSecurityTagLibrary.java create mode 100644 spring-faces/src/main/resources/META-INF/springsecurity.taglib.xml diff --git a/spring-faces/.classpath b/spring-faces/.classpath index 8c8ac035..d73b9c86 100644 --- a/spring-faces/.classpath +++ b/spring-faces/.classpath @@ -29,6 +29,8 @@ + + diff --git a/spring-faces/ivy.xml b/spring-faces/ivy.xml index 26be6c28..326223d4 100644 --- a/spring-faces/ivy.xml +++ b/spring-faces/ivy.xml @@ -37,6 +37,8 @@ + + diff --git a/spring-faces/src/main/java/org/springframework/faces/security/AbstractAuthorizeTag.java b/spring-faces/src/main/java/org/springframework/faces/security/AbstractAuthorizeTag.java new file mode 100644 index 00000000..38ef73bc --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/security/AbstractAuthorizeTag.java @@ -0,0 +1,334 @@ +/* + * Copyright 2004-2010 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; + +/** + *

+ * A base class for an <authorize> tag used to make Spring Security based authorization decisions. + *

+ * + *

+ * 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. + *

+ * + * @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 <authorize> tag attributes. The following are valid + * combinations of attributes: + *
    + *
  • access
  • + *
  • url, method
  • + *
  • ifAllGranted, ifAnyGranted, ifNotGranted
  • + *
+ * 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 granted = getPrincipalAuthorities(); + + if (hasTextAllGranted) { + if (!granted.containsAll(parseAuthoritiesString(getIfAllGranted()))) { + return false; + } + } + + if (hasTextAnyGranted) { + Set grantedCopy = retainAll(granted, parseAuthoritiesString(getIfAnyGranted())); + if (grantedCopy.isEmpty()) { + return false; + } + } + + if (hasTextNotGranted) { + Set 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) { + throw new IOException(e); + } + + 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 access; + } + + public void setAccess(String access) { + this.access = access; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = (method != null) ? method.toUpperCase() : null; + } + + public String getIfAllGranted() { + return ifAllGranted; + } + + public void setIfAllGranted(String ifAllGranted) { + this.ifAllGranted = ifAllGranted; + } + + public String getIfAnyGranted() { + return ifAnyGranted; + } + + public void setIfAnyGranted(String ifAnyGranted) { + this.ifAnyGranted = ifAnyGranted; + } + + public String getIfNotGranted() { + return ifNotGranted; + } + + public void setIfNotGranted(String ifNotGranted) { + this.ifNotGranted = ifNotGranted; + } + + /*------------- Private helper methods -----------------*/ + + private Collection getPrincipalAuthorities() { + Authentication currentUser = SecurityContextHolder.getContext().getAuthentication(); + if (null == currentUser) { + return Collections.emptyList(); + } + return currentUser.getAuthorities(); + } + + private Set parseAuthoritiesString(String authorizationsString) { + final Set requiredAuthorities = new HashSet(); + requiredAuthorities.addAll(AuthorityUtils.commaSeparatedStringToAuthorityList(authorizationsString)); + return requiredAuthorities; + } + + private Set retainAll(final Collection granted, + final Set required) { + Set grantedRoles = authoritiesToRoles(granted); + Set requiredRoles = authoritiesToRoles(required); + grantedRoles.retainAll(requiredRoles); + + return rolesToAuthorities(grantedRoles, granted); + } + + private Set authoritiesToRoles(Collection c) { + Set target = new HashSet(); + 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 rolesToAuthorities(Set grantedRoles, Collection granted) { + Set target = new HashSet(); + 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 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 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]; + } +} diff --git a/spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTag.java b/spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTag.java new file mode 100644 index 00000000..9313b24d --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTag.java @@ -0,0 +1,93 @@ +/* + * Copyright 2004-2010 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 javax.el.ValueExpression; +import javax.faces.context.FacesContext; +import javax.faces.view.facelets.FaceletContext; +import javax.faces.view.facelets.TagAttribute; +import javax.faces.view.facelets.TagHandler; +import javax.servlet.ServletContext; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +/** + * A concrete implementation of {@link AbstractAuthorizeTag} for use with standard Facelets rendering technology (JSF 2 + * or higher). + * + * @author Rossen Stoyanchev + * @since 2.2.0 + */ +public class FaceletsAuthorizeTag extends AbstractAuthorizeTag { + + /** + * A class constructor for use in a {@link TagHandler}. Accepts all possible tag attributes as {@link TagAttribute} + * instances. The constructor extracts the attribute values by evaluating them as Unified EL expressions. This + * excludes the access attribute, which is expected to be a Spring EL expression. + * + * @param faceletContext the current FaceletContext + * @param access the access attribute or null + * @param url the url attribute or null + * @param method the method attribute or null + * @param ifAllGranted the ifAllGranted attribute or null + * @param ifAnyGranted the ifAnyGranted attribute or null + * @param ifNotGranted the ifNotGranted attribute or null + */ + public FaceletsAuthorizeTag(FaceletContext faceletContext, TagAttribute access, TagAttribute url, + TagAttribute method, TagAttribute ifAllGranted, TagAttribute ifAnyGranted, TagAttribute ifNotGranted) { + setAccess(getAttributeValue(faceletContext, access, false)); + setUrl(getAttributeValue(faceletContext, url, true)); + setMethod(getAttributeValue(faceletContext, method, true)); + setIfAllGranted(getAttributeValue(faceletContext, ifAllGranted, true)); + setIfAnyGranted(getAttributeValue(faceletContext, ifAnyGranted, true)); + setIfNotGranted(getAttributeValue(faceletContext, ifNotGranted, true)); + } + + /** + * A default constructor. Callers of this constructor are responsible for setting one or more of the tag attributes + * in {@link AbstractAuthorizeTag}. + */ + public FaceletsAuthorizeTag() { + } + + protected ServletRequest getRequest() { + return (ServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); + } + + protected ServletResponse getResponse() { + return (ServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse(); + } + + protected ServletContext getServletContext() { + return (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext(); + } + + /*---- Pirvate helper methods ----*/ + + private String getAttributeValue(FaceletContext faceletContext, TagAttribute tagAttribute, boolean evaluate) { + String value = null; + if (tagAttribute != null) { + if (evaluate) { + ValueExpression expression = tagAttribute.getValueExpression(faceletContext, String.class); + value = (String) expression.getValue(faceletContext.getFacesContext().getELContext()); + } else { + value = tagAttribute.getValue(); + } + } + return value; + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTagHandler.java b/spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTagHandler.java new file mode 100644 index 00000000..4ed8fa91 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTagHandler.java @@ -0,0 +1,88 @@ +/* + * Copyright 2004-2010 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 javax.faces.component.UIComponent; +import javax.faces.view.facelets.FaceletContext; +import javax.faces.view.facelets.TagAttribute; +import javax.faces.view.facelets.TagConfig; +import javax.faces.view.facelets.TagHandler; + +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * A standard Facelets {@link TagHandler} for performing Spring Security authorization decisions. The tag supports the + * following combinations attributes for authorization: + *
    + *
  • access
  • + *
  • url, method
  • + *
  • ifAllGranted, ifAnyGranted, ifNotGranted
  • + *
+ * The var attribute can be used to store the result of the authorization decision for later use in the view. + * + * @author Rossen Stoyanchev + * @since 2.2.0 + * @see FaceletsAuthorizeTag + */ +public class FaceletsAuthorizeTagHandler extends TagHandler { + + private final TagAttribute access; + private final TagAttribute url; + private final TagAttribute method; + private final TagAttribute ifAllGranted; + private final TagAttribute ifAnyGranted; + private final TagAttribute ifNotGranted; + private final TagAttribute var; + + /** + * @see TagHandler#TagHandler(TagConfig) + */ + public FaceletsAuthorizeTagHandler(TagConfig config) { + super(config); + this.access = this.getAttribute("access"); + this.url = this.getAttribute("url"); + this.method = this.getAttribute("method"); + this.ifAllGranted = this.getAttribute("ifAllGranted"); + this.ifAnyGranted = this.getAttribute("ifAnyGranted"); + this.ifNotGranted = this.getAttribute("ifNotGranted"); + this.var = this.getAttribute("var"); + } + + /** + * @see TagHandler#apply(FaceletContext, UIComponent) + */ + public void apply(FaceletContext faceletContext, UIComponent parent) throws IOException { + if (SecurityContextHolder.getContext().getAuthentication() == null) { + return; + } + + FaceletsAuthorizeTag authorizeTag = new FaceletsAuthorizeTag(faceletContext, access, url, method, ifAllGranted, + ifAnyGranted, ifNotGranted); + + boolean isAuthorized = authorizeTag.authorize(); + + if (isAuthorized) { + this.nextHandler.apply(faceletContext, parent); + } + + if (this.var != null) { + faceletContext.setAttribute(var.getValue(faceletContext), Boolean.valueOf(isAuthorized)); + } + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTagUtils.java b/spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTagUtils.java new file mode 100644 index 00000000..250e3467 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/security/FaceletsAuthorizeTagUtils.java @@ -0,0 +1,73 @@ +/* + * Copyright 2004-2010 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; + +/** + * This class provides static methods that are registered as EL functions and available for use in Unified EL + * expressions in standard Facelets views (JSF 2 or higher). + * + * @author Rossen Stoyanchev + * @since 2.2.0 + */ +public abstract class FaceletsAuthorizeTagUtils { + + /** + * Returns true if the user has all of of the given authorities. + * + * @param authorities a comma-separated list of user authorities. + */ + public static boolean areAllGranted(String authorities) throws IOException { + FaceletsAuthorizeTag authorizeTag = new FaceletsAuthorizeTag(); + authorizeTag.setIfAllGranted(authorities); + return authorizeTag.authorizeUsingGrantedAuthorities(); + } + + /** + * Returns true if the user has any of the given authorities. + * + * @param authorities a comma-separated list of user authorities. + */ + public static boolean areAnyGranted(String authorities) throws IOException { + FaceletsAuthorizeTag authorizeTag = new FaceletsAuthorizeTag(); + authorizeTag.setIfAnyGranted(authorities); + return authorizeTag.authorizeUsingGrantedAuthorities(); + } + + /** + * Returns true if the user does not have any of the given authorities. + * + * @param authorities a comma-separated list of user authorities. + */ + public static boolean areNotGranted(String authorities) throws IOException { + FaceletsAuthorizeTag authorizeTag = new FaceletsAuthorizeTag(); + authorizeTag.setIfNotGranted(authorities); + return authorizeTag.authorizeUsingGrantedAuthorities(); + } + + /** + * Returns true if the user is allowed to access the given URL and HTTP method combination. The HTTP method is + * optional and case insensitive. + */ + public static boolean isAllowed(String url, String method) throws IOException { + FaceletsAuthorizeTag authorizeTag = new FaceletsAuthorizeTag(); + authorizeTag.setUrl(url); + authorizeTag.setMethod(method); + return authorizeTag.authorizeUsingUrlCheck(); + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTag.java b/spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTag.java new file mode 100644 index 00000000..8f6e04d8 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTag.java @@ -0,0 +1,95 @@ +/* + * Copyright 2004-2010 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 javax.el.ValueExpression; +import javax.faces.context.FacesContext; +import javax.faces.view.facelets.TagHandler; +import javax.servlet.ServletContext; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; + +import com.sun.facelets.FaceletContext; +import com.sun.facelets.tag.TagAttribute; + +/** + * A concrete implementation of {@link AbstractAuthorizeTag} for use with Facelets rendering technology in JSF 1.2 + * environments. + * + * @author Rossen Stoyanchev + * @since 2.2.0 + * @see FaceletsAuthorizeTag + */ +public class Jsf12FaceletsAuthorizeTag extends AbstractAuthorizeTag { + + /** + * A class constructor for use in a {@link TagHandler}. Accepts all possible tag attributes as {@link TagAttribute} + * instances. The constructor extracts the attribute values by evaluating them as Unified EL expressions. This + * excludes the access attribute, which is expected to be a Spring EL expression. + * + * @param faceletContext the current FaceletContext + * @param access the access attribute or null + * @param url the url attribute or null + * @param method the method attribute or null + * @param ifAllGranted the ifAllGranted attribute or null + * @param ifAnyGranted the ifAnyGranted attribute or null + * @param ifNotGranted the ifNotGranted attribute or null + */ + public Jsf12FaceletsAuthorizeTag(FaceletContext faceletContext, TagAttribute access, TagAttribute url, + TagAttribute method, TagAttribute ifAllGranted, TagAttribute ifAnyGranted, TagAttribute ifNotGranted) { + setAccess(getAttributeValue(faceletContext, access, false)); + setUrl(getAttributeValue(faceletContext, url, true)); + setMethod(getAttributeValue(faceletContext, method, true)); + setIfAllGranted(getAttributeValue(faceletContext, ifAllGranted, true)); + setIfAnyGranted(getAttributeValue(faceletContext, ifAnyGranted, true)); + setIfNotGranted(getAttributeValue(faceletContext, ifNotGranted, true)); + } + + /** + * A default constructor. Callers of this constructor are responsible for setting one or more of the tag attributes + * in {@link AbstractAuthorizeTag}. + */ + public Jsf12FaceletsAuthorizeTag() { + } + + protected ServletRequest getRequest() { + return (ServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest(); + } + + protected ServletResponse getResponse() { + return (ServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse(); + } + + protected ServletContext getServletContext() { + return (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext(); + } + + /*---- Pirvate helper methods ----*/ + + private String getAttributeValue(FaceletContext faceletContext, TagAttribute tagAttribute, boolean evaluate) { + String value = null; + if (tagAttribute != null) { + if (evaluate) { + ValueExpression expression = tagAttribute.getValueExpression(faceletContext, String.class); + value = (String) expression.getValue(faceletContext.getFacesContext().getELContext()); + } else { + value = tagAttribute.getValue(); + } + } + return value; + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTagHandler.java b/spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTagHandler.java new file mode 100644 index 00000000..180ab85a --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTagHandler.java @@ -0,0 +1,90 @@ +/* + * Copyright 2004-2010 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 javax.faces.component.UIComponent; + +import org.springframework.security.core.context.SecurityContextHolder; + +import com.sun.facelets.FaceletContext; +import com.sun.facelets.tag.TagAttribute; +import com.sun.facelets.tag.TagConfig; +import com.sun.facelets.tag.TagHandler; + +/** + * A JSF 1.2 Facelets {@link TagHandler} for performing Spring Security authorization decisions. The tag supports the + * following combinations attributes for authorization: + *
    + *
  • access
  • + *
  • url, method
  • + *
  • ifAllGranted, ifAnyGranted, ifNotGranted
  • + *
+ * The var attribute can be used to store the result of the authorization decision for later use in the view. + * + * @author Rossen Stoyanchev + * @since 2.2.0 + * @see Jsf12FaceletsAuthorizeTag + */ +public class Jsf12FaceletsAuthorizeTagHandler extends TagHandler { + + private final TagAttribute access; + private final TagAttribute url; + private final TagAttribute method; + private final TagAttribute ifAllGranted; + private final TagAttribute ifAnyGranted; + private final TagAttribute ifNotGranted; + private final TagAttribute var; + + /** + * @see TagHandler#TagHandler(TagConfig) + */ + public Jsf12FaceletsAuthorizeTagHandler(TagConfig config) { + super(config); + this.access = this.getAttribute("access"); + this.url = this.getAttribute("url"); + this.method = this.getAttribute("method"); + this.ifAllGranted = this.getAttribute("ifAllGranted"); + this.ifAnyGranted = this.getAttribute("ifAnyGranted"); + this.ifNotGranted = this.getAttribute("ifNotGranted"); + this.var = this.getAttribute("var"); + } + + /** + * @see TagHandler#apply(FaceletContext, UIComponent) + */ + public void apply(FaceletContext faceletContext, UIComponent parent) throws IOException { + if (SecurityContextHolder.getContext().getAuthentication() == null) { + return; + } + + Jsf12FaceletsAuthorizeTag authorizeTag = new Jsf12FaceletsAuthorizeTag(faceletContext, access, url, method, + ifAllGranted, ifAnyGranted, ifNotGranted); + + boolean isAuthorized = authorizeTag.authorize(); + + if (isAuthorized) { + this.nextHandler.apply(faceletContext, parent); + } + + if (this.var != null) { + faceletContext.setAttribute(var.getValue(faceletContext), Boolean.valueOf(isAuthorized)); + } + + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTagUtils.java b/spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTagUtils.java new file mode 100644 index 00000000..7f98e5ca --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/security/Jsf12FaceletsAuthorizeTagUtils.java @@ -0,0 +1,73 @@ +/* + * Copyright 2004-2010 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; + +/** + * This class provides static methods that are registered as EL functions and available for use in Unified EL + * expressions in JSF 1.2 Facelets views. + * + * @author Rossen Stoyanchev + * @since 2.2.0 + */ +public abstract class Jsf12FaceletsAuthorizeTagUtils { + + /** + * Returns true if the user has all of of the given authorities. + * + * @param authorities a comma-separated list of user authorities. + */ + public static boolean areAllGranted(String authorities) throws IOException { + Jsf12FaceletsAuthorizeTag authorizeTag = new Jsf12FaceletsAuthorizeTag(); + authorizeTag.setIfAllGranted(authorities); + return authorizeTag.authorizeUsingGrantedAuthorities(); + } + + /** + * Returns true if the user has any of the given authorities. + * + * @param authorities a comma-separated list of user authorities. + */ + public static boolean areAnyGranted(String authorities) throws IOException { + Jsf12FaceletsAuthorizeTag authorizeTag = new Jsf12FaceletsAuthorizeTag(); + authorizeTag.setIfAnyGranted(authorities); + return authorizeTag.authorizeUsingGrantedAuthorities(); + } + + /** + * Returns true if the user does not have any of the given authorities. + * + * @param authorities a comma-separated list of user authorities. + */ + public static boolean areNotGranted(String authorities) throws IOException { + Jsf12FaceletsAuthorizeTag authorizeTag = new Jsf12FaceletsAuthorizeTag(); + authorizeTag.setIfNotGranted(authorities); + return authorizeTag.authorizeUsingGrantedAuthorities(); + } + + /** + * Returns true if the user is allowed to access the given URL and HTTP method combination. The HTTP method is + * optional and case insensitive. + */ + public static boolean isAllowed(String url, String method) throws IOException { + Jsf12FaceletsAuthorizeTag authorizeTag = new Jsf12FaceletsAuthorizeTag(); + authorizeTag.setUrl(url); + authorizeTag.setMethod(method); + return authorizeTag.authorizeUsingUrlCheck(); + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/security/SpringSecurityTagLibrary.java b/spring-faces/src/main/java/org/springframework/faces/security/SpringSecurityTagLibrary.java new file mode 100644 index 00000000..f875e538 --- /dev/null +++ b/spring-faces/src/main/java/org/springframework/faces/security/SpringSecurityTagLibrary.java @@ -0,0 +1,61 @@ +/* + * Copyright 2004-2010 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.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.springframework.faces.webflow.JsfRuntimeInformation; + +import com.sun.faces.facelets.tag.AbstractTagLibrary; + +/** + * Registers a tag handler for the <authorize> tag and several EL functions that can be used on any component that + * accepts EL expressions in its attributes. For details on the EL functions see {@link FaceletsAuthorizeTagUtils} or + * {@link Jsf12FaceletsAuthorizeTagUtils} depending on your environment. + * + * @author Rossen Stoyanchev + * @since 2.2.0 + * @see FaceletsAuthorizeTagHandler + * @see FaceletsAuthorizeTagUtils + */ +public class SpringSecurityTagLibrary extends AbstractTagLibrary { + + public static final String NAMESPACE = "http://www.springframework.org/security/tags"; + + public SpringSecurityTagLibrary() { + super(NAMESPACE); + + if (JsfRuntimeInformation.isAtLeastJsf20()) { + this.addTagHandler("authorize", FaceletsAuthorizeTagHandler.class); + } else { + this.addTagHandler("authorize", Jsf12FaceletsAuthorizeTagHandler.class); + } + + try { + Method[] methods = (JsfRuntimeInformation.isAtLeastJsf20()) ? FaceletsAuthorizeTagUtils.class.getMethods() + : Jsf12FaceletsAuthorizeTagUtils.class.getMethods(); + for (int i = 0; i < methods.length; i++) { + if (Modifier.isStatic(methods[i].getModifiers())) { + this.addFunction(methods[i].getName(), methods[i]); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/spring-faces/src/main/java/org/springframework/faces/support/RequestLoggingPhaseListener.java b/spring-faces/src/main/java/org/springframework/faces/support/RequestLoggingPhaseListener.java index 6a87eff2..e74541f4 100644 --- a/spring-faces/src/main/java/org/springframework/faces/support/RequestLoggingPhaseListener.java +++ b/spring-faces/src/main/java/org/springframework/faces/support/RequestLoggingPhaseListener.java @@ -1,10 +1,10 @@ /* - * Copyright 2004-2008 the original authorimport javax.faces.event.PhaseEvent; -import javax.faces.event.PhaseId; -import javax.faces.event.PhaseListener; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; + * Copyright 2004-2008 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 diff --git a/spring-faces/src/main/resources/META-INF/springsecurity.taglib.xml b/spring-faces/src/main/resources/META-INF/springsecurity.taglib.xml new file mode 100644 index 00000000..5455cb2f --- /dev/null +++ b/spring-faces/src/main/resources/META-INF/springsecurity.taglib.xml @@ -0,0 +1,7 @@ + + + + org.springframework.faces.security.SpringSecurityTagLibrary + \ No newline at end of file