// BlogBridge -- RSS feed reader, manager, and web based service // Copyright (C) 2002-2006 by R. Pito Salas // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software Foundation; // either version 2 of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along with this program; // if not, write to the Free Software Foundation, Inc., 59 Temple Place, // Suite 330, Boston, MA 02111-1307 USA // // Contact: R. Pito Salas // mailto:pitosalas@users.sourceforge.net // More information: about BlogBridge // http://www.blogbridge.com // http://sourceforge.net/projects/blogbridge // // $Id: CachingAuthenticator.java,v 1.6 2006/08/08 10:47:55 spyromus Exp $ // package com.salas.bb.utils.net.auth; import com.jgoodies.uif.application.Application; import com.salas.bb.utils.i18n.Strings; import java.net.Authenticator; import java.net.PasswordAuthentication; import java.util.List; import java.util.ArrayList; import java.util.prefs.Preferences; /** * This authenticator stores the passwords in its own database. */ public class CachingAuthenticator extends Authenticator { private static final Preferences PREFERENCES = Preferences.userNodeForPackage(CachingAuthenticator.class); private static final String PREF_SAVING_PASSWORDS = "savingPasswords"; private static final boolean DEFAULT_SAVING_PASSWORDS = false; // When someone asks for auth. info and we have it we return this information without // showing login/password dialog to the user. But if this information is incorrect, we // will be questioned again and, at this time, we will need to display dialog. This // list contains the hash codes of contexts which were already questioned, so, once // someone will ask ask again for them, we will know that the previous information was // incorrect. private final List queriedContexts; private final IPasswordsRepository repository; private AuthDialog authDialog; /** * Creates authenticator. * * @param aRepository repository to use for passwords storing. */ public CachingAuthenticator(IPasswordsRepository aRepository) { queriedContexts = new ArrayList(); repository = aRepository; } /** * Called when password authorization is needed. Subclasses should override the default * implementation, which returns null. * * @return The PasswordAuthentication collected from the user, or null if none is provided. */ protected synchronized PasswordAuthentication getPasswordAuthentication() { String key = getKey(); PasswordAuthentication auth = repository.getAuthInformation(key); if (auth == null || (!isPasswordUnknown(auth) && wasQueried(key))) { auth = getPasswordAuthentication0(auth); // If user canceled authentication, we create a fake record if (auth == null) auth = authPasswordUnknown(); if (isSavingPasswords()) repository.record(key, auth); } if (isPasswordUnknown(auth)) throw new AuthCancelException(Strings.error("net.user.canceled.authentication")); recordQuery(key); return auth; } /** * Returns <code>TRUE</code> if the auth info is a special marker. * * @param auth auth info. * * @return <code>TRUE</code> if marker. */ private static boolean isPasswordUnknown(PasswordAuthentication auth) { return auth != null && "~".equals(auth.getUserName()) && auth.getPassword() != null && auth.getPassword().length == 1 && auth.getPassword()[0] == '~'; } /** * Returns a special marker authentication info. * * @return marker auth info. */ private static PasswordAuthentication authPasswordUnknown() { return new PasswordAuthentication("~", "~".toCharArray()); } /** * Records querying of auth. info. * * @param context context questioned. */ private void recordQuery(String context) { Integer hashCode = new Integer(context.hashCode()); if (!queriedContexts.contains(hashCode)) queriedContexts.add(hashCode); } /** * Returns <code>TRUE</code> if our queries list has record about query for this context. * * @param context context. * * @return <code>TRUE</code> if our queries list has record about query for this context. */ private boolean wasQueried(String context) { return queriedContexts.contains(new Integer(context.hashCode())); } /** * Called when password authorization is needed. * * @param auth authentication info or <code>NULL</code> if not known. * * @return authentication info. * * @throws AuthCancelException if user canceled authentication. */ private PasswordAuthentication getPasswordAuthentication0(PasswordAuthentication auth) throws AuthCancelException { String username = null; char[] password = null; if (auth != null) { username = auth.getUserName(); password = auth.getPassword(); } String server = getRequestingProtocol() + "://" + getRequestingHost() + ":" + getRequestingPort(); AuthDialog dialog = getDialog(); dialog.open(server, getRequestingPrompt(), username, password, isSavingPasswords()); if (!dialog.hasBeenCanceled()) { auth = new PasswordAuthentication(dialog.getUsername(), dialog.getPassword()); setSavingPasswords(dialog.isSavingRequired()); } else { auth = null; } return auth; } /** * Returns the dialog which is initialized lazily. * * @return the dialog. */ private synchronized AuthDialog getDialog() { if (authDialog == null) authDialog = new AuthDialog(Application.getDefaultParentFrame()); return authDialog; } /** * Returns key of realm. * * @return key. */ private String getKey() { StringBuffer sb = new StringBuffer(); sb.append(getRequestingProtocol()).append(':'); sb.append(getRequestingHost()).append(':'); sb.append(getRequestingPort()).append(':'); sb.append(getRequestingScheme()).append(':'); sb.append(getRequestingPrompt()); return sb.toString(); } /** * Returns the values of <code>savingPasswords</code> flag. * * @return <code>TRUE</code> to save passwords. */ private boolean isSavingPasswords() { return PREFERENCES.getBoolean(PREF_SAVING_PASSWORDS, DEFAULT_SAVING_PASSWORDS); } /** * Changes the value of <code>savingPasswords</code> flag. * * @param saving <code>TRUE</code> to save passwords. */ private void setSavingPasswords(boolean saving) { PREFERENCES.putBoolean(PREF_SAVING_PASSWORDS, saving); } }