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.util.ArrayList;
030    import java.util.HashSet;
031    import java.util.Iterator;
032    import java.util.List;
033    import java.util.Set;
034    
035    import org.apache.log4j.Logger;
036    import org.jdom.Element;
037    import org.mycore.access.MCRAccessManager;
038    import org.mycore.common.MCRCache;
039    import org.mycore.common.MCRConfiguration;
040    import org.mycore.common.MCRException;
041    import org.mycore.common.MCRSession;
042    import org.mycore.common.MCRSessionMgr;
043    
044    /**
045     * This class is the user (and group) manager of the MyCoRe system. It is
046     * implemented using the singleton design pattern in order to ensure that there
047     * is only one instance of this class, i.e. one user manager, running. The user
048     * manager has several responsibilities. First it serves as a facade for client
049     * classes such as MyCoRe-Servlets to retrieve objects from the persistent
050     * datastore. Then the manager is used by the user and group objects themselves
051     * to manage their existence in the underlying datastore.
052     * 
053     * @author Detlev Degenhardt
054     * @author Jens Kupferschmidt
055     * @version $Revision: 14994 $ $Date: 2009-01-29 09:42:52 +0100 (Do, 29. Jan
056     *          2009) $
057     */
058    public class MCRUserMgr {
059        /** The LOGGER and the configuration */
060        private static Logger LOGGER = Logger.getLogger(MCRUserMgr.class.getName());
061    
062        private static MCRConfiguration CONFIG = null;
063    
064        //    private static MCRAccessInterface AI;
065    
066        /**
067         * flag that determines whether write access to the data is denied (true) or
068         * allowed
069         */
070        private boolean locked = false;
071    
072        /** flag that determines whether we use password encryption */
073        private boolean useEncryption = false;
074    
075        /** the user cache */
076        private MCRCache userCache;
077    
078        /** the group cache */
079        private MCRCache groupCache;
080    
081        /** the class responsible for persistent datastore (configurable ) */
082        private MCRUserStore mcrUserStore;
083    
084        /** The one and only instance of this class */
085        private static MCRUserMgr theInstance = null;
086    
087        /**
088         * private constructor to create the singleton instance.
089         */
090        private MCRUserMgr() throws MCRException {
091            CONFIG = MCRConfiguration.instance();
092    
093            String userStoreName = CONFIG.getString("MCR.Persistence.User.Store.Class");
094            useEncryption = CONFIG.getBoolean("MCR.Users.UsePasswordEncryption", false);
095    
096            try {
097                mcrUserStore = (MCRUserStore) Class.forName(userStoreName).newInstance();
098            } catch (Exception e) {
099                throw new MCRException("MCRUserStore error", e);
100            }
101    
102            userCache = new MCRCache(20, "UserMgr users"); // resonable values?
103            // This might also be
104            groupCache = new MCRCache(10, "UserMgr groups"); // read from
105            // mycore.properties....
106        }
107    
108        /**
109         * This method is the only way to get an instance of this class. It calls
110         * the private constructor to create the singleton.
111         * 
112         * @return returns the one and only instance of <CODE>MCRUserMgr</CODE>
113         */
114        public final static synchronized MCRUserMgr instance() throws MCRException {
115            if (theInstance == null) {
116                theInstance = new MCRUserMgr();
117            }
118    
119            return theInstance;
120        }
121    
122        /**
123         * This method checks the consistency of the user and group data. It should
124         * be executed after importing data from xml files, e.g.
125         */
126        public final void checkConsistency() throws MCRException {
127            locked = true; // we now run in the read only mode
128    
129            // Get the MCRSession object for the current thread from the session
130            // manager.
131            MCRSession session = MCRSessionMgr.getCurrentSession();
132    
133            if (session == null) {
134                return;
135            }
136            // For this action you must have list rights.
137            if (!MCRAccessManager.checkPermission("administrate-user") && !session.getCurrentUserID().equals("administrator")) {
138                locked = false;
139                throw new MCRException("The session does not have the permission \"administrate-user\"!");
140            }
141    
142            // For all users in the system get their groups and check if the groups
143            // really exist at all.
144            // We do not need to check if the user is a member of the groups listed
145            // in his or her groups
146            // list since the user object is constructed from the data - so he or
147            // she *must* be a member
148            // by definition. However, since the primary group of the user is added
149            // automatically to the
150            // group list in MCRUser, we have to check if this group has the user as
151            // one of its members.
152            LOGGER.info("Consistency check is started.");
153    
154            List<String> allUserIDs = mcrUserStore.getAllUserIDs();
155    
156            for (String userID : allUserIDs) {
157                MCRUser currentUser = retrieveUser(userID, true);
158                List<String> currentGroupIDs = currentUser.getGroupIDs();
159    
160                for (String gid : currentGroupIDs) {
161                    if (!mcrUserStore.existsGroup(gid)) {
162                        LOGGER.error("user : '" + currentUser.getID() + "' error: unknown group '" + gid + "'!");
163                    }
164                }
165    
166                MCRGroup primaryGroup = retrieveGroup(currentUser.getPrimaryGroupID(), true);
167                ArrayList<String> mbrUserIDs = primaryGroup.getMemberUserIDs();
168    
169                if (!mbrUserIDs.contains(currentUser.getID())) {
170                    LOGGER.error("user : '" + currentUser.getID() + "' error: is not member of" + " primary group '"
171                            + currentUser.getPrimaryGroupID() + "'!");
172                }
173            }
174    
175            // For all groups get the admins and members (user and group lists,
176            // respectively) and check
177            // if they have unknown users as admins or members, unknown groups as
178            // admins or members etc.
179            List<String> allGroupIDs = mcrUserStore.getAllGroupIDs();
180    
181            for (String gid : allGroupIDs) {
182                MCRGroup currentGroup = retrieveGroup(gid, true);
183    
184                // check the admin users
185                ArrayList<String> admUserIDs = currentGroup.getAdminUserIDs();
186    
187                for (int j = 0; j < admUserIDs.size(); j++) {
188                    if (!mcrUserStore.existsUser((String) admUserIDs.get(j))) {
189                        LOGGER.error("group: '" + currentGroup.getID() + "' error: unknown admin" + " user '" + (String) admUserIDs.get(j)
190                                + "'!");
191                    }
192                }
193    
194                // check the admin groups
195                ArrayList<String> admGroupIDs = currentGroup.getAdminGroupIDs();
196    
197                for (int j = 0; j < admGroupIDs.size(); j++) {
198                    if (!mcrUserStore.existsGroup((String) admGroupIDs.get(j))) {
199                        LOGGER.error("group: '" + currentGroup.getID() + "' error: unknown admin" + " group '" + (String) admGroupIDs.get(j)
200                                + "'!");
201                    }
202                }
203    
204                // check the users (members)
205                ArrayList<String> mbrUserIDs = currentGroup.getMemberUserIDs();
206    
207                for (int j = 0; j < mbrUserIDs.size(); j++) {
208                    if (!mcrUserStore.existsUser((String) mbrUserIDs.get(j))) {
209                        LOGGER.error("group: '" + currentGroup.getID() + "' error: unknown user '" + (String) mbrUserIDs.get(j) + "'!");
210                    }
211                }
212            }
213    
214            LOGGER.info("done.");
215            locked = false; // write access is allowed again
216        }
217    
218        /**
219         * This method creates a group in the datastore (and the group cache as
220         * well). The logical correctness of the data is checked.
221         * 
222         * @param group
223         *            The group object to be created
224         */
225        public final void createGroup(MCRGroup group) throws MCRException {
226            if (locked) {
227                throw new MCRException("The user component is locked. At the moment write access is denied.");
228            }
229            // Check the permissions
230            if ((!MCRAccessManager.checkPermission("create-group"))) {
231                throw new MCRException("The current user does not have the permission to create a group!");
232            }
233            createGroupInternal(group);
234        }
235    
236        /**
237         * This method is internal to create a group.
238         * 
239         * @param group
240         *            The group object to be created
241         */
242        private final synchronized void createGroupInternal(MCRGroup group) throws MCRException {
243            // Check if there really is a non-null group object provided
244            if (group == null) {
245                throw new MCRException("The given group object is null.");
246            }
247            // Check if the given group object is valid.
248            if (!group.isValid()) {
249                throw new MCRException("The given group object is not valid.");
250            }
251            // Check if the group already exists.
252            if (mcrUserStore.existsGroup(group.getID())) {
253                throw new MCRException("The group '" + group.getID() + "' already exists!");
254            }
255            // set the current user ID as administration user
256            MCRSession session = MCRSessionMgr.getCurrentSession();
257            MCRUser admin = retrieveUser(session.getCurrentUserID(), false);
258            if (admin != null) {
259                group.addAdminUserID(admin.getID());
260                group.addAdminGroupID(admin.getPrimaryGroupID());
261            }
262            // add other administration user
263            ArrayList<String> admUserIDs = group.getAdminUserIDs();
264            for (int j = 0; j < admUserIDs.size(); j++) {
265                if (!mcrUserStore.existsUser((String) admUserIDs.get(j))) {
266                    throw new MCRException("Unknown admin userID: " + (String) admUserIDs.get(j));
267                }
268            }
269            // add other primary groups
270            ArrayList<String> admGroupIDs = group.getAdminGroupIDs();
271            for (int j = 0; j < admGroupIDs.size(); j++) {
272                if (((String) admGroupIDs.get(j)).equals(group.getID())) {
273                    continue;
274                }
275                if (!mcrUserStore.existsGroup((String) admGroupIDs.get(j))) {
276                    throw new MCRException("Unknown admin groupID: " + (String) admGroupIDs.get(j));
277                }
278            }
279    
280            try {
281                // Set some data by the manager
282                group.setCreationDate();
283                group.setModifiedDate();
284                group.setCreator(session.getCurrentUserID());
285    
286                // Just create the group. The group must be created before updating
287                // the groups this
288                // group is a member of because the existence of the group will be
289                // checked while
290                // updating the groups.
291                mcrUserStore.createGroup(group);
292            } catch (Exception ex) {
293                // Since something went wrong we delete the previously created
294                // group. We do this
295                // using this.deleteGroup() in order to ensure that already updated
296                // groups will
297                // be resetted to the original state as well.
298                try {
299                    deleteGroup(group.getID());
300                } catch (MCRException e) {
301                }
302    
303                LOGGER.error("Can't create MCRGroup", ex);
304                throw new MCRException("Can't create MCRGroup.", ex);
305            }
306        }
307    
308        /**
309         * This method creates a user in the datastore (and the user cache as well).
310         * The logical correctness of the data is checked.
311         * 
312         * @param user
313         *            The user object which will be created
314         */
315        public final void createUser(MCRUser user) throws MCRException {
316            if (locked) {
317                throw new MCRException("The user component is locked. At the moment write access is denied.");
318            }
319            // Check the permissions
320            if (!MCRAccessManager.checkPermission("create-user")) {
321                throw new MCRException("The current user does not have the permission to create a user!");
322            }
323            createUserInternal(user);
324        }
325    
326        /**
327         * This is the internal method to create a user.
328         * 
329         * @param user
330         *            The user object which will be created
331         */
332        private final synchronized void createUserInternal(MCRUser user) throws MCRException {
333            // Check if there really is a non-null object provided
334            if (user == null) {
335                throw new MCRException("The given user object is null.");
336            }
337            // Check if the given user object is valid.
338            if (!user.isValid()) {
339                throw new MCRException("The given user object is not valid.");
340            }
341            // Check if the user already exists.
342            if (mcrUserStore.existsUser(user.getNumID(), user.getID())) {
343                throw new MCRException("The user '" + user.getID() + "' or numerical ID '" + user.getNumID() + "' already exists!");
344            }
345            // Check if the primary group exists and if so, whether the current user
346            // may modify the group
347            if (!mcrUserStore.existsGroup(user.getPrimaryGroupID())) {
348                throw new MCRException("The primary group of the user '" + user.getID() + "' does not exist.");
349            }
350            MCRGroup primGroup = retrieveGroup(user.getPrimaryGroupID());
351            // Check if the groups the user will be a member of really exist
352            List<String> groupIDs = user.getGroupIDs();
353            for (String gid : groupIDs) {
354                if (!mcrUserStore.existsGroup(gid)) {
355                    throw new MCRException("The user '" + user.getID() + "' is linked to the unknown group '" + gid + "'.");
356                }
357            }
358            try {
359                // Set some data by the manager
360                user.setCreationDate();
361                user.setModifiedDate();
362                MCRSession session = MCRSessionMgr.getCurrentSession();
363                user.setCreator(session.getCurrentUserID());
364    
365                // At first create the user. The user must be created before
366                // updating the groups
367                // because the existence of the user will be checked while updating
368                // the groups.
369                mcrUserStore.createUser(user);
370    
371                // now we update the primary group
372                primGroup.addMemberUserID(user.getID());
373                groupCache.remove(primGroup.getID());
374                mcrUserStore.updateGroup(primGroup);
375    
376                // now update the other groups
377                for (int i = 0; i < groupIDs.size(); i++) {
378                    if (!primGroup.getID().equals(groupIDs.get(i))) {
379                        MCRGroup otherGroup = retrieveGroup((String) groupIDs.get(i), true);
380                        otherGroup.addMemberUserID(user.getID());
381                        groupCache.remove(otherGroup.getID());
382                        mcrUserStore.updateGroup(otherGroup);
383                    }
384                }
385            } catch (MCRException ex) {
386                // Since something went wrong we delete the previously created user.
387                // We do this
388                // using this.deleteUser() in order to ensure that already updated
389                // groups will
390                // be resetted to the original state as well.
391                try {
392                    deleteUser(user.getID());
393                } catch (Exception e) {
394                }
395    
396                throw new MCRException("Can't create user.", ex);
397            }
398        }
399    
400        /**
401         * This method deletes a group from the datastore (and the group cache as
402         * well).
403         * 
404         * @param groupID
405         *            The group ID which will be deleted
406         */
407        public final void deleteGroup(String groupID) throws MCRException {
408            if (locked) {
409                throw new MCRException("The user component is locked. At the moment write" + " access is denied.");
410            }
411            // Check the permissions
412            if (!MCRAccessManager.checkPermission("delete-group")) {
413                throw new MCRException("The current user does not have the permission to delete a group!");
414            }
415            deleteGroupInternal(groupID);
416        }
417    
418        /**
419         * This is the internal method to delete groups.
420         * 
421         * @param groupID
422         *            The group ID which will be deleted
423         */
424        private final synchronized void deleteGroupInternal(String groupID) throws MCRException {
425            // Check if the group exists at all
426            if (!mcrUserStore.existsGroup(groupID)) {
427                throw new MCRException("The group '" + groupID + "' is unknown!");
428            }
429    
430            // Now we check if there are users in the system which have this group
431            // as their primary
432            // group. If so, this group cannot be deleted. First the users must be
433            // updated.
434            List<String> primUserIDs = mcrUserStore.getUserIDsWithPrimaryGroup(groupID);
435    
436            if (primUserIDs.iterator().hasNext()) {
437                throw new MCRException("Group '" + groupID + "' can't be deleted since there" + " are users with '" + groupID
438                        + "' as their primary group. First update or" + " delete the users!");
439            }
440    
441            try {
442                // It is sufficient to remove the members from the
443                // caches. The next time they will be used they will be rebuild from
444                // the datastore and
445                // hence no longer have this group in their group lists.
446                MCRGroup delGroup = retrieveGroup(groupID);
447    
448                for (int i = 0; i < delGroup.getMemberUserIDs().size(); i++) {
449                    userCache.remove(delGroup.getMemberUserIDs().get(i));
450    
451                    MCRUser uuser = retrieveUser((String) delGroup.getMemberUserIDs().get(i), false);
452                    uuser.removeGroupID(groupID);
453                    mcrUserStore.updateUser(uuser);
454                }
455    
456                // Remove all admin items from other groups
457                List<String> ogroups = mcrUserStore.getGroupIDsWithAdminUser(groupID);
458    
459                for (String gid : ogroups) {
460                    groupCache.remove(gid);
461    
462                    MCRGroup agroup = retrieveGroup(gid);
463                    agroup.removeAdminGroupID(groupID);
464                    mcrUserStore.updateGroup(agroup);
465                }
466            } catch (Exception ex) {
467                throw new MCRException("Can't delete group " + groupID, ex);
468            } finally {
469                groupCache.remove(groupID);
470                mcrUserStore.deleteGroup(groupID);
471            }
472            LOGGER.info("Group with ID " + groupID + " deleted.");
473        }
474    
475        /**
476         * This method deletes a user from the datastore (and the user cache as
477         * well).
478         * 
479         * @param userID
480         *            The user ID which will be deleted
481         */
482        public final void deleteUser(String userID) throws MCRException {
483            if (locked) {
484                throw new MCRException("The user component is locked. At the moment write" + " access is denied.");
485            }
486            // Check the permission
487            if (!MCRAccessManager.checkPermission("delete-user")) {
488                throw new MCRException("The current user does not have the permission to delete a user!");
489            }
490            deleteUserInternal(userID);
491        }
492    
493        /**
494         * This is the internal method for delete a user.
495         * 
496         * @param userID
497         *            The user ID which will be deleted
498         */
499        private final synchronized void deleteUserInternal(String userID) throws MCRException {
500            // Check if the user exists at all
501            if (!mcrUserStore.existsUser(userID)) {
502                throw new MCRException("User '" + userID + "' is unknown!");
503            }
504            MCRUser user = retrieveUser(userID, false);
505            if (!user.isUpdateAllowed()) {
506                throw new MCRException("Delete for user '" + userID + "' is not allowed!");
507            }
508            // We have to notify the groups where this user is an administrative
509            // user
510            List<String> adminGroups = mcrUserStore.getGroupIDsWithAdminUser(userID);
511            for (String gid : adminGroups) {
512                try {
513                    MCRGroup adminGroup = retrieveGroup(gid);
514                    adminGroup.removeAdminUserID(userID);
515                    groupCache.remove(adminGroup.getID());
516                    mcrUserStore.updateGroup(adminGroup);
517                } catch (Exception ex) {
518                    LOGGER.warn("Can't remove " + userID + "from group " + gid);
519                }
520            }
521    
522            // We have to notify the groups this user is a member of
523            for (int i = 0; i < user.getGroupIDs().size(); i++) {
524                try {
525                    MCRGroup currentGroup = retrieveGroup((String) user.getGroupIDs().get(i));
526                    currentGroup.removeMemberUserID(userID);
527                    groupCache.remove(currentGroup.getID());
528                    mcrUserStore.updateGroup(currentGroup);
529                } catch (Exception ex) {
530                    LOGGER.warn("Can't remove " + userID + "from group " + adminGroups.get(i));
531                }
532            }
533            userCache.remove(userID);
534            mcrUserStore.deleteUser(userID);
535            LOGGER.info("User with ID " + userID + " deleted.");
536        }
537    
538        /**
539         * This method disables the user.
540         * 
541         * @param userID
542         *            The user object which will be disabled
543         */
544        public final void disableUser(String userID) throws MCRException {
545            enable(userID, false);
546            LOGGER.info("User with ID " + userID + " disabled!");
547        }
548    
549        /**
550         * This method enables the user.
551         * 
552         * @param userID
553         *            The user object which will be enabled
554         */
555        public final void enableUser(String userID) throws MCRException {
556            enable(userID, true);
557            LOGGER.info("User with ID " + userID + " enabled!");
558        }
559    
560        /**
561         * This method gets all group IDs from the persistent datastore and returns
562         * them as a ArrayList of strings.
563         * 
564         * @return ArrayList of strings containing the group IDs of the system.
565         */
566        public final List<String> getAllGroupIDs() throws MCRException {
567            return mcrUserStore.getAllGroupIDs();
568        }
569    
570        /**
571         * This method returns a JDOM presentation of all groups of the system
572         * 
573         * @return JDOM document presentation of all groups of the system
574         */
575        public final org.jdom.Document getAllGroups() throws MCRException {
576            // Build the DOM
577            MCRGroup currentGroup = null;
578            org.jdom.Element root = new org.jdom.Element("mycoregroup");
579            root.addNamespaceDeclaration(XSI_NAMESPACE);
580            root.addNamespaceDeclaration(XLINK_NAMESPACE);
581            root.setAttribute("noNamespaceSchemaLocation", "MCRGroup.xsd", XSI_NAMESPACE);
582    
583            List<String> allGroupIDs = mcrUserStore.getAllGroupIDs();
584    
585            for (String gid : allGroupIDs) {
586                currentGroup = mcrUserStore.retrieveGroup(gid);
587                root.addContent(currentGroup.toJDOMElement());
588            }
589    
590            org.jdom.Document jdomDoc = new org.jdom.Document(root);
591    
592            return jdomDoc;
593        }
594    
595        /**
596         * This method gets all user IDs from the persistent datastore and returns
597         * them as an ArrayList of strings.
598         * 
599         * @return ArrayList of strings containing the user IDs of the system.
600         */
601        public final List<String> getAllUserIDs() throws MCRException {
602            return mcrUserStore.getAllUserIDs();
603        }
604    
605        /**
606         * This method returns a JDOM presentation of all users of the system
607         * 
608         * @return JDOM document presentation of all users of the system
609         */
610        public final org.jdom.Document getAllUsers() throws MCRException {
611            // Build the DOM
612            MCRUser currentUser;
613            org.jdom.Element root = new org.jdom.Element("mycoreuser");
614            root.addNamespaceDeclaration(XSI_NAMESPACE);
615            root.addNamespaceDeclaration(XLINK_NAMESPACE);
616            root.setAttribute("noNamespaceSchemaLocation", "MCRUser.xsd", XSI_NAMESPACE);
617    
618            List<String> allUserIDs = mcrUserStore.getAllUserIDs();
619    
620            for (String uid : allUserIDs) {
621                currentUser = mcrUserStore.retrieveUser(uid);
622                root.addContent(currentUser.toJDOMElement());
623            }
624    
625            org.jdom.Document jdomDoc = new org.jdom.Document(root);
626    
627            return jdomDoc;
628        }
629    
630        /**
631         * The access control subsystem needs to know the current working user. This
632         * information is held in the session, so we just ask the session about it.
633         * 
634         * @return the current user
635         */
636        public final MCRUser getCurrentUser() {
637            // Get the MCRSession object for the current thread from the session
638            // manager.
639            MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
640    
641            return retrieveUser(mcrSession.getCurrentUserID(), false);
642        }
643    
644        /**
645         * This method determines in which groups of a given set of groups a given
646         * user is a member of.
647         * 
648         * @return set of groups the given user is a member of
649         */
650        public final Set<MCRGroup> getGroupsContainingUser(MCRUser user, Set<MCRGroup> groups) {
651            Set<MCRGroup> set = new HashSet<MCRGroup>();
652            Iterator<MCRGroup> iterator = groups.iterator();
653    
654            while (iterator.hasNext()) {
655                Object element = iterator.next();
656    
657                if (element instanceof MCRGroup) {
658                    MCRGroup group = (MCRGroup) element;
659    
660                    if (group.hasUserMember(user)) {
661                        set.add(group);
662                    }
663                }
664            }
665    
666            return set;
667        }
668    
669        /**
670         * This method determines in which groups is the user member.
671         * 
672         * @return an ArrayList of groups where the given user is a member of
673         */
674        public final List<String> getGroupsContainingUser(String user) {
675            MCRUser u = retrieveUser(user);
676            List<String> a = getAllGroupIDs();
677            List<String> o = new ArrayList<String>(a.size());
678            for (String aa : a) {
679                if (retrieveGroup(aa).hasUserMember(u)) {
680                    if (!o.contains(aa)) {
681                        o.add(aa);
682                    }
683                }
684            }
685            return o;
686        }
687    
688        /**
689         * This method returns the maximum value of the numerical user IDs
690         * 
691         * @return maximum value of the numerical user IDs
692         */
693        public final int getMaxUserNumID() throws MCRException {
694            // without checking a permission, because we only want to see it
695            return mcrUserStore.getMaxUserNumID();
696        }
697    
698        /**
699         * This method returns the ID of the primary group for a given userID.
700         * 
701         * @param userID
702         *            the userID for which the primary group ID is requested
703         * @return groupID the ID of the primary group of the user
704         */
705        public final String getPrimaryGroupIDOfUser(String userID) {
706            try {
707                MCRUser u = retrieveUser(userID);
708    
709                return u.getPrimaryGroupID();
710            } catch (MCRException e) {
711            }
712    
713            return null;
714        }
715    
716        /**
717         * This method is used by the initialization process of the user/group
718         * system to create a starting configuration without checking the
719         * consistency of the data. It is also used when importing groups into the
720         * system.
721         * 
722         * @param group
723         *            The group object which should be created
724         * @param creator
725         *            the creator
726         */
727        public final synchronized void initializeGroup(MCRGroup group, String creator) throws MCRException {
728            if (locked) { // This is very unlikely to happen since we are just
729    
730                // initializing the system.
731                throw new MCRException("The user component is locked. At the moment write access is denied.");
732            }
733    
734            if (group == null) {
735                throw new MCRException("The provided group object is null.");
736            }
737    
738            if ((creator == null) || ((creator = creator.trim()).length() == 0)) {
739                throw new MCRException("The value for creator is null or empty.");
740            }
741    
742            if (!group.isValid()) {
743                throw new MCRException("The data of the provided group object is not valid.");
744            }
745    
746            if (mcrUserStore.existsGroup(group.getID())) {
747                throw new MCRException("The group '" + group.getID() + "' already exists!");
748            }
749    
750            try {
751                mcrUserStore.createGroup(group);
752            } catch (Exception ex) {
753                throw new MCRException("Can't initalize group system.", ex);
754            }
755        }
756    
757        /**
758         * This method is used by the initialization process of the user/group
759         * system to create a starting configuration without checking the
760         * consistency of the data. It is also used when importing users into the
761         * system.
762         * 
763         * @param user
764         *            The user object which should be created
765         * @param creator
766         *            The creator
767         */
768        public final synchronized void initializeUser(MCRUser user, String creator) throws MCRException {
769            if (locked) { // This is very unlikely to happen since we are just
770    
771                // initialising the system.
772                throw new MCRException("The user component is locked. At the moment write access is denied.");
773            }
774    
775            if (user == null) {
776                throw new MCRException("The provided user object is null.");
777            }
778    
779            if ((creator == null) || ((creator = creator.trim()).length() == 0)) {
780                throw new MCRException("The value for creator is null or empty.");
781            }
782    
783            if (!user.isValid()) {
784                throw new MCRException("The data of the provided user object is not valid.");
785            }
786    
787            if (mcrUserStore.existsUser(user.getNumID(), user.getID())) {
788                throw new MCRException("The user '" + user.getID() + "' or numerical ID '" + user.getNumID() + "' already exists!");
789            }
790    
791            try {
792                mcrUserStore.createUser(user);
793            } catch (MCRException ex) {
794                throw new MCRException("Can't initialize user system.", ex);
795            }
796        }
797    
798        /**
799         * This method imports groups and user data from XML JDOM trees. It clean
800         * the old existing user and group entries. Only the administrator of from
801         * the property file is authorized to do this command.
802         * 
803         * @param groupfile
804         *            the JDOM tree of the MCRGroups data input
805         * @param userfile
806         *            the JDOM tree of the MCRUsers data input
807         */
808        @SuppressWarnings("unchecked")
809        public final void importUserSystemFromFiles(org.jdom.Element groups, org.jdom.Element users) throws MCRException {
810            // check authorization
811            MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
812            String sessionUser = mcrSession.getCurrentUserID();
813            String propertyUser = CONFIG.getString("MCR.Users.Superuser.UserName", "administrator");
814            if (!sessionUser.equals(propertyUser)) {
815                throw new MCRException("The user " + sessionUser + " is not authorized for import the user system.");
816            }
817            // the user system should not locked
818            if (locked) {
819                throw new MCRException("The user component is locked. At the moment write access is denied.");
820            }
821            // check input for null
822            if (groups == null || users == null) {
823                throw new MCRException("The input JDOM tree is null.");
824            }
825            // clean user system
826            List<String> uids = mcrUserStore.getAllUserIDs();
827            List<String> gids = mcrUserStore.getAllGroupIDs();
828            for (int i = 0; i < uids.size(); i++) {
829                deleteUserInternal(uids.get(i));
830            }
831            for (int i = 0; i < gids.size(); i++) {
832                deleteGroupInternal(gids.get(i));
833            }
834            LOGGER.info("The user system is clean now.");
835            // load elementary group entries
836            List<Element> grouplistelm = groups.getChildren();
837            for (Element groupElement : grouplistelm) {
838                MCRGroup gin = new MCRGroup(groupElement);
839                if (!gin.isValid()) {
840                    throw new MCRException("The data of the provided group object is not valid.");
841                }
842                // Check exist group
843                if (existGroup(gin.getID())) {
844                    throw new MCRException("The group '" + gin.getID() + "' already exists!");
845                }
846                gin.removeAllAdminGroupID();
847                gin.removeAllAdminUserID();
848                gin.removeAllMemberUserID();
849                createGroupInternal(gin);
850                LOGGER.info("Elementary data of group " + gin.getID() + " are imported.");
851            }
852            // load user entries without encryption password
853            List<Element> listelm = users.getChildren();
854            for (Element userElement : listelm) {
855                MCRUser u = new MCRUser(userElement, false);
856                if (!u.isValid()) {
857                    throw new MCRException("The data of the provided user object is not valid.");
858                }
859                // check exist group
860                if (existUser(u.getID())) {
861                    throw new MCRException("The user '" + u.getID() + "' already exists!");
862                }
863                // Check if the primary group exists
864                String primarygroup = u.getPrimaryGroupID();
865                if (!existGroup(primarygroup)) {
866                    throw new MCRException("The primary group of the user '" + u.getID() + "' does not exist.");
867                }
868                // Check if the groups the user will be a member of really exist
869                List<String> groupIDs = u.getGroupIDs();
870                for (int j = 0; j < groupIDs.size(); j++) {
871                    if (!existGroup((String) groupIDs.get(j))) {
872                        throw new MCRException("The user '" + u.getID() + "' is linked to the unknown group '" + groupIDs.get(j) + "'.");
873                    }
874                }
875                // At first create the user. The user must be created before
876                // updating the groups because the existence of the user will
877                // be checked while updating the groups.
878                createUserInternal(u);
879                // now we update the primary group
880                MCRGroup primGroup = retrieveGroup(primarygroup);
881                primGroup.addMemberUserID(u.getID());
882                updateGroupInternal(primGroup);
883                // now update the other groups
884                for (int j = 0; j < groupIDs.size(); j++) {
885                    MCRGroup otherGroup = retrieveGroup((String) groupIDs.get(j));
886                    otherGroup.addMemberUserID(u.getID());
887                    updateGroupInternal(otherGroup);
888                }
889                LOGGER.info("All data of user " + u.getID() + " are imported.");
890            }
891            // complete groups
892            for (int i = 0; i < grouplistelm.size(); i++) {
893                MCRGroup gin = new MCRGroup((org.jdom.Element) grouplistelm.get(i));
894                MCRGroup g = MCRUserMgr.instance().retrieveGroup(gin.getID());
895                // backup up creator and dates
896                g.setAdminGroupIDs(gin.getAdminGroupIDs());
897                g.setAdminUserIDs(gin.getAdminUserIDs());
898                g.setCreator(gin.getCreator());
899                g.setCreationDate(gin.getCreationDate());
900                g.setModifiedDate(gin.getModifiedDate());
901                updateGroupInternal(g);
902                LOGGER.info("All data of group " + gin.getID() + " are imported.");
903            }
904        }
905    
906        /**
907         * This method checks if the user is authenticated, i.e. if he or she is in
908         * the current session.
909         * 
910         * @return returns true if the user is authenticated
911         */
912        public final static boolean isAuthenticated(MCRUser user) {
913            // Get the MCRSession object for the current thread from the session
914            // manager.
915            MCRSession mcrSession = MCRSessionMgr.getCurrentSession();
916    
917            String sessionUser = mcrSession.getCurrentUserID();
918    
919            return (sessionUser.equals(user.getID())) ? true : false;
920        }
921    
922        /**
923         * return This method returns true is if the user manager is in the locked
924         * state
925         */
926        public final boolean isLocked() {
927            return locked;
928        }
929    
930        /**
931         * login to the system. This method just checks the password for a given
932         * user.
933         * 
934         * @param userID
935         *            user ID for the login
936         * @param passwd
937         *            password for the user
938         * @return true if the password matches the password stored, false otherwise
939         */
940        public boolean login(String userID, String passwd) throws MCRException {
941            MCRUser loginUser = retrieveUser(userID, false);
942    
943            if (loginUser == null) {
944                throw new MCRException("Login denied. User does not exist: " + userID);
945            }
946    
947            if (loginUser.isEnabled()) {
948                if (useEncryption) {
949                    String salt = loginUser.getPassword().substring(0, 3);
950                    String newCrypt = MCRCrypt.crypt(salt, passwd);
951    
952                    return (loginUser.getPassword().equals(newCrypt));
953                }
954    
955                return (loginUser.getPassword().equals(passwd));
956            }
957    
958            throw new MCRException("Login denied. User is disabled.");
959        }
960    
961        /**
962         * This method retrieves the group object with the given group ID.
963         * 
964         * @param groupID
965         *            string representing the requested group object
966         * @return MCRGroup group object (if available)
967         * @exception MCRException
968         *                if group object is not known
969         */
970        public MCRGroup retrieveGroup(String groupID) throws MCRException {
971            return this.retrieveGroup(groupID, false);
972        }
973    
974        /**
975         * This method first looks for a given groupID in the group cache and
976         * returns this group object. In case that the group object is not in the
977         * cache, the group will be retrieved from the database. Then the group
978         * object is put into the cache.
979         * 
980         * @param groupID
981         *            string representing the requested group object
982         * @param bFromDataStore
983         *            boolean value, if true the group must be retrieved directly
984         *            from the data store
985         * @return MCRGroup group object (if available)
986         * @exception MCRException
987         *                if group object is not known
988         */
989        protected MCRGroup retrieveGroup(String groupID, boolean bFromDataStore) throws MCRException {
990            // In order to compare a modified group object with the persistent one
991            // we must
992            // be able to force this method to get the group from the store
993            MCRGroup reqGroup = bFromDataStore ? null : (MCRGroup) groupCache.get(groupID);
994    
995            if (reqGroup == null) { // We do not have this group in the cache.
996                try {
997                    reqGroup = mcrUserStore.retrieveGroup(groupID);
998                } catch (MCRException e) {
999                    throw new MCRException("MCRUserMgr.retrieveGroup(): Unknown group '" + groupID + "'!");
1000                }
1001    
1002                if (reqGroup == null) {
1003                    throw new MCRException("MCRUserMgr.retrieveGroup(): Unknown group '" + groupID + "'!");
1004                }
1005    
1006                groupCache.put(groupID, reqGroup);
1007            }
1008    
1009            return reqGroup;
1010        }
1011    
1012        /**
1013         * In the access control subsystem only IDs are stored, not references to
1014         * user or group objects. Therefore the user system must provide a method to
1015         * retrieve a set of groups according to a set of given group IDs.
1016         * 
1017         * @param groupIDs
1018         *            A set of group IDs for which the group objects are to be
1019         *            retrieved
1020         * @return set of groups according to the given set of group IDs
1021         * @throws MCRException
1022         */
1023        public final Set<MCRGroup> retrieveGroups(Set<String> groupIDs) throws MCRException {
1024            Set<MCRGroup> groups = new HashSet<MCRGroup>();
1025            Iterator<String> iterator = groupIDs.iterator();
1026    
1027            while (iterator.hasNext()) {
1028                Object object = iterator.next();
1029    
1030                if (object instanceof String) {
1031                    String groupid = (String) object;
1032                    MCRGroup group = retrieveGroup(groupid);
1033    
1034                    if (group != null) {
1035                        groups.add(group);
1036                    }
1037                }
1038            }
1039    
1040            return groups;
1041        }
1042    
1043        /**
1044         * This method first retrieves the user object with the given userID.
1045         * 
1046         * @param userID
1047         *            string representing the requested user object
1048         * @return MCRUser user object (if available), otherwise null
1049         * @exception MCRException
1050         *                if user object is not known
1051         */
1052        public MCRUser retrieveUser(String userID) throws MCRException {
1053            return this.retrieveUser(userID, false);
1054        }
1055    
1056        /**
1057         * This method first looks for a given userID in the user cache and returns
1058         * this user object. In case that the user object is not in the cache, the
1059         * user will be retrieved from the database. Then the user object is put
1060         * into the cache.
1061         * 
1062         * @param userID
1063         *            string representing the requested user object
1064         * @param bFromDataStore
1065         *            boolean value, if true the user must be retrieved directly
1066         *            from the data store
1067         * @return MCRUser user object (if available), otherwise null
1068         * @exception MCRException
1069         *                if user object is not known
1070         */
1071        protected MCRUser retrieveUser(String userID, boolean bFromDataStore) throws MCRException {
1072            // In order to compare a modified user object with the persistent one we
1073            // must
1074            // be able to force this method to get the user from the store
1075            MCRUser reqUser = bFromDataStore ? null : (MCRUser) userCache.get(userID);
1076    
1077            if (reqUser == null) { // We do not have this user in the cache
1078    
1079                try {
1080                    reqUser = mcrUserStore.retrieveUser(userID);
1081                } catch (MCRException e) {
1082                    LOGGER.warn(e.getMessage());
1083                    throw new MCRException("user can't be found in the database");
1084                }
1085    
1086                if (reqUser != null) {
1087                    userCache.put(userID, reqUser);
1088                }
1089            }
1090    
1091            return reqUser;
1092        }
1093    
1094        /**
1095         * In the access control subsystem only IDs are stored, not references to
1096         * user or group objects. Therefore the user system must provide a method to
1097         * retrieve a set of users according to a set of given user IDs.
1098         * 
1099         * @param userIDs
1100         *            A set of user IDs for which the user objects are to be
1101         *            retrieved
1102         * @return set of users according to the given set of user IDs
1103         * @throws MCRException
1104         */
1105        public final Set<MCRUser> retrieveUsers(Set<String> userIDs) {
1106            Set<MCRUser> users = new HashSet<MCRUser>();
1107            Iterator<String> iterator = userIDs.iterator();
1108    
1109            while (iterator.hasNext()) {
1110                Object object = iterator.next();
1111    
1112                if (object instanceof String) {
1113                    String userid = (String) object;
1114                    MCRUser user = retrieveUser(userid, false);
1115    
1116                    if (user != null) {
1117                        users.add(user);
1118                    }
1119                }
1120            }
1121    
1122            return users;
1123        }
1124    
1125        /**
1126         * This method sets the lock-status of the user manager.
1127         * 
1128         * @param locked
1129         *            flag that determines whether write access to the data is
1130         *            denied (true) or allowed
1131         */
1132        public final synchronized void setLock(boolean locked) {
1133            // Check the permissions
1134            if (!MCRAccessManager.checkPermission("administrate-user")) {
1135                throw new MCRException("The current user does not have the permission to  'administrate-user'!");
1136            }
1137    
1138            this.locked = locked;
1139            if (locked)
1140                LOGGER.info("Write access to the user component persistent database now is denied.");
1141            else
1142                LOGGER.info("Write access to the user component persistent database now is allowed.");
1143        }
1144    
1145        /**
1146         * This method sets a new password for the given user.
1147         * 
1148         * @param userID
1149         *            the userID
1150         * @param password
1151         *            the user password
1152         */
1153        public final void setPassword(String userID, String password) throws MCRException {
1154            // check for empty strings
1155            if ((userID == null) || ((userID = userID.trim()).length() == 0)) {
1156                throw new MCRException("The userID is null or empty!");
1157            }
1158    
1159            if ((password == null) || ((password = password.trim()).length() == 0)) {
1160                throw new MCRException("The password is null or empty!");
1161            }
1162    
1163            // Check permissions
1164            MCRSession session = MCRSessionMgr.getCurrentSession();
1165            MCRUser user = retrieveUser(userID, false);
1166            if (user == null) {
1167                throw new MCRException("Cant find user with ID " + userID + "!");
1168            }
1169    
1170            if (!user.isUpdateAllowed()) {
1171                throw new MCRException("The update for the user " + userID + " is not allowed!");
1172            }
1173    
1174            boolean test = false;
1175    
1176            if (session.getCurrentUserID().equals(userID)) {
1177                test = true;
1178            } else {
1179                if (MCRAccessManager.checkPermission("administrate-user")) {
1180                    test = true;
1181                }
1182            }
1183    
1184            if (!test) {
1185                throw new MCRException("You have no rights to change the users password!");
1186            }
1187    
1188            if (useEncryption) {
1189                user.setPassword(MCRCrypt.crypt(password));
1190            } else {
1191                user.setPassword(password);
1192            }
1193    
1194            userCache.remove(userID);
1195            mcrUserStore.updateUser(user);
1196            LOGGER.info("The new password was set.");
1197        }
1198    
1199        /**
1200         * This method updates a group in the datastore (and the cache as well).
1201         * 
1202         * @param updGroup
1203         *            The group object which will be updated
1204         */
1205        public final void updateGroup(MCRGroup updGroup) throws MCRException {
1206            if (locked) {
1207                throw new MCRException("The user component is locked. At the moment write access is denied.");
1208            }
1209            // Check the permission
1210            if (!MCRAccessManager.checkPermission("modify-group")) {
1211                throw new MCRException("The current user does not have the permission to modify this group!");
1212            }
1213            updateGroupInternal(updGroup);
1214        }
1215    
1216        /**
1217         * This method is internal for update groups.
1218         * 
1219         * @param updGroup
1220         *            The group object which will be updated
1221         */
1222        private final synchronized void updateGroupInternal(MCRGroup updGroup) throws MCRException {
1223            // check that the updGroup is valid
1224            if (updGroup == null) {
1225                throw new MCRException("The provided group object is null!");
1226            }
1227    
1228            if (!updGroup.isValid()) {
1229                throw new MCRException("The provided group object is not valid.");
1230            }
1231    
1232            // check that the group which shall be updated really exists
1233            String groupID = updGroup.getID();
1234    
1235            if (!mcrUserStore.existsGroup(groupID)) {
1236                throw new MCRException("You tried to update the unknown group '" + groupID + "'.");
1237            }
1238    
1239            try {
1240                // get the group directly from the datastore
1241                MCRGroup oldGroup = retrieveGroup(groupID, true);
1242    
1243                // At first we check that admins or members or permissions which may
1244                // be added to this
1245                // group really exist at all. Because this is not a performance
1246                // issue, we simply
1247                // check that all members etc. exist. This should be improved later.
1248                // We look for newly
1249                // added admin users in this group:
1250                for (int i = 0; i < updGroup.getAdminUserIDs().size(); i++) {
1251                    String userID = (String) updGroup.getAdminUserIDs().get(i);
1252                    if (!mcrUserStore.existsUser(userID)) {
1253                        throw new MCRException("You tried to add the unknown admin user '" + userID + "' to the group '" + groupID + "'.");
1254                    }
1255                }
1256    
1257                // We look for newly added admin groups in this group:
1258                for (int i = 0; i < updGroup.getAdminGroupIDs().size(); i++) {
1259                    String gid = (String) updGroup.getAdminGroupIDs().get(i);
1260                    if (!mcrUserStore.existsGroup(gid)) {
1261                        throw new MCRException("You tried to add the unknown admin group '" + gid + "' to the group '" + groupID + "'.");
1262                    }
1263                }
1264    
1265                // We look for newly added member users in this group:
1266                for (int i = 0; i < updGroup.getMemberUserIDs().size(); i++) {
1267                    String userID = (String) updGroup.getMemberUserIDs().get(i);
1268                    if (!mcrUserStore.existsUser(userID)) {
1269                        throw new MCRException("You tried to add the unknown member user '" + userID + "' to the group '" + groupID + "'.");
1270                    }
1271                }
1272    
1273                // Some values are taken from the old version, they cannot be
1274                // updated. Then we
1275                // really update the object in the datastore.
1276                updGroup.setCreationDate(oldGroup.getCreationDate());
1277                updGroup.setCreator(oldGroup.getCreator());
1278                mcrUserStore.updateGroup(updGroup);
1279                groupCache.remove(groupID);
1280                groupCache.put(groupID, updGroup);
1281    
1282                // We finally look again for recently added and deleted members
1283                // (users). If
1284                // so, we do not have to notify them, since the users and groups get
1285                // their membership
1286                // information when they are constructed from the datastore.
1287                // However, we have to remove
1288                // them from the user or group cache so that they will be retrieved
1289                // from the datastore.
1290                for (int i = 0; i < oldGroup.getMemberUserIDs().size(); i++) {
1291                    if (!updGroup.getMemberUserIDs().contains(oldGroup.getMemberUserIDs().get(i))) {
1292                        userCache.remove(oldGroup.getMemberUserIDs().get(i));
1293                    }
1294                }
1295    
1296                for (int i = 0; i < updGroup.getMemberUserIDs().size(); i++) {
1297                    if (!oldGroup.getMemberUserIDs().contains(updGroup.getMemberUserIDs().get(i))) {
1298                        userCache.remove(updGroup.getMemberUserIDs().get(i));
1299                    }
1300                }
1301            } catch (MCRException ex) {
1302                throw new MCRException("Error while updating group " + updGroup.getID(), ex);
1303            }
1304        }
1305    
1306        /**
1307         * This method updates a user in the datastore (and the cache as well).
1308         * 
1309         * @param updUser
1310         *            The user object which will be updated
1311         */
1312        public final synchronized void updateUser(MCRUser updUser) throws MCRException {
1313            if (locked) {
1314                throw new MCRException("The user component is locked. At the moment write access is denied.");
1315            }
1316    
1317            // Check the permissions
1318            if (!MCRAccessManager.checkPermission("modify-user") && !MCRAccessManager.checkPermission("modify-contact")) {
1319                throw new MCRException("The current user does not have the permission to modify this user!");
1320            }
1321    
1322            // Check that the provided user object is valid
1323            if (updUser == null) {
1324                throw new MCRException("The provided user object is null!");
1325            }
1326    
1327            if (!updUser.isValid()) {
1328                throw new MCRException("The provided user object is not valid.");
1329            }
1330    
1331            // Check that the user exists
1332            if (!mcrUserStore.existsUser(updUser.getID())) {
1333                throw new MCRException("You tried to update the unknown user '" + updUser.getID() + "'.");
1334            }
1335    
1336            try {
1337                // get the user directly from the datastore
1338                MCRUser oldUser = mcrUserStore.retrieveUser(updUser.getID());
1339    
1340                // We have to check whether the membership to some of the groups of
1341                // this user changed.
1342                // For example, the user might be removed from one of the groups he
1343                // or she was
1344                // a member of. This group must be notified! To get information
1345                // about which groups
1346                // have been added or removed, we compare the current (updated) user
1347                // object with
1348                // the one from the datastore before the update process takes place.
1349                update(updUser, oldUser);
1350    
1351                // Now we come back to the primary group.
1352                if (!updUser.getPrimaryGroupID().equals(oldUser.getPrimaryGroupID())) {
1353                    MCRGroup primGroup = retrieveGroup(oldUser.getPrimaryGroupID());
1354                    primGroup.removeMemberUserID(oldUser.getID());
1355                    this.updateGroup(primGroup);
1356                    primGroup = retrieveGroup(updUser.getPrimaryGroupID());
1357                    primGroup.addMemberUserID(updUser.getID());
1358                    this.updateGroup(primGroup);
1359                }
1360    
1361                // Some values are taken from the old version, they cannot be
1362                // updated. Then we
1363                // really update the object in the datastore.
1364                updUser.setCreationDate(oldUser.getCreationDate());
1365                updUser.setCreator(oldUser.getCreator());
1366                mcrUserStore.updateUser(updUser);
1367                userCache.remove(updUser.getID());
1368                userCache.put(updUser.getID(), updUser);
1369            } catch (MCRException ex) {
1370                try {
1371                    deleteUser(updUser.getID());
1372                } catch (MCRException e) {
1373                }
1374    
1375                throw new MCRException("Error while updating user " + updUser.getID() + ", the user has been deleted.");
1376            }
1377        }
1378    
1379        /**
1380         * This method updates groups where a given user is a new member of or is no
1381         * longer a member of, respectively. The groups are determined by comparing
1382         * the user to be updated with the version out of the datastore, i.e. before
1383         * the update request.
1384         * 
1385         * @param updObject
1386         *            The user object which is to be updated
1387         * @param oldObject
1388         *            The same user object as it was before the update request.
1389         */
1390        private final void update(MCRUser updUser, MCRUser oldUser) throws MCRException {
1391            // It is important to first check whether *all* groups where the current
1392            // object is a new member of
1393            // exist at all. Furthermore it is very important that the current user
1394            // (session) has the right
1395            // to update the groups. Hence we must check this, too. If something
1396            // goes wrong here an exception
1397            // will be thrown and we do not have to care about a rollback of data
1398            // already modified. Hence do
1399            // not combine the following two loops!
1400            String ID = updUser.getID();
1401    
1402            for (int i = 0; i < updUser.getGroupIDs().size(); i++) {
1403                String gid = (String) updUser.getGroupIDs().get(i);
1404    
1405                if (!mcrUserStore.existsGroup(gid)) {
1406                    StringBuffer msg = new StringBuffer("You tried to update ");
1407                    msg.append((updUser instanceof MCRUser) ? "user '" : "group '").append(ID).append("' with the unknown group '").append(gid)
1408                            .append("'.");
1409                    throw new MCRException(msg.toString());
1410                }
1411            }
1412    
1413            // update groups where this User is a new member of
1414            for (int i = 0; i < updUser.getGroupIDs().size(); i++) {
1415                MCRGroup updGroup = retrieveGroup((String) updUser.getGroupIDs().get(i));
1416    
1417                if (!oldUser.getGroupIDs().contains(updGroup.getID())) {
1418                    updGroup.addMemberUserID(ID);
1419                    this.updateGroup(updGroup);
1420                }
1421            }
1422    
1423            // update groups where this object is no longer a member of
1424            for (int i = 0; i < oldUser.getGroupIDs().size(); i++) {
1425                MCRGroup updGroup = retrieveGroup((String) oldUser.getGroupIDs().get(i));
1426    
1427                if (!updUser.getGroupIDs().contains(updGroup.getID())) {
1428                    // The object is no longer member of this group
1429                    updGroup.removeMemberUserID(ID);
1430                    this.updateGroup(updGroup);
1431                }
1432            }
1433        }
1434    
1435        /**
1436         * This method enables the user.
1437         * 
1438         * @param userID
1439         *            The user object which will be enabled
1440         * @param flag
1441         *            the enable/disable flag
1442         */
1443        private final void enable(String userID, boolean flag) throws MCRException {
1444            if (locked) {
1445                throw new MCRException("The user component is locked. At the moment write access is denied.");
1446            }
1447    
1448            // Check the permissions
1449            if (!MCRAccessManager.checkPermission("administrate-user")) {
1450                throw new MCRException("The session does not have the permission  'administrate-user'!");
1451            }
1452    
1453            if (userID == null) {
1454                throw new MCRException("The userID String is null!");
1455            }
1456    
1457            // check that the user exists
1458            if (!mcrUserStore.existsUser(userID)) {
1459                throw new MCRException("You tried to update the unknown user '" + userID + "'.");
1460            }
1461    
1462            try {
1463                MCRUser olduser = mcrUserStore.retrieveUser(userID);
1464                olduser.setEnabled(flag);
1465                olduser.setModifiedDate();
1466                userCache.remove(olduser.getID());
1467                mcrUserStore.updateUser(olduser);
1468            } catch (MCRException ex) {
1469                throw new MCRException("Error while updating user " + userID);
1470            }
1471        }
1472    
1473        /**
1474         * The method check that a group entry exists.
1475         * 
1476         * @param group
1477         *            the group name as String
1478         * @return true if the group exists, else return false
1479         */
1480        public final boolean existGroup(String group) {
1481            if (group == null)
1482                return false;
1483            return mcrUserStore.existsGroup(group);
1484        }
1485    
1486        /**
1487         * The method check that an user entry exists.
1488         * 
1489         * @param user
1490         *            the user name as String
1491         * @return true if the user exists, else return false
1492         */
1493        public final boolean existUser(String user) {
1494            if (user == null)
1495                return false;
1496            return mcrUserStore.existsUser(user);
1497        }
1498    }