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 the user's role. 013 014 <P><span class="highlight">It's 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 show tag content only if the user is logged in, 025 and has at least 1 of the specified roles 026 </w:show> 027 </PRE> 028 029 Example with role specified by negation: 030 <PRE> 031 <w:show ifRoleNot="read-only"> 032 show tag content only if the user is logged in, 033 and has none of the specified roles 034 </w:show> 035 </PRE> 036 037 Example with logic attached not to role, but simply whether or not the user has logged in: 038 <PRE> 039 <w:show ifLoggedIn="true"> 040 show tag content only if the user is logged in 041 </w:show> 042 043 <w:show ifLoggedIn="false"> 044 show tag content only if the user is not logged in 045 </w:show></pre> 046 047 The above styles are all mutually exclusive. You can specify only 1 attribute at a time with this tag. 048 049 <P>The body of this class is either echoed as is, or is suppressed entirely. 050 051 <P>By definition (in the servlet specification), a user is logged in when <tt>request.getUserPrincipal()</tt> 052 returns a value having content. When a user is logged in, the container can assign 053 1 or more roles to the user. Roles are only assigned after a successful login. 054 */ 055 public final class ShowForRole extends TagHelper { 056 057 /** Optional, comma-delimited list of accepted roles. */ 058 public void setIfRole(String aRoles){ 059 fAcceptedRoles = getRoles(aRoles); 060 } 061 062 /** Optional, comma-delimited list of denied roles. */ 063 public void setIfRoleNot(String aRoles){ 064 fDeniedRoles = getRoles(aRoles); 065 } 066 067 /** 068 Optional, simple flag indicating if user is or is not logged in. 069 @param aFlag - see {@link Util#parseBoolean(String)} for the list of accepted values. 070 */ 071 public void setIfLoggedIn(String aFlag){ 072 fIfLoggedIn = Util.parseBoolean(aFlag); //null if the flag is null 073 } 074 075 /** 076 One and only one of the {@link #setIfRole}, {@link #setIfRoleNot}, or 077 {@link #setIfLoggedIn(String)} attributes must be set. 078 */ 079 protected void crossCheckAttributes() { 080 int numAttrsSpecified = 0; 081 if (isModeAcceptingRoles()) ++numAttrsSpecified; 082 if (isModeDenyingRoles()) ++numAttrsSpecified; 083 if (isModeLogin()) ++numAttrsSpecified; 084 085 if(numAttrsSpecified != 1) { 086 String message = "Please specify 1 (and only 1) attribute for this tag: ifRole, ifRoleNot, or ifLoggedIn. Page Name: " + getPageName(); 087 fLogger.severe(message); 088 throw new IllegalArgumentException(message); 089 } 090 } 091 092 /** See class comment. */ 093 @Override protected String getEmittedText(String aOriginalBody) { 094 boolean showBody = false; 095 Principal user = getRequest().getUserPrincipal(); 096 boolean isCurrentlyLoggedIn = (user != null); 097 098 if (isModeAcceptingRoles() && isCurrentlyLoggedIn){ 099 showBody = hasOneOrMoreOfThe(fAcceptedRoles); 100 } 101 else if (isModeDenyingRoles() && isCurrentlyLoggedIn){ 102 showBody = ! hasOneOrMoreOfThe(fDeniedRoles); 103 } 104 else if (isModeLogin()){ 105 showBody = fIfLoggedIn ? isCurrentlyLoggedIn : ! isCurrentlyLoggedIn; 106 } 107 return showBody ? aOriginalBody : EMPTY_STRING; 108 } 109 110 // PRIVATE 111 private List<String> fAcceptedRoles = new ArrayList<String>(); 112 private List<String> fDeniedRoles = new ArrayList<String>(); 113 /** The null-ity of this field is needed to indicate the 'unspecified' state. */ 114 private Boolean fIfLoggedIn; 115 116 private static final String DELIMITER = ","; 117 private static final Logger fLogger = Util.getLogger(ShowForRole.class); 118 119 private List<String> getRoles(String aRawRoles){ 120 List<String> result = new ArrayList<String>(); 121 StringTokenizer parser = new StringTokenizer( aRawRoles, DELIMITER); 122 while ( parser.hasMoreTokens() ) { 123 result.add( parser.nextToken().trim() ); 124 } 125 return result; 126 } 127 128 private boolean isModeAcceptingRoles(){ 129 return ! fAcceptedRoles.isEmpty(); 130 } 131 132 private boolean isModeDenyingRoles(){ 133 return ! fDeniedRoles.isEmpty(); 134 } 135 136 private boolean isModeLogin(){ 137 return fIfLoggedIn != null; 138 } 139 140 private boolean hasOneOrMoreOfThe(List<String> aRoles){ 141 boolean result = FAILS; 142 for (String role: aRoles){ 143 if ( getRequest().isUserInRole(role) ) { 144 result = PASSES; 145 break; 146 } 147 } 148 return result; 149 } 150 }