001: // AuthFilter.java
002: // $Id: AuthFilter.java,v 1.11 2000/08/16 21:38:03 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.www.protocol.http.auth;
007:
008: import java.awt.Button;
009: import java.awt.Component;
010: import java.awt.Container;
011: import java.awt.Event;
012: import java.awt.Frame;
013: import java.awt.GridBagConstraints;
014: import java.awt.GridBagLayout;
015: import java.awt.Label;
016: import java.awt.Panel;
017: import java.awt.TextComponent;
018: import java.awt.TextField;
019: import java.awt.Window;
020:
021: import java.io.IOException;
022: import java.io.InputStream;
023:
024: import java.util.Hashtable;
025:
026: import org.w3c.tools.codec.Base64Encoder;
027:
028: import org.w3c.www.protocol.http.HttpException;
029: import org.w3c.www.protocol.http.HttpManager;
030: import org.w3c.www.protocol.http.PropRequestFilter;
031: import org.w3c.www.protocol.http.Reply;
032: import org.w3c.www.protocol.http.Request;
033:
034: import org.w3c.www.http.HTTP;
035: import org.w3c.www.http.HttpChallenge;
036: import org.w3c.www.http.HttpCredential;
037: import org.w3c.www.http.HttpFactory;
038: import org.w3c.www.http.HttpReplyMessage;
039: import org.w3c.www.http.HttpRequestMessage;
040:
041: class UserField extends TextField {
042: PasswordPrompter prompter = null;
043:
044: public boolean keyDown(Event evt, int key) {
045: if (key == '\t') {
046: prompter.focusPassword();
047: return true;
048: }
049: return super .keyDown(evt, key);
050: }
051:
052: UserField(PasswordPrompter prompter, String txt, int len) {
053: super (txt, len);
054: this .prompter = prompter;
055: }
056:
057: }
058:
059: class PasswordField extends TextField {
060: PasswordPrompter prompter = null;
061:
062: public boolean keyDown(Event evt, int key) {
063: if ((key == '\n') || (key == '\r')) {
064: prompter.done(PasswordPrompter.EVT_OK);
065: return true;
066: }
067: return super .keyDown(evt, key);
068: }
069:
070: PasswordField(PasswordPrompter prompter, String txt, int len) {
071: super (txt, len);
072: setEchoCharacter('*');
073: this .prompter = prompter;
074: }
075: }
076:
077: class PasswordPrompter extends Panel {
078: TextField txtUser = null;
079: TextField txtPassword = null;
080: Button butOk = null;
081: Button butCancel = null;
082:
083: String user = null;
084: String password = null;
085:
086: static final int EVT_OK = 1;
087: static final int EVT_CANCEL = 2;
088: int evt = -1;
089:
090: protected synchronized boolean waitForCompletion() {
091: while (true) {
092: // Wait for next event:
093: while (evt < 0) {
094: try {
095: wait();
096: } catch (InterruptedException ex) {
097: }
098: }
099: // Handle the event:
100: switch (evt) {
101: case EVT_OK:
102: return true;
103: case EVT_CANCEL:
104: return false;
105: }
106: }
107: }
108:
109: protected synchronized void done(int evt) {
110: this .evt = evt;
111: notifyAll();
112: }
113:
114: protected void focusPassword() {
115: txtPassword.requestFocus();
116: }
117:
118: public boolean action(Event evt, Object what) {
119: int e = -1;
120: if (evt.target == butOk) {
121: e = EVT_OK;
122: } else if (evt.target == butCancel) {
123: e = EVT_CANCEL;
124: } else {
125: return super .action(evt, what);
126: }
127: // We are done with this dialog:
128: done(e);
129: return true;
130: }
131:
132: /**
133: * Get the entered user name.
134: * @return The user name as a String.
135: */
136:
137: public String getUser() {
138: return user;
139: }
140:
141: /**
142: * Get the entered password.
143: * @return The password as a String.
144: */
145:
146: public String getPassword() {
147: return password;
148: }
149:
150: /**
151: * Run the dialog, as if modal.
152: * @return A boolean <strong>false</strong> if interaction was canceled,
153: * <strong>true</strong> otherwise.
154: */
155:
156: public boolean prompt() {
157: Frame toplevel = new Frame("Authentication Required");
158: toplevel.add("Center", this );
159: toplevel.pack();
160: toplevel.show();
161: // Set focus to the user name:
162: txtUser.requestFocus();
163: // Wait for completion, pack up the result, and delete GUI:
164: boolean result = waitForCompletion();
165: this .user = txtUser.getText();
166: this .password = txtPassword.getText();
167: toplevel.hide();
168: toplevel.dispose();
169: return result;
170: }
171:
172: PasswordPrompter(Request request, Reply reply) {
173: // Setup the layout:
174: super ();
175: GridBagLayout gb = new GridBagLayout();
176: setLayout(gb);
177: // Create the title label:
178: HttpChallenge challenge = (request.hasProxy() ? reply
179: .getProxyAuthenticate() : reply.getWWWAuthenticate());
180: Label label = new Label(challenge.getScheme()
181: + " authentication for "
182: + challenge.getAuthParameter("realm"));
183: GridBagConstraints row = new GridBagConstraints();
184: row.gridwidth = GridBagConstraints.REMAINDER;
185: row.anchor = GridBagConstraints.WEST;
186: gb.setConstraints(label, row);
187: add(label);
188: // Create the entries:
189: GridBagConstraints ct = new GridBagConstraints();
190: ct.gridx = GridBagConstraints.RELATIVE;
191: ct.anchor = GridBagConstraints.EAST;
192: ct.weighty = 1.0;
193: GridBagConstraints cv = new GridBagConstraints();
194: cv.gridx = GridBagConstraints.RELATIVE;
195: cv.gridwidth = GridBagConstraints.REMAINDER;
196: cv.fill = GridBagConstraints.HORIZONTAL;
197: cv.anchor = GridBagConstraints.WEST;
198: cv.weightx = 1.0;
199: cv.weighty = 1.0;
200: // Create user entry:
201: label = new Label("User:", Label.LEFT);
202: gb.setConstraints(label, ct);
203: add(label);
204: txtUser = new UserField(this , "", 32);
205: gb.setConstraints(txtUser, cv);
206: add(txtUser);
207: // Create password entry:
208: label = new Label("Password:", Label.LEFT);
209: gb.setConstraints(label, ct);
210: add(label);
211: txtPassword = new PasswordField(this , "", 32);
212: gb.setConstraints(txtPassword, cv);
213: add(txtPassword);
214: // Add the row of buttons:
215: butOk = new Button("Ok");
216: row.anchor = GridBagConstraints.EAST;
217: row.weightx = 1.0;
218: row.gridwidth = GridBagConstraints.RELATIVE;
219: gb.setConstraints(butOk, row);
220: add(butOk);
221: butCancel = new Button("Cancel");
222: row.anchor = GridBagConstraints.WEST;
223: row.gridwidth = GridBagConstraints.REMAINDER;
224: gb.setConstraints(butCancel, row);
225: add(butCancel);
226: }
227: }
228:
229: class CachedRealm {
230: public String realm = null;
231: public HttpCredential credentials = null;
232:
233: CachedRealm(String realm, HttpCredential credentials) {
234: this .realm = realm;
235: this .credentials = credentials;
236: }
237: }
238:
239: public class AuthFilter implements PropRequestFilter {
240: /**
241: * The per-server realms we know about.
242: */
243: protected static Hashtable realms = new Hashtable(13);
244: /**
245: * the HttpManager that installed us.
246: */
247: protected HttpManager manager = null;
248:
249: protected static void registerRealm(Request request, Reply reply,
250: HttpCredential credentials) {
251: // Do we already know about that realm ?
252: if (lookupRealm(request, reply) != null)
253: return;
254: // Register the realm:
255: String srvkey = request.getManager().getServerKey(request);
256: String realm = ((request.hasProxy() ? reply
257: .getProxyAuthenticate() : reply.getWWWAuthenticate())
258: .getAuthParameter("realm"));
259: CachedRealm cache[] = (CachedRealm[]) realms.get(srvkey);
260: if (cache == null) {
261: cache = new CachedRealm[1];
262: cache[0] = new CachedRealm(realm, credentials);
263: } else {
264: CachedRealm nc[] = new CachedRealm[cache.length + 1];
265: System.arraycopy(cache, 0, nc, 0, cache.length);
266: nc[cache.length] = new CachedRealm(realm, credentials);
267: cache = nc;
268: }
269: realms.put(srvkey, cache);
270: }
271:
272: protected static HttpCredential lookupRealm(Request request,
273: Reply reply) {
274: // Lookup known realms on target server:
275: String srvkey = request.getManager().getServerKey(request);
276: CachedRealm cache[] = (CachedRealm[]) realms.get(srvkey);
277: if (cache == null)
278: return null;
279: // Found something, check:
280: HttpChallenge challenge = (request.hasProxy() ? reply
281: .getProxyAuthenticate() : reply.getWWWAuthenticate());
282: String realm = challenge.getAuthParameter("realm");
283: for (int i = 0; i < cache.length; i++) {
284: if (cache[i].realm.equalsIgnoreCase(realm))
285: return cache[i].credentials;
286: }
287: return null;
288: }
289:
290: /**
291: * PropRequestFilter implementation - Initialize the filter.
292: * Time to register ourself to the HttpManager.
293: * @param manager The HTTP manager that is initializing ourself.
294: */
295:
296: public void initialize(HttpManager manager) {
297: this .manager = manager;
298: // We install ourself as a global filter, we are cool !
299: manager.setFilter(this );
300: manager.setAllowUserInteraction(true);
301: }
302:
303: /**
304: * This filter doesn't handle exceptions.
305: * @param request The request that triggered the exception.
306: * @param ex The triggered exception.
307: * @return Always <strong>false</strong>.
308: */
309:
310: public boolean exceptionFilter(Request request, HttpException ex) {
311: return false;
312: }
313:
314: /**
315: * On the way out, we let the request fly through.
316: * @param request The request about to be emitted.
317: */
318:
319: public Reply ingoingFilter(Request request) {
320: return null;
321: }
322:
323: /**
324: * Catch any authentication requirement, and fullfill it with user's help.
325: * This method trap all request for authentication, and pops up a
326: * dialog prompting for the user's name and password.
327: * <p>It then retries the request with the provided authentication
328: * informations.
329: * @param request The request that requires authentication.
330: * @param reply The original reply.
331: * @exception HttpException If some HTTP error occurs.
332: */
333:
334: public Reply outgoingFilter(Request request, Reply reply)
335: throws HttpException {
336: // Is this really for us to catch ?
337: if ((reply.getStatus() != HTTP.UNAUTHORIZED)
338: && (reply.getStatus() != HTTP.PROXY_AUTH_REQUIRED))
339: return null;
340: // Do we know about this realm ?
341: HttpCredential credentials = null;
342: if ((credentials = lookupRealm(request, reply)) == null) {
343: // If we can't interact, we can't help:
344: if (!request.getAllowUserInteraction())
345: return null;
346: // Great ! Now we can indeed help:
347: PasswordPrompter prompter = new PasswordPrompter(request,
348: reply);
349: if (!prompter.prompt())
350: return null;
351: String user = prompter.getUser();
352: String password = prompter.getPassword();
353: // Compute credentials:
354: credentials = HttpFactory.makeCredential("Basic");
355: Base64Encoder encoder = new Base64Encoder(user + ":"
356: + password);
357: credentials.setAuthParameter("cookie", encoder
358: .processString());
359: }
360: // Now restart the request we the right auth infos:
361: if (request.hasProxy())
362: request.setProxyAuthorization(credentials);
363: else
364: request.setAuthorization(credentials);
365: Reply retry = request.getManager().runRequest(request);
366: if (retry.getStatus() / 100 != 4) {
367: // We did succeed, register server/realm infos:
368: registerRealm(request, reply, credentials);
369: // Create the local auth filter:
370: if (request.hasProxy()) {
371: LocalAuthFilter.installProxyAuth(manager, credentials);
372: } else {
373: LocalAuthFilter.installLocalAuth(manager, request
374: .getURL(), credentials);
375: }
376: // Swallow input stream of original reply:
377: try {
378: InputStream in = reply.getInputStream();
379: if (in != null)
380: in.close();
381: } catch (IOException ex) {
382: }
383: // Return the right reply:
384: return retry;
385: } else {
386: return null;
387: }
388: }
389:
390: /**
391: * We don't maintain cached informations.
392: */
393:
394: public void sync() {
395: }
396:
397: }
|