1   package org.mortbay.jetty.client.security;
2   
3   import java.io.IOException;
4   import java.util.HashMap;
5   import java.util.Map;
6   import java.util.StringTokenizer;
7   
8   import javax.servlet.http.HttpServletResponse;
9   
10  import org.mortbay.io.Buffer;
11  import org.mortbay.jetty.HttpHeaders;
12  import org.mortbay.jetty.client.HttpDestination;
13  import org.mortbay.jetty.client.HttpEventListenerWrapper;
14  import org.mortbay.jetty.client.HttpExchange;
15  import org.mortbay.log.Log;
16  import org.mortbay.util.StringUtil;
17  
18  public class SecurityListener extends HttpEventListenerWrapper
19  {
20      private HttpDestination _destination;
21      private HttpExchange _exchange;
22      
23      private int _attempts = 0; // TODO remember to settle on winning solution
24  
25      public SecurityListener(HttpDestination destination, HttpExchange ex)
26      {
27          // Start of sending events through to the wrapped listener
28          // Next decision point is the onResponseStatus
29          super(ex.getEventListener(),true);
30          _destination=destination;
31          _exchange=ex;
32      }
33      
34      
35      /**
36       * scrapes an authentication type from the authString
37       * 
38       * @param authString
39       * @return
40       */
41      protected String scrapeAuthenticationType( String authString )
42      {
43          String authType;
44  
45          if ( authString.indexOf( " " ) == -1 )
46          {
47              authType = authString.toString().trim();
48          }
49          else
50          {
51              String authResponse = authString.toString();
52              authType = authResponse.substring( 0, authResponse.indexOf( " " ) ).trim();
53          }
54          return authType;
55      }
56      
57      /**
58       * scrapes a set of authentication details from the authString
59       * 
60       * @param authString
61       * @return
62       */
63      protected Map<String, String> scrapeAuthenticationDetails( String authString )
64      {
65          Map<String, String> authenticationDetails = new HashMap<String, String>();
66          authString = authString.substring( authString.indexOf( " " ) + 1, authString.length() );
67          StringTokenizer strtok = new StringTokenizer( authString, ",");
68          
69          while ( strtok.hasMoreTokens() )
70          {
71              String[] pair = strtok.nextToken().split( "=" );
72              if ( pair.length == 2 )
73              {
74                  String itemName = pair[0].trim();
75                  String itemValue = pair[1].trim();
76                  
77                  itemValue = StringUtil.unquote( itemValue );
78                  
79                  authenticationDetails.put( itemName, itemValue );
80              }
81              else
82              {
83                  throw new IllegalArgumentException( "unable to process authentication details" );
84              }      
85          }
86          return authenticationDetails;
87      }
88  
89      @Override
90      public void onResponseStatus( Buffer version, int status, Buffer reason )
91          throws IOException
92      {
93          System.err.println("SecurityListener:Response Status: " + status );
94          if ( status == HttpServletResponse.SC_UNAUTHORIZED && _attempts<_destination.getHttpClient().maxRetries()) 
95          {
96              // Let's absorb events until we have done some retries
97              setDelegating(false);
98          }
99          else 
100         {
101             setDelegating(true);
102         }
103         super.onResponseStatus(version,status,reason);
104     }
105 
106 
107     @Override
108     public void onResponseHeader( Buffer name, Buffer value )
109         throws IOException
110     {
111         System.err.println( "SecurityListener:Header: " + name.toString() + " / " + value.toString() );
112         if (!isDelegating())
113         {
114             int header = HttpHeaders.CACHE.getOrdinal(name);
115             switch (header)
116             {
117                 case HttpHeaders.WWW_AUTHENTICATE_ORDINAL:
118 
119                     // TODO don't hard code this bit.
120                     String authString = value.toString();
121                     String type = scrapeAuthenticationType( authString );                  
122 
123                     // TODO maybe avoid this map creation
124                     Map<String,String> details = scrapeAuthenticationDetails( authString );
125                     String pathSpec="/"; // TODO work out the real path spec
126                     SecurityRealmResolver realmResolver = _destination.getHttpClient().getSecurityRealmResolver();
127                     
128                     if ( realmResolver == null )
129                     {
130                         break;
131                     }
132                     
133                     SecurityRealm realm = realmResolver.getRealm( details.get("realm"), _destination, pathSpec ); // TODO work our realm correctly 
134                     
135                     if ( realm == null )
136                     {
137                         Log.warn( "Unknown Security Realm: " + details.get("realm") );
138                     }
139                     else if ("digest".equalsIgnoreCase(type))
140                     {
141                         _destination.addAuthorization("/",new DigestAuthentication(realm,details));
142                         
143                     }
144                     else if ("basic".equalsIgnoreCase(type))
145                     {
146                         _destination.addAuthorization(pathSpec,new BasicAuthentication(realm));
147                     }
148                     
149                     break;
150             }
151         }
152         super.onResponseHeader(name,value);
153     }
154     
155     @Override
156     public void onResponseComplete() throws IOException
157     {
158         if (!isDelegating())
159             _destination.resend(_exchange);
160         else
161             super.onResponseComplete();
162     }
163 
164     @Override
165     public void onRetry()
166     {
167         _attempts++;
168         setDelegating(true);
169         super.onRetry();
170     }  
171     
172     
173 }