package users; import java.io.Serializable; import java.util.Map; import java.util.HashMap; import java.util.Set; import java.util.HashSet; import java.util.Collections; import java.util.Iterator; import javax.swing.*; import users.events.*; import util.*; /** * A user, having a name, a password for log-in purposes, and a set of capabilities. * * <p><code>User</code> objects are used to store all information associated with a user. As a * default users have a name, a password for log-in purposes, and a set of capabilities * that can be used to restrict the users usage of the application. Additional information * stored in subclasses of <code>User</code> could include statistics on application usage, bonus data * etc.</p> * * @see UserManager * @see Capability * * @author Steffen Zschaler * @version 2.0 05/05/1999 * @since v2.0 */ public class User extends Object implements Serializable { /** * The user's name. This is an immutable value and cannot be changed once the user * has been created. * * @serial */ private final String m_sName; /** * The user's log-in password. This should normally be stored in garbled form as it * may be serialized and thus there is the potential risk of it being read by unauthorized * persons. * * @serial */ private String m_sPassWd; /** * The user's capabilities. * * @see Capability * * @serial */ private Map m_mpCapabilities = new HashMap(); /** * The list of all listeners that showed an interest in this user. * * @serial See <a href="#util.ListenerHelper">ListenerHelper's serializable form</a> for more information on * what listeners get a chance to be serialized. */ protected ListenerHelper m_lhListeners = new ListenerHelper(); /** * Create a new User with a given name. The password will initially be the empty * string and there will be no capabilities. * * @param sName the new user's name. */ public User (String sName) { super(); m_sName = sName; m_sPassWd = ""; } /** * Retrieve the name of this user. * * @return the name of the user. * * @override Never */ public final String getName() { return m_sName; } /** * Check whether a given string is identical to the password of this user. * * <p>For security reasons there is no <code>getPassWd()</code> method. The only way to * check a user's password is this method. The string you pass as a parameter will be * compared to the user's password as it is stored, i.e. if the password is stored in * a garbled form (recommended) the string you pass as a parameter must also be in * garbled form.</p> * * @param sPassWd the string to be compared to the user's password. Must be in the * same form as the actual password, i.e. esp. it must be garbled if the actual password * is. * * @return true if the password and the string passed as a parameter are equal. * * @see #garblePassWD * * @override Never */ public final boolean isPassWd (String sPassWd) { return m_sPassWd.equals (sPassWd); } /** * Set the password of this user. * * <p>The password is stored exactly as given, i.e. no garbling of any kind is performed. * It is strongly recommended, though, that you pass a garbled password, so that * passwords are not stored as plain text.</p> * * @param sPassWd the new password * * @see #garblePassWD * @see PassWDGarbler * * @override Never */ public final void setPassWd (String sPassWd) { m_sPassWd = sPassWd; } /** * Check whether the given object equals this user. * * <p>Two users are considered equal if their names are equal.</p> * * @param o the object to be compared to. * * @return true if the given object equals this user. * * @override Sometimes Override this method if you need to implement a different notion of equality between * users. */ public boolean equals (Object o) { if (o instanceof User) { return ((User) o).getName().equals (this.getName()); } else return false; } /** * Return a String representation. * * @return the {@link #getName name} of the user. * * @override Sometimes */ public String toString() { return getName(); } /** * Set a range of the user's capabilities to new values. * * <p>Sets all capabilities from <code>mpCapabilities</code> to the new values. * This will fire <code>capabilitiesAdded</code> events, and <code>capabilitiesReplaced</code> * events if capabilities were changed.</p> * * <p><strong>Attention:</strong> A capability that has been set cannot be removed * again. Capabilities have two states (Granted and Not Granted). If you want to * remove a certain capability, set its state to Not Granted.</p> * * @param mpCapabilities the capabilities to be set. The keys of this map must be the * names of the capabilities to be set, whereas the corresponding values must be the * actual Capability objects. * * @see Capability * @see #setCapability * @see users.events.CapabilityDataListener * * @override Never */ public synchronized void setCapabilities (Map mpCapabilities) { // distinguish added and replaced capabilities to make sure we fire // the correct events. Set stReplacements = new HashSet (mpCapabilities.keySet()); stReplacements.retainAll (m_mpCapabilities.keySet()); Set stAdded = new HashSet (mpCapabilities.keySet()); stAdded.removeAll (m_mpCapabilities.keySet()); // store the capabilities m_mpCapabilities.putAll (mpCapabilities); // fire the events fireCapabilitiesAdded (Collections.unmodifiableSet (stAdded)); fireCapabilitiesReplaced (Collections.unmodifiableSet (stReplacements)); } /** * Set one capability. * * <p><strong>Attention:</strong> A capability that has been set cannot be removed * again. Capabilities have two states (Granted and Not Granted). If you want to * remove a certain capability, set its state to Not Granted.</p> * * <p>This will fire a <code>capabilitiesAdded</code> or a <code>capabilitiesReplaced</code> * event.</p> * * @param cap the capability to be set. * * @return the previous value of the capability or <code>null</code> if none. * * @override Never */ public synchronized Capability setCapability (Capability cap) { if (cap != null) { Capability capReturn = (Capability) m_mpCapabilities.remove (cap.getName()); m_mpCapabilities.put (cap.getName(), cap); if (capReturn != null) { fireCapabilitiesReplaced (Collections.singleton (cap.getName())); } else { fireCapabilitiesAdded (Collections.singleton (cap.getName())); } return capReturn; } return null; } /** * Retrieve one of this user's capabilities. * * <p>Retrieves the capability of this user that is identified by <code>sCapName</code>.</p> * * @param sCapName the name of the capability to be returned. * * @return the capability associated with the given name or <code>null</code> if none. * * @see Capability * * @override Never */ public synchronized Capability getCapability (String sCapName) { return (Capability) m_mpCapabilities.get (sCapName); } /** * Return a checkbox that can be used to visualize and change the value of a certain * capability of this user. * * <p>The checkbox will be backed by the capability, i.e. changes of the capability * will be directly reflected in the checkbox and vice-versa. There will be a * <code>NullPointerException</code> if the specified capability does not exist.</p> * * @param sCapName the name of the capability to be visualized by the checkbox. * * @return a checkbox that can be used to visualize and change the capability. * * @exception NullPointerException if Capability does not exist. * * @see javax.swing.JCheckBox * @see Capability * @see Capability#getDisplayName * * @override Never */ public JCheckBox getCapabilityCheckBox (final String sCapName) { class CapabilityButtonModel extends JToggleButton.ToggleButtonModel implements CapabilityDataListener, HelpableListener, SerializableListener { { // replace listener list for special support listenerList = new ListenerHelper (this); updateModel(); } private Capability capModelled; // ButtonModel interface methods public boolean isSelected() { ((ListenerHelper) listenerList).needModelUpdate(); return capModelled.isGranted(); } public void setSelected (boolean bSelect) { if (bSelect != capModelled.isGranted()) { setCapability (capModelled.getToggled()); } } // CapabilityDataListener interface methods public void capabilitiesAdded (CapabilityDataEvent e) {} public void capabilitiesReplaced (CapabilityDataEvent e) { if (e.affectsCapability (capModelled.getName())) { capModelled = e.getCapability (capModelled.getName()); fireStateChanged(); } } // HelpableListener interface methods public void updateModel() { capModelled = getCapability (sCapName); } public void subscribe() { User.this.addCapabilityDataListener (CapabilityButtonModel.this); Debug.print ("CapabilityButtonModel.subscribe", -1); } public void unsubscribe() { User.this.removeCapabilityDataListener (CapabilityButtonModel.this); Debug.print ("CapabilityButtonModel.unsubscribe", -1); } } Capability cap = getCapability (sCapName); JCheckBox jcbReturn = new JCheckBox (cap.getDisplayName(), cap.isGranted()); jcbReturn.setModel (new CapabilityButtonModel()); return jcbReturn; } // Event handling /** * Add a CapabilityDataListener. CapabilityDataListeners receive events whenever a * user's capability list changed. * * @param cdl the CapabilityDataListener to add. * * @override Never */ public void addCapabilityDataListener (CapabilityDataListener cdl) { m_lhListeners.add (CapabilityDataListener.class, cdl); } /** * Remove a CapabilityDataListener. CapabilityDataListeners receive events whenever a * user's capability list changed. * * @param cdl the CapabilityDataListener to remove. * * @override Never */ public void removeCapabilityDataListener (CapabilityDataListener cdl) { m_lhListeners.remove (CapabilityDataListener.class, cdl); } /** * Fire a <code>capabilitiesAdded</code> event. * * @param stCapNames the set of capability names that where added. * * @see users.events.CapabilityDataListener#capabilitiesAdded * * @override Never */ protected void fireCapabilitiesAdded (Set stCapNames) { CapabilityDataEvent cde = null; // Guaranteed to return a non-null array Object[] listeners = m_lhListeners.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==CapabilityDataListener.class) { // Lazily create the event: if (cde == null) cde = new CapabilityDataEvent (this, stCapNames); ((CapabilityDataListener) listeners[i+1]).capabilitiesAdded (cde); } } } /** * Fire a <code>capabilitiesReplaced</code> event. * * @param stCapNames the set of capability names that where replaced. * * @see users.events.CapabilityDataListener#capabilitiesReplaced * * @override Never */ protected void fireCapabilitiesReplaced (Set stCapNames) { CapabilityDataEvent cde = null; // Guaranteed to return a non-null array Object[] listeners = m_lhListeners.getListenerList(); // Process the listeners last to first, notifying // those that are interested in this event for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==CapabilityDataListener.class) { // Lazily create the event: if (cde == null) cde = new CapabilityDataEvent (this, stCapNames); ((CapabilityDataListener) listeners[i+1]).capabilitiesReplaced (cde); } } } /** * Method called by the UserManager when the user was associated with some object. * * @param oTo the object this user was associated with. * * @see UserManager * * @override Sometimes Override this method if you need to be informed when the user was logged on to some * object. */ public void loggedOn (Object oTo) {} /** * Method called by the UserManager when the user was disassociated from some object. * * @param oFrom the object this user was disassociated from. * * @see UserManager * * @override Sometimes Override this method if you need to be informed when the user was logged off from * some object. */ public void loggedOff (Object oFrom) {} /////////////////////////////////////////////////////////////////////////////////////////////// /// STATIC PART /////////////////////////////////////////////////////////////////////////////////////////////// /** * The default password garbler. * * <p>The default password garbling algorithm is very simple and should only be used if no real security * concerns are present. It will take the input String and perform a one-complement and add 7 for each byte * in the String.</p> */ public static final PassWDGarbler DEFAULT_PASSWORD_GARBLER = new PassWDGarbler() { public String garblePassWD (String sPassWD) { byte[] baBytes = sPassWD.getBytes(); byte bConverter = (byte) 0xFF; for (int i = 0; i < baBytes.length; i++) { baBytes[i] ^= bConverter; if (baBytes[i] <= Byte.MAX_VALUE - 7) { baBytes[i] += 7; } else { // wrap around baBytes[i] = (byte) ((Byte.MIN_VALUE + 7) - (Byte.MAX_VALUE - baBytes[i])); } if (baBytes[i] < 0) { if (baBytes[i] > Byte.MIN_VALUE) { baBytes[i] *= -1; } else { baBytes[i] = Byte.MAX_VALUE; } } } return new String (baBytes); } }; /** * The global password garbler. It defaults to {@link #DEFAULT_PASSWORD_GARBLER}. */ private static PassWDGarbler s_pwdgGlobal = DEFAULT_PASSWORD_GARBLER; /** * Set the global password garbler. * * <p>The global password garbler can be used as a central instance for garbling * your users' passwords. It defaults to {@link #DEFAULT_PASSWORD_GARBLER}.</p> * * @param pwdgNew the new global password garbler. * * @return the previous global password garbler. */ public synchronized static PassWDGarbler setGlobalPassWDGarbler (PassWDGarbler pwdgNew) { PassWDGarbler pwdg = s_pwdgGlobal; s_pwdgGlobal = pwdgNew; return pwdg; } /** * Get the global password garbler. * * @return the global password garbler. */ public synchronized static PassWDGarbler getGlobalPassWDGarbler() { return s_pwdgGlobal; } /** * Garble a password using the global password garbler, if any. * * <p>If no global password garbler is installed, the password * is returned unchanged. Otherwise the garbled password is returned.</p> * * @param sPassWD the password to garble * * @return the garbled password. */ public synchronized static String garblePassWD (String sPassWD) { return ((s_pwdgGlobal == null)?(sPassWD):(s_pwdgGlobal.garblePassWD (sPassWD))); } }