001 package hirondelle.web4j.security; 002 003 import static hirondelle.web4j.util.Consts.NOT_FOUND; 004 import hirondelle.web4j.action.Operation; 005 import hirondelle.web4j.readconfig.Config; 006 import hirondelle.web4j.request.RequestParser; 007 import hirondelle.web4j.util.Util; 008 import hirondelle.web4j.util.WebUtil; 009 010 import java.util.List; 011 012 /** 013 Default implementation of {@link UntrustedProxyForUserId}. 014 015 <P>This implementation depends on settings in <tt>web.xml</tt>, which are read in on startup. 016 Later, each request URL is parsed by {@link #usesUntrustedIdentifier(RequestParser)}, 017 and an attempt is made to find a match to the aforementioned settings in <tt>web.xml</tt>. 018 019 <P>This class uses settings in <tt>web.xml</tt> to define requests having ownership constraints that use an untrusted proxy 020 for the user id. It uses a <i>roughly</i> similar style as used for role-based constraints. 021 Here is an example of a number of several such ownership constraints defined in <tt>web.xml</tt>:<PRE><init-param> 022 <description> 023 Operations having an ownership constraint that uses an untrusted identifier. 024 </description> 025 <param-name>UntrustedProxyForUserId</param-name> 026 <param-value> 027 FoodAction.* 028 VacationAction.add 029 VacationAction.delete 030 </param-value> 031 </init-param> 032 </PRE> 033 034 <P>Each line is treated as a separate constraint, one per line. You can define as many as required. 035 The period character separates the 'noun' (the Action) from the 'verb' (the {@link Operation}). 036 037 <P>The special '*' character refers to all verbs/operations attached to a given noun/action. 038 */ 039 public final class UntrustedProxyForUserIdImpl implements UntrustedProxyForUserId { 040 041 /** 042 Return <tt>true</tt> only if the given request matches one of the items defined by the <tt>UntrustedProxyForUserId</tt> setting 043 in <tt>web.xml</tt>. 044 045 <P>For example, given the URL : 046 <PRE>'.../VacationAction.list?X=Y'</PRE> 047 this method will parse the URL into a 'noun' and a 'verb' : 048 <PRE>noun: 'VacationAction' 049 verb: 'list'</PRE> 050 051 It will then compare the noun-and-verb to the settings defined in <tt>web.xml</tt>. 052 If there's a match, then this method returns <tt>true</tt>. 053 */ 054 public boolean usesUntrustedIdentifier(RequestParser aRequestParser) { 055 boolean result = false; //by default, there is no ownership constraint 056 String noun = extractNoun(aRequestParser); //WebUtil needs response too! servlet path + path info? 057 if( isRestrictedRequest(noun) ){ 058 List<String> restrictedVerbs = fConfig.getUntrustedProxyForUserId().get(noun); 059 if ( hasAllOperationsRestricted(restrictedVerbs) ) { 060 result = true; 061 } 062 else { 063 String verb = extractVerb(aRequestParser); 064 if ( restrictedVerbs.contains(verb) ) { 065 result = true; 066 } 067 } 068 } 069 return result; 070 } 071 072 /** Special character denoting all operations/verbs. */ 073 public static final String ALL_OPERATIONS = "*"; 074 075 // PRIVATE 076 077 private Config fConfig = new Config(); 078 079 private boolean isRestrictedRequest(String aNoun){ 080 return fConfig.getUntrustedProxyForUserId().containsKey(aNoun); 081 } 082 083 private boolean hasAllOperationsRestricted(List<String> aVerbs){ 084 return aVerbs.contains(ALL_OPERATIONS); 085 } 086 087 /** 088 For the example URL '.../BlahAction.do?X=Y', this method returns 'BlahAction' as the noun. 089 Relies on the presence of '/' and '.' characters. 090 */ 091 private String extractNoun(RequestParser aRequestParser){ 092 String uri = getURI(aRequestParser); 093 int firstPeriod = uri.indexOf("."); 094 if( firstPeriod == NOT_FOUND ) { 095 throw new RuntimeException("Cannot find '.' character in URL: " + Util.quote(uri)); 096 } 097 int lastSlash = uri.lastIndexOf("/"); 098 if( lastSlash == NOT_FOUND ) { 099 throw new RuntimeException("Cannot find '/' character in URL: " + Util.quote(uri)); 100 } 101 return uri.substring(lastSlash + 1, firstPeriod); 102 } 103 104 /** Return the part of the URL after the '.' character, but before any '?' character (if present). */ 105 private String extractVerb(RequestParser aRequestParser){ 106 return WebUtil.getFileExtension(getURI(aRequestParser)); 107 } 108 109 private String getURI(RequestParser aRequestParser){ 110 return aRequestParser.getRequest().getRequestURI(); 111 } 112 }