View Javadoc

    1   //========================================================================
    2   //Copyright 2008 Mort Bay Consulting Pty. Ltd.
    3   //------------------------------------------------------------------------
    4   //Licensed under the Apache License, Version 2.0 (the "License");
    5   //you may not use this file except in compliance with the License.
    6   //You may obtain a copy of the License at
    7   //http://www.apache.org/licenses/LICENSE-2.0
    8   //Unless required by applicable law or agreed to in writing, software
    9   //distributed under the License is distributed on an "AS IS" BASIS,
    10  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  //See the License for the specific language governing permissions and
    12  //limitations under the License.
    13  //========================================================================
    14
    15  package org.mortbay.jetty.client.security;
    16
    17  import java.io.IOException;
    18  import java.util.HashMap;
    19  import java.util.Map;
    20  import java.util.StringTokenizer;
    21
    22  import javax.servlet.http.HttpServletResponse;
    23
    24  import org.mortbay.io.Buffer;
    25  import org.mortbay.jetty.HttpHeaders;
    26  import org.mortbay.jetty.client.HttpDestination;
    27  import org.mortbay.jetty.client.HttpEventListenerWrapper;
    28  import org.mortbay.jetty.client.HttpExchange;
    29  import org.mortbay.log.Log;
    30  import org.mortbay.util.StringUtil;
    31
    32
    33  /**
    34   * SecurityListener
    35   *
    36   * Allow for insertion of security dialog when performing an
    37   * HttpExchange.
    38   */
    39  public class SecurityListener extends HttpEventListenerWrapper
    40  {
    41      private HttpDestination _destination;
    42      private HttpExchange _exchange;
    43      private boolean _requestComplete;
    44      private boolean _responseComplete;
    45      private boolean _needIntercept;
    46
    47      private int _attempts = 0; // TODO remember to settle on winning solution
    48
    49      public SecurityListener(HttpDestination destination, HttpExchange ex)
    50      {
    51          // Start of sending events through to the wrapped listener
    52          // Next decision point is the onResponseStatus
    53          super(ex.getEventListener(),true);
    54          _destination=destination;
    55          _exchange=ex;
    56      }
    57
    58
    59      /**
    60       * scrapes an authentication type from the authString
    61       *
    62       * @param authString
    63       * @return
    64       */
    65      protected String scrapeAuthenticationType( String authString )
    66      {
    67          String authType;
    68
    69          if ( authString.indexOf( " " ) == -1 )
    70          {
    71              authType = authString.toString().trim();
    72          }
    73          else
    74          {
    75              String authResponse = authString.toString();
    76              authType = authResponse.substring( 0, authResponse.indexOf( " " ) ).trim();
    77          }
    78          return authType;
    79      }
    80
    81      /**
    82       * scrapes a set of authentication details from the authString
    83       *
    84       * @param authString
    85       * @return
    86       */
    87      protected Map<String, String> scrapeAuthenticationDetails( String authString )
    88      {
    89          Map<String, String> authenticationDetails = new HashMap<String, String>();
    90          authString = authString.substring( authString.indexOf( " " ) + 1, authString.length() );
    91          StringTokenizer strtok = new StringTokenizer( authString, ",");
    92
    93          while ( strtok.hasMoreTokens() )
    94          {
    95              String[] pair = strtok.nextToken().split( "=" );
    96              if ( pair.length == 2 )
    97              {
    98                  String itemName = pair[0].trim();
    99                  String itemValue = pair[1].trim();
    100
    101                 itemValue = StringUtil.unquote( itemValue );
    102
    103                 authenticationDetails.put( itemName, itemValue );
    104             }
    105             else
    106             {
    107                 throw new IllegalArgumentException( "unable to process authentication details" );
    108             }
    109         }
    110         return authenticationDetails;
    111     }
    112
    113
    114     public void onResponseStatus( Buffer version, int status, Buffer reason )
    115         throws IOException
    116     {
    117         if (Log.isDebugEnabled())
    118             Log.debug("SecurityListener:Response Status: " + status );
    119
    120         if ( status == HttpServletResponse.SC_UNAUTHORIZED && _attempts<_destination.getHttpClient().maxRetries())
    121         {
    122             // Let's absorb events until we have done some retries
    123             setDelegatingResponses(false);
    124             _needIntercept = true;
    125         }
    126         else
    127         {
    128             setDelegatingResponses(true);
    129             setDelegatingRequests(true);
    130             _needIntercept = false;
    131         }
    132         super.onResponseStatus(version,status,reason);
    133     }
    134
    135
    136     public void onResponseHeader( Buffer name, Buffer value )
    137         throws IOException
    138     {
    139         if (Log.isDebugEnabled())
    140             Log.debug( "SecurityListener:Header: " + name.toString() + " / " + value.toString() );
    141
    142
    143         if (!isDelegatingResponses())
    144         {
    145             int header = HttpHeaders.CACHE.getOrdinal(name);
    146             switch (header)
    147             {
    148                 case HttpHeaders.WWW_AUTHENTICATE_ORDINAL:
    149
    150                     // TODO don't hard code this bit.
    151                     String authString = value.toString();
    152                     String type = scrapeAuthenticationType( authString );
    153
    154                     // TODO maybe avoid this map creation
    155                     Map<String,String> details = scrapeAuthenticationDetails( authString );
    156                     String pathSpec="/"; // TODO work out the real path spec
    157                     RealmResolver realmResolver = _destination.getHttpClient().getRealmResolver();
    158
    159                     if ( realmResolver == null )
    160                     {
    161                         break;
    162                     }
    163
    164                     Realm realm = realmResolver.getRealm( details.get("realm"), _destination, pathSpec ); // TODO work our realm correctly
    165
    166                     if ( realm == null )
    167                     {
    168                         Log.warn( "Unknown Security Realm: " + details.get("realm") );
    169                     }
    170                     else if ("digest".equalsIgnoreCase(type))
    171                     {
    172                         _destination.addAuthorization("/",new DigestAuthorization(realm,details));
    173
    174                     }
    175                     else if ("basic".equalsIgnoreCase(type))
    176                     {
    177                         _destination.addAuthorization(pathSpec,new BasicAuthorization(realm));
    178                     }
    179
    180                     break;
    181             }
    182         }
    183         super.onResponseHeader(name,value);
    184     }
    185
    186
    187     public void onRequestComplete() throws IOException
    188     {
    189         _requestComplete = true;
    190
    191         if (_needIntercept)
    192         {
    193             if (_requestComplete && _responseComplete)
    194             {
    195                if (Log.isDebugEnabled())
    196                    Log.debug("onRequestComplete, Both complete: Resending from onResponseComplete "+_exchange);
    197                 _responseComplete = false;
    198                 _requestComplete = false;
    199                 setDelegatingRequests(true);
    200                 setDelegatingResponses(true);
    201                 _destination.resend(_exchange);
    202             }
    203             else
    204             {
    205                 if (Log.isDebugEnabled())
    206                     Log.debug("onRequestComplete, Response not yet complete onRequestComplete, calling super for "+_exchange);
    207                 super.onRequestComplete();
    208             }
    209         }
    210         else
    211         {
    212             if (Log.isDebugEnabled())
    213                 Log.debug("onRequestComplete, delegating to super with Request complete="+_requestComplete+", response complete="+_responseComplete+" "+_exchange);
    214             super.onRequestComplete();
    215         }
    216     }
    217
    218
    219     public void onResponseComplete() throws IOException
    220     {
    221         _responseComplete = true;
    222         if (_needIntercept)
    223         {
    224             if (_requestComplete && _responseComplete)
    225             {
    226                 if (Log.isDebugEnabled())
    227                     Log.debug("onResponseComplete, Both complete: Resending from onResponseComplete"+_exchange);
    228                 _responseComplete = false;
    229                 _requestComplete = false;
    230                 setDelegatingResponses(true);
    231                 setDelegatingRequests(true);
    232                 _destination.resend(_exchange);
    233
    234             }
    235             else
    236             {
    237                if (Log.isDebugEnabled())
    238                    Log.debug("onResponseComplete, Request not yet complete from onResponseComplete,  calling super "+_exchange);
    239                 super.onResponseComplete();
    240             }
    241         }
    242         else
    243         {
    244             if (Log.isDebugEnabled())
    245                 Log.debug("OnResponseComplete, delegating to super with Request complete="+_requestComplete+", response complete="+_responseComplete+" "+_exchange);
    246             super.onResponseComplete();
    247         }
    248     }
    249
    250     public void onRetry()
    251     {
    252         _attempts++;
    253         setDelegatingRequests(true);
    254         setDelegatingResponses(true);
    255         _requestComplete=false;
    256         _responseComplete=false;
    257         _needIntercept=false;
    258         super.onRetry();
    259     }
    260
    261
    262 }