1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.client.security;
15
16 import java.io.IOException;
17 import java.util.HashMap;
18 import java.util.Map;
19 import java.util.StringTokenizer;
20
21 import org.eclipse.jetty.client.HttpDestination;
22 import org.eclipse.jetty.client.HttpEventListenerWrapper;
23 import org.eclipse.jetty.client.HttpExchange;
24 import org.eclipse.jetty.http.HttpHeaders;
25 import org.eclipse.jetty.http.HttpStatus;
26 import org.eclipse.jetty.io.Buffer;
27 import org.eclipse.jetty.util.StringUtil;
28 import org.eclipse.jetty.util.log.Log;
29 import org.eclipse.jetty.util.log.Logger;
30
31
32
33
34
35
36
37
38 public class SecurityListener extends HttpEventListenerWrapper
39 {
40 private static final Logger LOG = Log.getLogger(SecurityListener.class);
41
42 private HttpDestination _destination;
43 private HttpExchange _exchange;
44 private boolean _requestComplete;
45 private boolean _responseComplete;
46 private boolean _needIntercept;
47
48 private int _attempts = 0;
49
50 public SecurityListener(HttpDestination destination, HttpExchange ex)
51 {
52
53
54 super(ex.getEventListener(),true);
55 _destination=destination;
56 _exchange=ex;
57 }
58
59
60
61
62
63
64
65
66 protected String scrapeAuthenticationType( String authString )
67 {
68 String authType;
69
70 if ( authString.indexOf( " " ) == -1 )
71 {
72 authType = authString.toString().trim();
73 }
74 else
75 {
76 String authResponse = authString.toString();
77 authType = authResponse.substring( 0, authResponse.indexOf( " " ) ).trim();
78 }
79 return authType;
80 }
81
82
83
84
85
86
87
88 protected Map<String, String> scrapeAuthenticationDetails( String authString )
89 {
90 Map<String, String> authenticationDetails = new HashMap<String, String>();
91 authString = authString.substring( authString.indexOf( " " ) + 1, authString.length() );
92 StringTokenizer strtok = new StringTokenizer( authString, ",");
93
94 while ( strtok.hasMoreTokens() )
95 {
96 String token = strtok.nextToken();
97 String[] pair = token.split( "=" );
98
99
100 if ( pair.length == 2 )
101 {
102 String itemName = pair[0].trim();
103 String itemValue = pair[1].trim();
104
105 itemValue = StringUtil.unquote( itemValue );
106
107 authenticationDetails.put( itemName, itemValue );
108 }
109 else
110 {
111 LOG.debug("SecurityListener: missed scraping authentication details - " + token );
112 }
113 }
114 return authenticationDetails;
115 }
116
117
118 @Override
119 public void onResponseStatus( Buffer version, int status, Buffer reason )
120 throws IOException
121 {
122 if (LOG.isDebugEnabled())
123 LOG.debug("SecurityListener:Response Status: " + status );
124
125 if ( status == HttpStatus.UNAUTHORIZED_401 && _attempts<_destination.getHttpClient().maxRetries())
126 {
127
128 setDelegatingResponses(false);
129 _needIntercept = true;
130 }
131 else
132 {
133 setDelegatingResponses(true);
134 setDelegatingRequests(true);
135 _needIntercept = false;
136 }
137 super.onResponseStatus(version,status,reason);
138 }
139
140
141 @Override
142 public void onResponseHeader( Buffer name, Buffer value )
143 throws IOException
144 {
145 if (LOG.isDebugEnabled())
146 LOG.debug( "SecurityListener:Header: " + name.toString() + " / " + value.toString() );
147
148
149 if (!isDelegatingResponses())
150 {
151 int header = HttpHeaders.CACHE.getOrdinal(name);
152 switch (header)
153 {
154 case HttpHeaders.WWW_AUTHENTICATE_ORDINAL:
155
156
157 String authString = value.toString();
158 String type = scrapeAuthenticationType( authString );
159
160
161 Map<String,String> details = scrapeAuthenticationDetails( authString );
162 String pathSpec="/";
163 RealmResolver realmResolver = _destination.getHttpClient().getRealmResolver();
164
165 if ( realmResolver == null )
166 {
167 break;
168 }
169
170 Realm realm = realmResolver.getRealm( details.get("realm"), _destination, pathSpec );
171
172 if ( realm == null )
173 {
174 LOG.warn( "Unknown Security Realm: " + details.get("realm") );
175 }
176 else if ("digest".equalsIgnoreCase(type))
177 {
178 _destination.addAuthorization("/",new DigestAuthentication(realm,details));
179
180 }
181 else if ("basic".equalsIgnoreCase(type))
182 {
183 _destination.addAuthorization(pathSpec,new BasicAuthentication(realm));
184 }
185
186 break;
187 }
188 }
189 super.onResponseHeader(name,value);
190 }
191
192
193 @Override
194 public void onRequestComplete() throws IOException
195 {
196 _requestComplete = true;
197
198 if (_needIntercept)
199 {
200 if (_requestComplete && _responseComplete)
201 {
202 if (LOG.isDebugEnabled())
203 LOG.debug("onRequestComplete, Both complete: Resending from onResponseComplete "+_exchange);
204 _responseComplete = false;
205 _requestComplete = false;
206 setDelegatingRequests(true);
207 setDelegatingResponses(true);
208 _destination.resend(_exchange);
209 }
210 else
211 {
212 if (LOG.isDebugEnabled())
213 LOG.debug("onRequestComplete, Response not yet complete onRequestComplete, calling super for "+_exchange);
214 super.onRequestComplete();
215 }
216 }
217 else
218 {
219 if (LOG.isDebugEnabled())
220 LOG.debug("onRequestComplete, delegating to super with Request complete="+_requestComplete+", response complete="+_responseComplete+" "+_exchange);
221 super.onRequestComplete();
222 }
223 }
224
225
226 @Override
227 public void onResponseComplete() throws IOException
228 {
229 _responseComplete = true;
230 if (_needIntercept)
231 {
232 if (_requestComplete && _responseComplete)
233 {
234 if (LOG.isDebugEnabled())
235 LOG.debug("onResponseComplete, Both complete: Resending from onResponseComplete"+_exchange);
236 _responseComplete = false;
237 _requestComplete = false;
238 setDelegatingResponses(true);
239 setDelegatingRequests(true);
240 _destination.resend(_exchange);
241
242 }
243 else
244 {
245 if (LOG.isDebugEnabled())
246 LOG.debug("onResponseComplete, Request not yet complete from onResponseComplete, calling super "+_exchange);
247 super.onResponseComplete();
248 }
249 }
250 else
251 {
252 if (LOG.isDebugEnabled())
253 LOG.debug("OnResponseComplete, delegating to super with Request complete="+_requestComplete+", response complete="+_responseComplete+" "+_exchange);
254 super.onResponseComplete();
255 }
256 }
257
258 @Override
259 public void onRetry()
260 {
261 _attempts++;
262 setDelegatingRequests(true);
263 setDelegatingResponses(true);
264 _requestComplete=false;
265 _responseComplete=false;
266 _needIntercept=false;
267 super.onRetry();
268 }
269
270
271 }