001 /*
002 *
003 * $Revision: 14994 $ $Date: 2009-03-24 13:01:57 +0100 (Tue, 24 Mar 2009) $
004 *
005 * This file is part of *** M y C o R e ***
006 * See http://www.mycore.de/ for details.
007 *
008 * This program is free software; you can use it, redistribute it
009 * and / or modify it under the terms of the GNU General Public License
010 * (GPL) as published by the Free Software Foundation; either version 2
011 * of the License or (at your option) any later version.
012 *
013 * This program is distributed in the hope that it will be useful, but
014 * WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program, in a file called gpl.txt or license.txt.
020 * If not, write to the Free Software Foundation Inc.,
021 * 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
022 */
023
024 package org.mycore.user;
025
026 import static org.mycore.common.MCRConstants.XLINK_NAMESPACE;
027 import static org.mycore.common.MCRConstants.XSI_NAMESPACE;
028
029 import java.security.Principal;
030 import java.sql.Timestamp;
031 import java.util.ArrayList;
032 import java.util.GregorianCalendar;
033 import java.util.List;
034
035 import org.jdom.Document;
036 import org.jdom.Element;
037 import org.mycore.access.MCRAccessManager;
038 import org.mycore.common.MCRConfiguration;
039 import org.mycore.common.MCRException;
040 import org.mycore.common.MCRSession;
041 import org.mycore.common.MCRSessionMgr;
042
043 /**
044 * Instances of this class represent MyCoRe users.
045 *
046 * @see org.mycore.user.MCRUserMgr
047 * @see org.mycore.user.MCRUserObject
048 * @see org.mycore.user.MCRUserContact
049 * @see org.mycore.user.MCRUserMgr
050 *
051 * @author Detlev Degenhardt
052 * @author Jens Kupferschmidt
053 * @author Thomas Scheffler (yagee)
054 * @author Heiko Helmbrecht
055 * @version $Revision: 14994 $ $Date: 2008-10-09 11:30:08 +0200 (Do, 09. Okt
056 * 2008) $
057 */
058 public class MCRUser extends MCRUserObject implements MCRPrincipal, Principal {
059 /** The numerical ID of the MyCoRe user unit (either user ID or group ID) */
060 protected int numID = -1;
061
062 /** Specify whether the user ID is enabled or disabled */
063 protected boolean idEnabled = false;
064
065 /** Specify whether the user is allowed to update the user object */
066 protected boolean updateAllowed = false;
067
068 /** The password of the MyCoRe user */
069 protected String passwd = "";
070
071 /** The primary group ID of the user */
072 protected String primaryGroupID = "";
073
074 /** Object representing user address information */
075 protected MCRUserContact userContact;
076
077 /** A list of groups (IDs) where this user is a member of */
078 protected List<String> groupIDs = null;
079
080 /**
081 * Default constructor. It is used to create a user object with empty
082 * fields. This is useful for constructing an XML representation of a user
083 * without specialized data which is used e.g. by MCRUserServlet just to get
084 * an XML-representation. The XML representation is used by the
085 * XSLT-Stylesheet to create HTML output for the presentation. This empty
086 * user object will not be created in the persistent data store.
087 */
088 public MCRUser() {
089 super();
090 numID = -1;
091 idEnabled = false;
092 updateAllowed = false;
093 passwd = "";
094 primaryGroupID = "";
095 groupIDs = new ArrayList<String>();
096 userContact = new MCRUserContact();
097 }
098
099 /**
100 * This minimal constructor only takes the user ID as a parameter. For all
101 * other attributes the default constructor is invoked. This constructor is
102 * used by the access control system.
103 *
104 * @param id
105 * the named user ID
106 */
107 public MCRUser(String id) {
108 super.ID = id.trim();
109 }
110
111 /**
112 * This constructor takes all attributes of this class as single variables.
113 *
114 * @param numID
115 * (int) the numerical user ID
116 * @param ID
117 * the named user ID
118 * @param idEnabled
119 * (boolean) specifies whether the account is disabled or enabled
120 * @param updateAllowed
121 * (boolean) specifies whether the user may update his or her
122 * data
123 * @param description
124 * description of the user
125 * @param passwd
126 * password of the user (encrypted or not encrypted, depending on
127 * property)
128 * @param primaryGroupID
129 * the ID of the primary group of the user
130 * @param groupIDs
131 * a ArrayList of groups (IDs) the user belongs to
132 * @param salutation
133 * contact information
134 * @param firstname
135 * contact information
136 * @param lastname
137 * contact information
138 * @param street
139 * contact information
140 * @param city
141 * contact information
142 * @param postalcode
143 * contact information
144 * @param country
145 * contact information
146 * @param state
147 * contact information
148 * @param institution
149 * contact information
150 * @param faculty
151 * contact information
152 * @param department
153 * contact information
154 * @param institute
155 * contact information
156 * @param telephone
157 * telephone number
158 * @param fax
159 * fax number
160 * @param email
161 * email address
162 * @param cellphone
163 * number of cellular phone, if available
164 */
165 public MCRUser(int numID, String ID, String creator, Timestamp creationDate, Timestamp modifiedDate, boolean idEnabled, boolean updateAllowed, String description, String passwd, String primaryGroupID, List<String> groupIDs, String salutation, String firstname, String lastname, String street, String city, String postalcode, String country, String state, String institution, String faculty, String department, String institute, String telephone, String fax, String email, String cellphone) throws MCRException, Exception {
166 // The following data will never be changed by update
167 super.ID = trim(ID, id_len);
168 this.numID = numID;
169 super.creator = trim(creator, id_len);
170
171 // check if the creation and modified timestamp is provided. If not, use
172 // current timestamp
173 if (creationDate == null) {
174 super.creationDate = new Timestamp(new GregorianCalendar().getTime().getTime());
175 } else {
176 super.creationDate = creationDate;
177 }
178
179 if (modifiedDate == null) {
180 super.modifiedDate = new Timestamp(new GregorianCalendar().getTime().getTime());
181 } else {
182 super.modifiedDate = modifiedDate;
183 }
184
185 this.idEnabled = idEnabled;
186 this.updateAllowed = updateAllowed;
187 super.description = trim(description, description_len);
188 this.passwd = trim(passwd, password_len);
189 this.primaryGroupID = trim(primaryGroupID, id_len);
190 this.groupIDs = groupIDs;
191
192 this.userContact = new MCRUserContact(salutation, firstname, lastname, street, city, postalcode, country, state, institution, faculty, department, institute, telephone, fax, email, cellphone);
193 }
194
195 public MCRUser(String userid, String passwd) throws MCRException, Exception {
196 this(MCRUserMgr.instance().getMaxUserNumID() + 1, userid, MCRSessionMgr.getCurrentSession().getCurrentUserID(), null, null, true,
197 true, null, null, MCRConfiguration.instance().getString("MCR.Users.Guestuser.GroupName", "guestgroup"), null, null, null,
198 null, null, null, null, null, null, null, null, null, null, null, null, null, null);
199 }
200
201 /**
202 * It the passes the given JDOM element to a different constructor and after
203 * that encrypts the password if the flag useEncryption is true. This
204 * constructor must only be used if a cleartext password is provided in the
205 * JDOM element user data.
206 *
207 * @param elm
208 * JDOM Element defining a user
209 * @param useEncryption
210 * flag to determine if the password has to be encrypted
211 */
212 public MCRUser(Element elm, boolean useEncryption) {
213 this(elm);
214
215 if (useEncryption) {
216 String cryptPwd = MCRCrypt.crypt(this.passwd);
217 this.passwd = cryptPwd;
218 }
219 }
220
221 /**
222 * This constructor creates the data of this object from a given JDOM
223 * element.
224 *
225 * @param elm
226 * JDOM Element defining a user
227 */
228 @SuppressWarnings("unchecked")
229 public MCRUser(Element elm) {
230 this();
231
232 if (!elm.getName().equals("user")) {
233 return;
234 } // Detlev asks: Jens, what is this good for??
235
236 super.ID = trim(elm.getAttributeValue("ID"), id_len);
237
238 String numIDtmp = trim(elm.getAttributeValue("numID"));
239
240 try {
241 this.numID = Integer.parseInt(numIDtmp);
242 } catch (Exception e) {
243 this.numID = -1;
244 }
245
246 this.idEnabled = (elm.getAttributeValue("id_enabled").equals("true")) ? true : false;
247 this.updateAllowed = (elm.getAttributeValue("update_allowed").equals("true")) ? true : false;
248 this.creator = trim(elm.getChildTextTrim("user.creator"), id_len);
249 this.passwd = trim(elm.getChildTextTrim("user.password"), password_len);
250
251 String tmp = elm.getChildTextTrim("user.creation_date");
252
253 if (tmp != null) {
254 try {
255 super.creationDate = Timestamp.valueOf(tmp);
256 } catch (Exception e) {
257 }
258 }
259
260 tmp = elm.getChildTextTrim("user.last_modified");
261
262 if (tmp != null) {
263 try {
264 super.modifiedDate = Timestamp.valueOf(tmp);
265 } catch (Exception e) {
266 }
267 }
268
269 this.description = trim(elm.getChildTextTrim("user.description"), description_len);
270 this.primaryGroupID = trim(elm.getChildTextTrim("user.primary_group"), id_len);
271
272 Element contactElement = elm.getChild("user.contact");
273
274 if (contactElement != null) {
275 userContact = new MCRUserContact(contactElement);
276 }
277
278 Element userGroupElement = elm.getChild("user.groups");
279
280 if (userGroupElement != null) {
281 List<Element> groupIDList = userGroupElement.getChildren();
282
283 for (int j = 0; j < groupIDList.size(); j++) {
284 Element groupID = (Element) groupIDList.get(j);
285
286 if (!(groupID.getTextTrim()).equals("")) {
287 groupIDs.add(groupID.getTextTrim());
288 }
289 }
290 }
291 }
292
293 /**
294 * @return This method returns the contact object of the user
295 */
296 public MCRUserContact getUserContact() {
297 return (MCRUserContact) userContact.clone();
298 }
299
300 /**
301 * @return This method returns the ID (user ID or group ID) of the user
302 * object.
303 */
304 public final String getID() {
305 return ID;
306 }
307
308 /**
309 * This method removes a group from the groups list of the user object. This
310 * list is the list of group IDs where this user or group itself is a member
311 * of, not the list of groups this user or group has as members.
312 *
313 * @param groupID
314 * ID of the group removed from the user object
315 */
316 public void removeGroupID(String groupID) throws MCRException {
317 // Since this operation is a modification of the group with ID groupID
318 // and not of
319 // the current group we do not need to check if the modification is
320 // allowed.
321 if (groupIDs.contains(groupID)) {
322 groupIDs.remove(groupID);
323 }
324 }
325
326 /**
327 * This method adds a group to the groups list of the user object. This is
328 * the list of group IDs where this user or group itself is a member of, not
329 * the list of groups this user or group has as members.
330 *
331 * @param groupID
332 * ID of the group added to the user object
333 */
334 public void addGroupID(String groupID) throws MCRException {
335 // Since this operation is a modification of the group with ID groupID
336 // and not of
337 // the current group we do not need to check if the modification is
338 // allowed.
339 if (!groupIDs.contains(groupID)) {
340 groupIDs.add(groupID);
341 }
342 }
343
344 /**
345 * This method determines the list of all groups the user is a member of,
346 * including the implicit ones. That means: if user u is a member of group
347 * G1 and G1 is member of group G2 and G2 itself is member of G3, then user
348 * u is considered to be an implicit member of groups G2 and G3.
349 *
350 * @return list of all groups the user is a member of
351 * @deprecated use getGroupIDs instead
352 */
353 public final List<String> getAllGroupIDs() {
354 return groupIDs;
355 }
356
357 /**
358 * @return This method returns the numerical ID of the user object.
359 */
360 public int getNumID() {
361 return numID;
362 }
363
364 /**
365 * @return This method returns the password of the user.
366 */
367 public String getPassword() {
368 return passwd;
369 }
370
371 /**
372 * @return This method returns the ID of the primary group of the user.
373 */
374 public final String getPrimaryGroupID() {
375 return primaryGroupID;
376 }
377
378 /**
379 * This method checks if the user is authenticated. It does so by querying
380 * the user manager. This information is needed to assign categories in the
381 * access control subsystem.
382 *
383 * @return returns true if the user is authenticated
384 */
385 public final boolean isAuthenticated() {
386 return MCRUserMgr.isAuthenticated(this);
387 }
388
389 /**
390 * @return This method returns true if the user is enabled and may login.
391 */
392 public boolean isEnabled() {
393 return idEnabled;
394 }
395
396 /**
397 * This method checks if the user is member of a given group.
398 *
399 * @param group
400 * Is the user a member of this group?
401 * @return Returns true if the user is a member of the given group.
402 */
403 public boolean isMemberOf(MCRGroup group) {
404 if (this.getPrimaryGroupID().equals(group.getID()) || groupIDs.contains(group.getID())) {
405 return true;
406 }
407
408 return false;
409 }
410
411 /**
412 * @return This method returns true if the user may update his or her data.
413 */
414 public boolean isUpdateAllowed() {
415 return updateAllowed;
416 }
417
418 /**
419 * This method checks if all required fields have been provided. In a later
420 * stage of the software development a User Policy object will be asked,
421 * which fields exactly are the required fields. This will be configurable.
422 *
423 * @return returns true if all required fields have been provided
424 */
425 public boolean isValid() {
426 ArrayList<String> requiredUserAttributes = MCRUserPolicy.instance().getRequiredUserAttributes();
427 boolean test = true;
428
429 if (requiredUserAttributes.contains("userID")) {
430 test = test && (super.ID.length() > 0);
431 }
432
433 if (requiredUserAttributes.contains("numID")) {
434 test = test && (this.numID >= 0);
435 }
436
437 if (requiredUserAttributes.contains("creator")) {
438 test = test && (super.ID.length() > 0);
439 }
440
441 if (requiredUserAttributes.contains("password")) {
442 test = test && (this.passwd.length() > 0);
443 }
444
445 if (requiredUserAttributes.contains("primary_group")) {
446 test = test && (this.primaryGroupID.length() > 0);
447 }
448
449 return test;
450 }
451
452 /**
453 * This method sets the password of the user.
454 *
455 * @param newPassword
456 * The new password of the user
457 */
458 public boolean setPassword(String newPassword) {
459 // We do not allow empty passwords. Later we might check if the password
460 // is
461 // conform with a password policy.
462 if (newPassword == null) {
463 return false;
464 }
465
466 if (modificationIsAllowed()) {
467 if (newPassword.length() != 0) {
468 passwd = trim(newPassword, password_len);
469 super.modifiedDate = new Timestamp(new GregorianCalendar().getTime().getTime());
470
471 return true;
472 }
473 }
474
475 return false;
476 }
477
478 /**
479 * This method sets the "enabled" attribute to a boolean value.
480 *
481 * @param flag
482 * the boolean data
483 */
484 public final void setEnabled(boolean flag) {
485 if (modificationIsAllowed()) {
486 idEnabled = flag;
487 }
488 }
489
490 /**
491 * This method updates this instance with the data of the given MCRUser.
492 *
493 * @param newuser
494 * the data for the update.
495 */
496 public final void update(MCRUser newuser) {
497 // updateAllowed is an attribute of the user object which determines,
498 // whether
499 // the user himself may modify his or her data at all.
500 if (!updateAllowed) {
501 return;
502 }
503
504 if (modificationIsAllowed()) { // check if the current user/session may
505
506 // modify the object
507 idEnabled = newuser.isEnabled();
508 passwd = newuser.getPassword();
509 primaryGroupID = newuser.getPrimaryGroupID();
510 description = newuser.getDescription();
511 groupIDs = newuser.getGroupIDs();
512 userContact = newuser.getUserContact();
513 }
514 }
515
516 /**
517 * @return This method returns the user or group object as a JDOM document.
518 */
519 public Document toJDOMDocument() throws MCRException {
520 Element root = new Element("mycoreuser");
521 root.addNamespaceDeclaration(XSI_NAMESPACE);
522 root.addNamespaceDeclaration(XLINK_NAMESPACE);
523 root.setAttribute("noNamespaceSchemaLocation", "MCRUser.xsd", XSI_NAMESPACE);
524 root.addContent(this.toJDOMElement());
525
526 Document jdomDoc = new Document(root);
527
528 return jdomDoc;
529 }
530
531 /**
532 * This method returns the user object as a JDOM element. This is needed if
533 * one wants to get a representation of several user objects in one xml
534 * document.
535 *
536 * @return this user data as JDOM element
537 */
538 public Element toJDOMElement() throws MCRException {
539 Element user = new Element("user").setAttribute("numID", Integer.toString(numID)).setAttribute("ID", ID).setAttribute("id_enabled", (idEnabled) ? "true" : "false").setAttribute("update_allowed", (updateAllowed) ? "true" : "false");
540 Element Creator = new Element("user.creator").setText(super.creator);
541 Element CreationDate = new Element("user.creation_date").setText(super.creationDate.toString());
542 Element ModifiedDate = new Element("user.last_modified").setText(super.modifiedDate.toString());
543 Element Passwd = new Element("user.password").setText(passwd);
544 Element Description = new Element("user.description").setText(super.description);
545 Element Primarygroup = new Element("user.primary_group").setText(primaryGroupID);
546
547 // Aggregate user element
548 user.addContent(Creator).addContent(CreationDate).addContent(ModifiedDate).addContent(Passwd).addContent(Description).addContent(Primarygroup).addContent(userContact.toJDOMElement());
549
550 // Loop over all group IDs
551 if (groupIDs.size() != 0) {
552 Element Groups = new Element("user.groups");
553 for (int i = 0; i < groupIDs.size(); i++) {
554 Element groupID = new Element("groups.groupID").setText((String) groupIDs.get(i));
555 Groups.addContent(groupID);
556 }
557 user.addContent(Groups);
558 }
559 return user;
560 }
561
562 /**
563 * This method writes debug data to the logger (for the debug mode).
564 */
565 public final void debug() {
566 debugDefault();
567 logger.debug("primaryGroupID = " + primaryGroupID);
568 logger.debug("groupIDs # = " + groupIDs.size());
569 for (int i = 0; i < groupIDs.size(); i++) {
570 logger.debug("groupIDs = " + ((String) groupIDs.get(i)));
571 }
572 userContact.debug();
573 }
574
575 /**
576 * This private helper method checks if the modification of the user object
577 * is allowed for the current user/session.
578 */
579 public final boolean modificationIsAllowed() throws MCRException {
580 // Get the MCRSession object for the current thread from the session
581 // manager.
582 MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
583 String currentUserID = mcrSession.getCurrentUserID();
584
585 MCRGroup primaryGroup = MCRUserMgr.instance().retrieveGroup(primaryGroupID, false);
586
587 if (MCRAccessManager.checkPermission("modify-user")) {
588 return true;
589 } else if (this.ID.equals(currentUserID) || this.creator.equals(currentUserID)) {
590 return true;
591 } else if (primaryGroup.getAdminUserIDs().contains(currentUserID)) {
592 return true;
593 } else { // check if the current user is (direct, not implicit)
594 // member
595
596 // of one of the admGroups
597 ArrayList<String> admGroupIDs = primaryGroup.getAdminGroupIDs();
598
599 for (int i = 0; i < admGroupIDs.size(); i++) {
600 MCRGroup currentGroup = MCRUserMgr.instance().retrieveGroup((String) admGroupIDs.get(i), false);
601
602 if (currentGroup.getMemberUserIDs().contains(mcrSession.getCurrentUserID())) {
603 return true;
604 }
605 }
606 }
607
608 throw new MCRException("The current user " + currentUserID + " has no right to modify the user " + this.ID + ".");
609 }
610
611 /**
612 * @return This method returns the list of groups as a ArrayList of strings.
613 * These are the groups where the object itself is a member of.
614 */
615 public final List<String> getGroupIDs() {
616 return groupIDs;
617 }
618
619 public final int getGroupCount() {
620 try {
621 return groupIDs.size();
622 } catch (Exception e) {
623 return 0;
624 }
625 }
626
627 /**
628 * @see #getID()
629 */
630 public String getName() {
631 return getID();
632 }
633
634 /**
635 * @see #getID()
636 */
637 public String toString() {
638 return getID();
639 }
640
641 public boolean equals(Object obj) {
642 if (!(obj instanceof MCRUser)) {
643 return false;
644 }
645 MCRUser u = (MCRUser) obj;
646 if (this == u) {
647 return true;
648 }
649 if (this.hashCode() != this.hashCode()) {
650 // acording to the hashCode() contract
651 return false;
652 }
653 return fastEquals(u);
654 }
655
656 private boolean fastEquals(MCRUser u) {
657 return (((this.getID() == u.getID()) || (this.getID().equals(u.getID()))) && ((this.getUserContact() == u.getUserContact()) || (this.getUserContact().equals(u.getUserContact()))));
658 }
659
660 public int hashCode() {
661 int result = 17;
662 result = 37 * result + getID().hashCode();
663 result = 37 * result + getUserContact().hashCode();
664 return result;
665 }
666 }