001 package hirondelle.web4j.ui.tag;
002
003 import java.util.*;
004 import java.util.logging.*;
005 import java.security.Principal;
006 import hirondelle.web4j.util.Util;
007 import static hirondelle.web4j.util.Consts.PASSES;
008 import static hirondelle.web4j.util.Consts.FAILS;
009 import static hirondelle.web4j.util.Consts.EMPTY_STRING;
010
011 /**
012 Toggle display according to user role.
013
014 <P><span class="highlight">It is important to note that the <em>sole</em> use of this
015 tag does <em>not</em> robustly enforce security constraints.</span>
016 This tag is meant as a "cosmetic convenience" for removing items from JSPs (usually a link).
017 The problem is that a hacker can always construct any given URI manually and send it to the server.
018 Such malicious requests can only be handled robustly by a <tt>security-constraint</tt>
019 defined in <tt>web.xml</tt>.
020
021 <P>Example:
022 <PRE>
023 <w:show ifRole="webmaster,translator">
024 (tag content - typically a link)
025 </w:show>
026 </PRE>
027
028 <P>Example with role specified by negation:
029 <PRE>
030 <w:show ifRoleNot="read-only">
031 (tag content - typically a link)
032 </w:show>
033 </PRE>
034
035 The above two styles are mutually exclusive.
036
037 <P>The body of this class is either echoed as is, or is suppressed entirely.
038 It is echoed only if the user is logged in, <em>and</em> their role is compatible with the
039 specified list. By definition, a user is logged in when <tt>request.getUserPrincipal()</tt>
040 returns a value having content.
041 */
042 public final class ShowForRole extends TagHelper {
043
044 /** Optional, comma-delimited list of accepted roles. */
045 public void setIfRole(String aRoles){
046 fAcceptedRoles = getRoles(aRoles);
047 }
048
049 /** Optional, comma-delimited list of denied roles. */
050 public void setIfRoleNot(String aRoles){
051 fDeniedRoles = getRoles(aRoles);
052 }
053
054 /**
055 One and only one of {@link #setIfRole} and {@link #setIfRoleNot} must be set.
056 */
057 protected void crossCheckAttributes() {
058 if( isAccepting() && isDenying() ) {
059 String message = "Please do not specify BOTH the ifRole, ifRoleNot attributes - just use one. Page Name: " + getPageName();
060 fLogger.severe(message);
061 throw new IllegalArgumentException(message);
062 }
063 if( !isAccepting() && !isDenying() ){
064 String message = "Please specify ONE of the ifRole, ifRoleNot attributes. Page Name : " + getPageName();
065 fLogger.severe(message);
066 throw new IllegalArgumentException(message);
067 }
068 }
069
070 /** See class comment. */
071 @Override protected String getEmittedText(String aOriginalBody) {
072 boolean showBody = false;
073 Principal user = getRequest().getUserPrincipal();
074 if ( user != null ) {
075 if( isAccepting() ){
076 showBody = isUserAccepted();
077 }
078 else if ( isDenying() ){
079 showBody = ! isUserDenied();
080 }
081 else {
082 throw new AssertionError("Unexpected branch. Page Name : " + getPageName());
083 }
084 }
085 return showBody ? aOriginalBody : EMPTY_STRING;
086 }
087
088 // PRIVATE //
089 private List<String> fAcceptedRoles = new ArrayList<String>();
090 private List<String> fDeniedRoles = new ArrayList<String>();
091 private static final String DELIMITER = ",";
092 private static final Logger fLogger = Util.getLogger(ShowForRole.class);
093
094 private List<String> getRoles(String aRawRoles){
095 List<String> result = new ArrayList<String>();
096 StringTokenizer parser = new StringTokenizer( aRawRoles, DELIMITER);
097 while ( parser.hasMoreTokens() ) {
098 result.add( parser.nextToken().trim() );
099 }
100 return result;
101 }
102
103 private boolean isAccepting(){
104 return ! fAcceptedRoles.isEmpty();
105 }
106
107 private boolean isDenying(){
108 return ! fDeniedRoles.isEmpty();
109 }
110
111 private boolean isUserAccepted(){
112 boolean result = FAILS;
113 for (String role: fAcceptedRoles){
114 if ( getRequest().isUserInRole(role) ) {
115 result = PASSES;
116 break;
117 }
118 }
119 return result;
120 }
121
122 private boolean isUserDenied(){
123 boolean result = FAILS;
124 for (String role: fDeniedRoles){
125 if ( getRequest().isUserInRole(role) ) {
126 result = PASSES;
127 break;
128 }
129 }
130 return result;
131 }
132 }