001 package users; 002 003 import java.io.Serializable; 004 import java.util.Collections; 005 import java.util.HashMap; 006 import java.util.HashSet; 007 import java.util.Map; 008 import java.util.Set; 009 010 import javax.swing.JCheckBox; 011 import javax.swing.JToggleButton; 012 013 import users.events.CapabilityDataEvent; 014 import users.events.CapabilityDataListener; 015 import util.Debug; 016 import util.HelpableListener; 017 import util.ListenerHelper; 018 import util.SerializableListener; 019 020 /** 021 * A user, having a name, a password for log-in purposes, and a set of capabilities. 022 * 023 * <p><code>User</code> objects are used to store all information associated with a user. As a 024 * default users have a name, a password for log-in purposes, and a set of capabilities 025 * that can be used to restrict the users usage of the application. Additional information 026 * stored in subclasses of <code>User</code> could include statistics on application usage, bonus data 027 * etc.</p> 028 * 029 * @see UserManager 030 * @see Capability 031 * 032 * @author Steffen Zschaler 033 * @version 2.0 05/05/1999 034 * @since v2.0 035 */ 036 public class User extends Object implements Serializable, Comparable { 037 038 /** 039 * The user's name. This is an immutable value and cannot be changed once the user 040 * has been created. 041 * 042 * @serial 043 */ 044 private final String m_sName; 045 046 /** 047 * The user's log-in password. This should normally be stored in garbled form as it 048 * may be serialized and thus there is the potential risk of it being read by unauthorized 049 * persons. 050 * 051 * @serial 052 */ 053 private char[] m_sPassWd; 054 055 /** 056 * The user's capabilities. 057 * 058 * @see Capability 059 * 060 * @serial 061 */ 062 private Map m_mpCapabilities = new HashMap(); 063 064 /** 065 * The list of all listeners that showed an interest in this user. 066 * 067 * @serial See <a href="#util.ListenerHelper">ListenerHelper's serializable form</a> for more information on 068 * what listeners get a chance to be serialized. 069 */ 070 protected ListenerHelper m_lhListeners = new ListenerHelper(); 071 072 /** 073 * Create a new User with a given name. The password will initially be the empty 074 * string and there will be no capabilities. 075 * 076 * @param sName the new user's name. 077 */ 078 public User(String sName) { 079 080 super(); 081 082 m_sName = sName; 083 m_sPassWd = new char[0]; 084 } 085 086 /** 087 * Retrieve the name of this user. 088 * 089 * @return the name of the user. 090 * 091 * @override Never 092 */ 093 public final String getName() { 094 return m_sName; 095 } 096 097 /** 098 * Check whether a given string is identical to the password of this user. 099 * 100 * <p>For security reasons there is no <code>getPassWd()</code> method. The only way to 101 * check a user's password is this method. The string you pass as a parameter will be 102 * compared to the user's password as it is stored, i.e. if the password is stored in 103 * a garbled form (recommended) the string you pass as a parameter must also be in 104 * garbled form.</p> 105 * 106 * @param sPassWd the string to be compared to the user's password. Must be in the 107 * same form as the actual password, i.e. esp. it must be garbled if the actual password 108 * is. 109 * 110 * @return true if the password and the string passed as a parameter are equal. 111 * 112 * @see #garblePassWD 113 * 114 * @override Never 115 */ 116 public final boolean isPassWd(char[] sPassWd) { 117 if(m_sPassWd.length != sPassWd.length) return false; 118 for(int i=0;i<m_sPassWd.length;i++) { 119 if(m_sPassWd[i] != sPassWd[i]) return false; 120 } 121 return true; 122 } 123 124 /** 125 * Set the password of this user. 126 * 127 * <p>The password is stored exactly as given, i.e. no garbling of any kind is performed. 128 * It is strongly recommended, though, that you pass a garbled password, so that 129 * passwords are not stored as plain text.</p> 130 * 131 * @param sPassWd the new password 132 * 133 * @see #garblePassWD 134 * @see PassWDGarbler 135 * 136 * @override Never 137 */ 138 public final void setPassWd(char[] sPassWd) { 139 m_sPassWd = sPassWd; 140 } 141 142 /** 143 * Check whether the given object equals this user. 144 * 145 * <p>Two users are considered equal if their names are equal.</p> 146 * 147 * @param o the object to be compared to. 148 * 149 * @return true if the given object equals this user. 150 * 151 * @override Sometimes Override this method if you need to implement a different notion of equality between 152 * users. 153 */ 154 public boolean equals(Object o) { 155 if (o instanceof User) { 156 return ((User)o).getName().equals(this.getName()); 157 } else { 158 return false; 159 } 160 } 161 162 /** 163 * Compares two Users. 164 * 165 * @param o the User to be compared with <code>this</code>. 166 * @return the comparison result. 167 * @override Sometimes 168 */ 169 public int compareTo(Object o) { 170 return m_sName.compareTo(((User)o).getName()); 171 } 172 173 /** 174 * Return a String representation. 175 * 176 * @return the {@link #getName name} of the user. 177 * 178 * @override Sometimes 179 */ 180 public String toString() { 181 return getName(); 182 } 183 184 /** 185 * Set a range of the user's capabilities to new values. 186 * 187 * <p>Sets all capabilities from <code>mpCapabilities</code> to the new values. 188 * This will fire <code>capabilitiesAdded</code> events, and <code>capabilitiesReplaced</code> 189 * events if capabilities were changed.</p> 190 * 191 * <p><strong>Attention:</strong> A capability that has been set cannot be removed 192 * again. Capabilities have two states (Granted and Not Granted). If you want to 193 * remove a certain capability, set its state to Not Granted.</p> 194 * 195 * @param mpCapabilities the capabilities to be set. The keys of this map must be the 196 * names of the capabilities to be set, whereas the corresponding values must be the 197 * actual Capability objects. 198 * 199 * @see Capability 200 * @see #setCapability 201 * @see users.events.CapabilityDataListener 202 * 203 * @override Never 204 */ 205 public synchronized void setCapabilities(Map mpCapabilities) { 206 207 // distinguish added and replaced capabilities to make sure we fire 208 // the correct events. 209 Set stReplacements = new HashSet(mpCapabilities.keySet()); 210 stReplacements.retainAll(m_mpCapabilities.keySet()); 211 212 Set stAdded = new HashSet(mpCapabilities.keySet()); 213 stAdded.removeAll(m_mpCapabilities.keySet()); 214 215 // store the capabilities 216 m_mpCapabilities.putAll(mpCapabilities); 217 218 // fire the events 219 fireCapabilitiesAdded(Collections.unmodifiableSet(stAdded)); 220 fireCapabilitiesReplaced(Collections.unmodifiableSet(stReplacements)); 221 } 222 223 /** 224 * Set one capability. 225 * 226 * <p><strong>Attention:</strong> A capability that has been set cannot be removed 227 * again. Capabilities have two states (Granted and Not Granted). If you want to 228 * remove a certain capability, set its state to Not Granted.</p> 229 * 230 * <p>This will fire a <code>capabilitiesAdded</code> or a <code>capabilitiesReplaced</code> 231 * event.</p> 232 * 233 * @param cap the capability to be set. 234 * 235 * @return the previous value of the capability or <code>null</code> if none. 236 * 237 * @override Never 238 */ 239 public synchronized Capability setCapability(Capability cap) { 240 if (cap != null) { 241 Capability capReturn = (Capability)m_mpCapabilities.remove(cap.getName()); 242 243 m_mpCapabilities.put(cap.getName(), cap); 244 245 if (capReturn != null) { 246 fireCapabilitiesReplaced(Collections.singleton(cap.getName())); 247 } else { 248 fireCapabilitiesAdded(Collections.singleton(cap.getName())); 249 } 250 251 return capReturn; 252 } 253 254 return null; 255 } 256 257 /** 258 * Retrieve one of this user's capabilities. 259 * 260 * <p>Retrieves the capability of this user that is identified by <code>sCapName</code>.</p> 261 * 262 * @param sCapName the name of the capability to be returned. 263 * 264 * @return the capability associated with the given name or <code>null</code> if none. 265 * 266 * @see Capability 267 * 268 * @override Never 269 */ 270 public synchronized Capability getCapability(String sCapName) { 271 return (Capability)m_mpCapabilities.get(sCapName); 272 } 273 274 /** 275 * Return a checkbox that can be used to visualize and change the value of a certain 276 * capability of this user. 277 * 278 * <p>The checkbox will be backed by the capability, i.e. changes of the capability 279 * will be directly reflected in the checkbox and vice-versa. There will be a 280 * <code>NullPointerException</code> if the specified capability does not exist.</p> 281 * 282 * @param sCapName the name of the capability to be visualized by the checkbox. 283 * 284 * @return a checkbox that can be used to visualize and change the capability. 285 * 286 * @exception NullPointerException if Capability does not exist. 287 * 288 * @see javax.swing.JCheckBox 289 * @see Capability 290 * @see Capability#getDisplayName 291 * 292 * @override Never 293 */ 294 public JCheckBox getCapabilityCheckBox(final String sCapName) { 295 296 class CapabilityButtonModel extends JToggleButton.ToggleButtonModel implements CapabilityDataListener, 297 HelpableListener, SerializableListener { 298 299 { 300 // replace listener list for special support 301 listenerList = new ListenerHelper(this); 302 303 updateModel(); 304 } 305 306 private Capability capModelled; 307 308 // ButtonModel interface methods 309 public boolean isSelected() { 310 ((ListenerHelper)listenerList).needModelUpdate(); 311 312 return capModelled.isGranted(); 313 } 314 315 public void setSelected(boolean bSelect) { 316 if (bSelect != capModelled.isGranted()) { 317 setCapability(capModelled.getToggled()); 318 } 319 } 320 321 // CapabilityDataListener interface methods 322 public void capabilitiesAdded(CapabilityDataEvent e) {} 323 324 public void capabilitiesReplaced(CapabilityDataEvent e) { 325 if (e.affectsCapability(capModelled.getName())) { 326 capModelled = e.getCapability(capModelled.getName()); 327 328 fireStateChanged(); 329 } 330 } 331 332 // HelpableListener interface methods 333 public void updateModel() { 334 capModelled = getCapability(sCapName); 335 } 336 337 public void subscribe() { 338 User.this.addCapabilityDataListener(CapabilityButtonModel.this); 339 Debug.print("CapabilityButtonModel.subscribe", -1); 340 } 341 342 public void unsubscribe() { 343 User.this.removeCapabilityDataListener(CapabilityButtonModel.this); 344 Debug.print("CapabilityButtonModel.unsubscribe", -1); 345 } 346 } 347 348 Capability cap = getCapability(sCapName); 349 JCheckBox jcbReturn = new JCheckBox(cap.getDisplayName(), cap.isGranted()); 350 jcbReturn.setModel(new CapabilityButtonModel()); 351 352 return jcbReturn; 353 } 354 355 // Event handling 356 /** 357 * Add a CapabilityDataListener. CapabilityDataListeners receive events whenever a 358 * user's capability list changed. 359 * 360 * @param cdl the CapabilityDataListener to add. 361 * 362 * @override Never 363 */ 364 public void addCapabilityDataListener(CapabilityDataListener cdl) { 365 m_lhListeners.add(CapabilityDataListener.class, cdl); 366 } 367 368 /** 369 * Remove a CapabilityDataListener. CapabilityDataListeners receive events whenever a 370 * user's capability list changed. 371 * 372 * @param cdl the CapabilityDataListener to remove. 373 * 374 * @override Never 375 */ 376 public void removeCapabilityDataListener(CapabilityDataListener cdl) { 377 m_lhListeners.remove(CapabilityDataListener.class, cdl); 378 } 379 380 /** 381 * Fire a <code>capabilitiesAdded</code> event. 382 * 383 * @param stCapNames the set of capability names that where added. 384 * 385 * @see users.events.CapabilityDataListener#capabilitiesAdded 386 * 387 * @override Never 388 */ 389 protected void fireCapabilitiesAdded(Set stCapNames) { 390 CapabilityDataEvent cde = null; 391 392 // Guaranteed to return a non-null array 393 Object[] listeners = m_lhListeners.getListenerList(); 394 395 // Process the listeners last to first, notifying 396 // those that are interested in this event 397 for (int i = listeners.length - 2; i >= 0; i -= 2) { 398 if (listeners[i] == CapabilityDataListener.class) { 399 // Lazily create the event: 400 if (cde == null) { 401 cde = new CapabilityDataEvent(this, stCapNames); 402 403 } 404 ((CapabilityDataListener)listeners[i + 1]).capabilitiesAdded(cde); 405 } 406 } 407 } 408 409 /** 410 * Fire a <code>capabilitiesReplaced</code> event. 411 * 412 * @param stCapNames the set of capability names that where replaced. 413 * 414 * @see users.events.CapabilityDataListener#capabilitiesReplaced 415 * 416 * @override Never 417 */ 418 protected void fireCapabilitiesReplaced(Set stCapNames) { 419 CapabilityDataEvent cde = null; 420 421 // Guaranteed to return a non-null array 422 Object[] listeners = m_lhListeners.getListenerList(); 423 424 // Process the listeners last to first, notifying 425 // those that are interested in this event 426 for (int i = listeners.length - 2; i >= 0; i -= 2) { 427 if (listeners[i] == CapabilityDataListener.class) { 428 // Lazily create the event: 429 if (cde == null) { 430 cde = new CapabilityDataEvent(this, stCapNames); 431 432 } 433 ((CapabilityDataListener)listeners[i + 1]).capabilitiesReplaced(cde); 434 } 435 } 436 } 437 438 /** 439 * Method called by the UserManager when the user was associated with some object. 440 * 441 * @param oTo the object this user was associated with. 442 * 443 * @see UserManager 444 * 445 * @override Sometimes Override this method if you need to be informed when the user was logged on to some 446 * object. 447 */ 448 public void loggedOn(Object oTo) {} 449 450 /** 451 * Method called by the UserManager when the user was disassociated from some object. 452 * 453 * @param oFrom the object this user was disassociated from. 454 * 455 * @see UserManager 456 * 457 * @override Sometimes Override this method if you need to be informed when the user was logged off from 458 * some object. 459 */ 460 public void loggedOff(Object oFrom) {} 461 462 /////////////////////////////////////////////////////////////////////////////////////////////// 463 /// STATIC PART 464 /////////////////////////////////////////////////////////////////////////////////////////////// 465 466 /** 467 * Converts a char[]-Array to String 468 * @param chValue the char[]-Array to be converted 469 * @return the converted value as String 470 */ 471 public static String getStringFromChar(char[] chValue) { 472 int i; 473 String strReturn = ""; 474 for(i=0;i<chValue.length;i++) { 475 strReturn += chValue[i]; 476 } 477 return strReturn; 478 } 479 480 /** 481 * Converst a String to char[]-Array 482 * @param strValue the String to be converted 483 * @return the converted value as char[]-Array 484 */ 485 public static char[] getCharFromString(String strValue) { 486 char[] chReturn = new char[strValue.length()]; 487 int i; 488 for(i=0;i<strValue.length();i++) { 489 chReturn[i] = strValue.charAt(i); 490 } 491 return chReturn; 492 } 493 494 /** 495 * The default password garbler. 496 * 497 * <p>The default password garbling algorithm is very simple and should only be used if no real security 498 * concerns are present. It will take the input String and perform a one-complement and add 7 for each byte 499 * in the String.</p> 500 */ 501 public static final PassWDGarbler DEFAULT_PASSWORD_GARBLER = new PassWDGarbler() { 502 public char[] garblePassWD(char[] sPassWD) { 503 return getCharFromString(MD5.encodeString(getStringFromChar(sPassWD))); 504 } 505 }; 506 507 /** 508 * The global password garbler. It defaults to {@link #DEFAULT_PASSWORD_GARBLER}. 509 */ 510 private static PassWDGarbler s_pwdgGlobal = DEFAULT_PASSWORD_GARBLER; 511 512 /** 513 * Set the global password garbler. 514 * 515 * <p>The global password garbler can be used as a central instance for garbling 516 * your users' passwords. It defaults to {@link #DEFAULT_PASSWORD_GARBLER}.</p> 517 * 518 * @param pwdgNew the new global password garbler. 519 * 520 * @return the previous global password garbler. 521 */ 522 public synchronized static PassWDGarbler setGlobalPassWDGarbler(PassWDGarbler pwdgNew) { 523 PassWDGarbler pwdg = s_pwdgGlobal; 524 525 s_pwdgGlobal = pwdgNew; 526 527 return pwdg; 528 } 529 530 /** 531 * Get the global password garbler. 532 * 533 * @return the global password garbler. 534 */ 535 public synchronized static PassWDGarbler getGlobalPassWDGarbler() { 536 return s_pwdgGlobal; 537 } 538 539 /** 540 * Garble a password using the global password garbler, if any. 541 * 542 * <p>If no global password garbler is installed, the password 543 * is returned unchanged. Otherwise the garbled password is returned.</p> 544 * 545 * @param sPassWD the password to garble 546 * 547 * @return the garbled password. 548 */ 549 public synchronized static char[] garblePassWD(char[] sPassWD) { 550 return ((s_pwdgGlobal == null) ? (sPassWD) : (s_pwdgGlobal.garblePassWD(sPassWD))); 551 } 552 }