001    /*
002     * 
003     * $Revision: 13712 $ $Date: 2008-07-04 11:59:05 +0200 (Fr, 04 Jul 2008) $
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.frontend.wcms;
025    
026    import java.io.File;
027    import java.io.FileNotFoundException;
028    import java.io.FileOutputStream;
029    import java.io.IOException;
030    import java.util.ArrayList;
031    import java.util.Collections;
032    import java.util.Enumeration;
033    import java.util.Hashtable;
034    import java.util.Iterator;
035    import java.util.List;
036    import java.util.Properties;
037    import java.util.StringTokenizer;
038    
039    import org.apache.log4j.Logger;
040    import org.jdom.Document;
041    import org.jdom.Element;
042    import org.jdom.JDOMException;
043    import org.jdom.input.SAXBuilder;
044    import org.jdom.output.XMLOutputter;
045    import org.jdom.xpath.XPath;
046    import org.mycore.access.MCRAccessManager;
047    import org.mycore.common.MCRConfiguration;
048    import org.mycore.common.MCRException;
049    import org.mycore.frontend.MCRLayoutUtilities;
050    import org.mycore.frontend.cli.MCRAbstractCommands;
051    import org.mycore.frontend.cli.MCRCommand;
052    import org.mycore.user.MCRCrypt;
053    import org.mycore.user.MCRGroup;
054    import org.mycore.user.MCRUser;
055    import org.mycore.user.MCRUserMgr;
056    
057    public class MCRWCMSMigrationCommands extends MCRAbstractCommands {
058        private static Logger LOGGER = Logger.getLogger(MCRWCMSMigrationCommands.class.getName());
059    
060        public MCRWCMSMigrationCommands() {
061            super();
062            MCRCommand com = null;
063    
064            com = new MCRCommand("migrate wcms-users", "org.mycore.frontend.wcms.MCRWCMSMigrationCommands.migrate",
065                            "The command migrates WCMSUserDB.xml into MyCoRe-User-Management.");
066            command.add(com);
067    
068            com = new MCRCommand("simulate migrate wcms-users", "org.mycore.frontend.wcms.MCRWCMSMigrationCommands.simulateMigrate",
069                            "NO database actions will be done. The command simulates a migration of WCMSUserDB.xml into MyCoRe-User-Management.");
070            command.add(com);
071    
072        }
073    
074        public static final void simulateMigrate() {
075            doMigration(true);
076        }
077    
078        public static final void migrate() {
079            doMigration(false);
080        }
081    
082        private static final void doMigration(boolean simulate) {
083            LOGGER.info(getSimText(simulate) + "Migration started\n");
084            // 1
085            Properties props = (Properties) (MCRConfiguration.instance().getProperties().clone());
086            migrateNavi(simulate, props);
087            // 2.1
088            Document dbOrig = loadXML(props.getProperty("MCR.WCMS.wcmsUserDBFile"));
089            Hashtable ht1 = getRootNodes(simulate, props, dbOrig);
090            // 2.3
091            Hashtable ht3 = getUserAndGroups(simulate, ht1, dbOrig);
092            // 3.1
093            Hashtable groupDescr_groupID = createGroups(simulate, ht3, props);
094            // 3.2
095            assignUsers(simulate, ht3, dbOrig, groupDescr_groupID);
096            // 3.3
097            addACLs(simulate, ht1, ht3, groupDescr_groupID);
098            LOGGER.info(getSimText(simulate) + "Migration finished\n");
099        }
100    
101        private static void addACLs(boolean simulate, Hashtable rootNodes, Hashtable userAndGroups, Hashtable groupDes_groupID) {
102            // all root nodes
103            for (Enumeration e = rootNodes.keys(); e.hasMoreElements();) {
104                String rootNode = (String) e.nextElement();
105                // all groups
106                Hashtable groups4Rules = new Hashtable();
107                for (Enumeration e2 = userAndGroups.keys(); e2.hasMoreElements();) {
108                    String userList = (String) e2.nextElement();
109                    String groupDes = getGroupDescrPrefix() + userAndGroups.get(userList).toString();
110                    boolean rootNodeContained = groupDes.contains(rootNode);
111                    if (rootNodeContained) {
112                        String groupID = groupDes_groupID.get(groupDes).toString();
113                        groups4Rules.put(groupID, "");
114                    }
115                }
116                // save rules as acl
117                String aclObjId = MCRLayoutUtilities.getOBJIDPREFIX_WEBPAGE() + rootNode;
118                String writePerm = MCRWCMSUtilities.getWritePermissionWebpage();
119                // // build rule as XML
120                Element rule = new Element("condition");
121                rule.addContent(new Element("boolean").setAttribute("operator", "or"));
122                for (Enumeration e3 = groups4Rules.keys(); e3.hasMoreElements();) {
123                    String groupID = (String) e3.nextElement();
124                    Element cond = new Element("condition");
125                    cond.setAttribute("field", "group");
126                    cond.setAttribute("operator", "=");
127                    cond.setAttribute("value", groupID);
128                    rule.getChild("boolean").addContent(cond);
129                }
130                // // save
131                if (!simulate)
132                    MCRAccessManager.addRule(aclObjId, writePerm, rule, "automatically created ACL for WCMS-Write-Access");
133                LOGGER.info(getSimText(simulate) + "saved ACL for object-ID=" + aclObjId + " with rule=" + rule);
134            }
135            LOGGER.info(getSimText(simulate) + "ACL creation finished, created " + rootNodes.size() + " ACL's\n");
136        }
137    
138        private static void assignUsers(boolean simulate, Hashtable userAndGroups, Document userDB, Hashtable groupDescr_groupID) {
139            MCRUserMgr uMan = MCRUserMgr.instance();
140            for (Enumeration e = userAndGroups.keys(); e.hasMoreElements();) {
141                String userList = (String) e.nextElement();
142                String group4Users = userAndGroups.get(userList).toString();
143                String mcrGroupID = (String) groupDescr_groupID.get(getGroupDescrPrefix() + group4Users);
144                // seperate users
145                StringTokenizer tok = new StringTokenizer(userList, getUserSeperator());
146                while (tok.hasMoreTokens()) {
147                    String user = tok.nextToken().toString();
148                    // assign to group
149                    MCRGroup mcrGroup = null;
150                    if (!simulate)
151                        mcrGroup = uMan.retrieveGroup(mcrGroupID);
152                    if (mcrGroup != null && mcrGroup.hasUserMember(user)) {
153                        LOGGER.info(getSimText(simulate) + "user=" + user + " not added as member to group=" + mcrGroupID + ", because it already exist");
154                    } else {
155                        // user exist ?
156                        if (!uMan.existUser(user))
157                            createUser(simulate, userDB, user, mcrGroup);
158                        if (!simulate)
159                            mcrGroup.addMemberUserID(user);
160                        LOGGER.info(getSimText(simulate) + "added user=" + user + " as member to group=" + mcrGroupID);
161                    }
162                }
163            }
164            LOGGER.info(getSimText(simulate) + "user assignment to groups finished sucessfully");
165        }
166    
167        private static Hashtable createGroups(boolean simulate, Hashtable userAndGroups, Properties props) {
168            String superUserID = props.getProperty("MCR.Users.Superuser.UserName", "administrator");
169            MCRUserMgr uMan = MCRUserMgr.instance();
170            int pos = 0;
171            Hashtable groupID_groupDes = new Hashtable();
172            // all groups
173            for (Enumeration e = userAndGroups.keys(); e.hasMoreElements();) {
174                pos++;
175                String userList = (String) e.nextElement();
176                String group = (String) userAndGroups.get(userList);
177                String groupName = getGroupDescrPrefix() + group;
178    
179                // build mcrGroup
180                int mcrGroupIDNum = getMCRGroupID(pos);
181                String mcrGroupID = getGroupIDPrefix() + Integer.toString(mcrGroupIDNum);
182                MCRGroup mcrGroup = new MCRGroup();
183                mcrGroup.setID(mcrGroupID);
184                mcrGroup.setDescription(groupName);
185                mcrGroup.addAdminUserID(superUserID);
186                // store in db
187                if (!simulate)
188                    uMan.createGroup(mcrGroup);
189                // store mcrGroupID - groupWithRootNodes
190                groupID_groupDes.put(groupName, mcrGroupID);
191                LOGGER.info(getSimText(simulate) + "group=" + mcrGroupID + " (" + groupName + ") added in DB");
192            }
193            LOGGER.info(getSimText(simulate) + "groups creation finished, " + userAndGroups.size() + " groups created");
194            return groupID_groupDes;
195        }
196    
197        private static int getMCRGroupID(int recommendedID) {
198            MCRUserMgr uMan = MCRUserMgr.instance();
199            int newID = recommendedID;
200            while (uMan.existGroup(getGroupIDPrefix() + Integer.toString(newID))) {
201                newID++;
202            }
203            return newID;
204        }
205    
206        private static String getGroupIDPrefix() {
207            return "WCMS_";
208        }
209    
210        private static String getGroupDescrPrefix() {
211            return "WCMS-Editors for: ";
212        }
213    
214        private static Hashtable getUserAndGroups(boolean simulate, Hashtable ht1, Document userDB) {
215            Hashtable userAndGroups = new Hashtable();
216            // all root nodes
217            for (Enumeration e = ht1.keys(); e.hasMoreElements();) {
218                String rootNode = (String) e.nextElement();
219                XPath xpath;
220                List nodes = null;
221                try {
222                    String xpathEx = "//rootNode[text()='" + rootNode + "']";
223                    xpath = XPath.newInstance(xpathEx);
224                    nodes = xpath.selectNodes(userDB);
225                } catch (JDOMException e1) {
226                    e1.printStackTrace();
227                }
228                // get user belonging to this root node
229                Hashtable user4Node = new Hashtable();
230                Iterator selNodeIt = nodes.iterator();
231                while (selNodeIt.hasNext()) {
232                    Element node = (Element) selNodeIt.next();
233                    String user = node.getParentElement().getAttributeValue("userID");
234                    user4Node.put(user, "");
235                }
236                // sort users alphabetically
237                List user4NodeSorted = new ArrayList();
238                Iterator it = user4Node.keySet().iterator();
239                while (it.hasNext()) {
240                    String elem = (String) it.next();
241                    user4NodeSorted.add(elem);
242                }
243                Collections.sort(user4NodeSorted);
244                // create csv version of user4NodeSorted
245                String userList = "";
246                Iterator it2 = user4NodeSorted.iterator();
247                while (it2.hasNext()) {
248                    String user = (String) it2.next();
249                    userList = userList + getUserSeperator() + user.trim();
250                }
251                // add user list with belonging root nodes to hashmap
252                String groups = "";
253                if (userAndGroups.containsKey(userList)) {
254                    groups = userAndGroups.get(userList) + getGroupSeperator() + rootNode;
255                    // LOGGER.info(getSimText(simulate) + "recalculated user(s)=" +
256                    // userList + " to group=" + groups);
257                } else {
258                    groups = rootNode;
259                    // LOGGER.info(getSimText(simulate) + "calculated user(s)=" +
260                    // userList + " to group=" + groups);
261                }
262                userAndGroups.put(userList, groups);
263    
264            }
265            // LOGGER.info(getSimText(simulate) + "2.3 successfully \n");
266            return userAndGroups;
267        }
268    
269        private static String getUserSeperator() {
270            return "#$#$#$#";
271        }
272    
273        private static String getGroupSeperator() {
274            return " AND ";
275        }
276    
277        private static void createUser(boolean simulate, Document userDB, String userID, MCRGroup group) {
278            // get users
279            XPath xpath;
280            Element user = null;
281            try {
282                xpath = XPath.newInstance("//user[@userID='" + userID + "']");
283                user = (Element) xpath.selectSingleNode(userDB);
284            } catch (JDOMException e) {
285                e.printStackTrace();
286            }
287            // verify if exist in system and if not add it
288            MCRUserMgr uMan = MCRUserMgr.instance();
289            if (!uMan.existUser(userID)) {
290                String userName = user.getAttributeValue("userRealName");
291                if (!simulate) {
292                    // encrypt password if property set
293                    String useCrypt = CONFIG.getString("MCR.Users.UsePasswordEncryption", "false");
294                    boolean useEncryption = (useCrypt.trim().equals("true")) ? true : false;
295                    String password = userID;
296                    if (useEncryption)
297                        password = MCRCrypt.crypt(password);
298                    // create user
299                    MCRUser userNew = null;
300                    try {
301                        userNew = new MCRUser(uMan.getMaxUserNumID() + 1, userID, "root", null, null, true, true, "", password, group.getID(), new ArrayList(), "",
302                                        "", userName, "", "", "", "", "", "", "", "", "", "", "", "", "");
303                    } catch (MCRException e) {
304                        e.printStackTrace();
305                    } catch (Exception e) {
306                        e.printStackTrace();
307                    }
308                    uMan.createUser(userNew);
309                }
310                LOGGER.info(getSimText(simulate) + "user=" + userID + " (" + userName + ") missed, so created it\n");
311            }
312        }
313    
314        private static Hashtable getRootNodes(boolean simulate, Properties props, Document userDB) {
315            XPath xpath;
316            List rootNodes = null;
317            try {
318                xpath = XPath.newInstance("//rootNode");
319                rootNodes = xpath.selectNodes(userDB);
320            } catch (JDOMException e) {
321                e.printStackTrace();
322            }
323            Hashtable rootNodesTable = new Hashtable(rootNodes.size());
324            Iterator rootNodeIt = rootNodes.iterator();
325            while (rootNodeIt.hasNext()) {
326                Element categ = (Element) rootNodeIt.next();
327                String rootNode = categ.getTextTrim();
328                rootNodesTable.put(rootNode, "");
329            }
330            // LOGGER.info(getSimText(simulate) + "2.1 successfully \n");
331            return rootNodesTable;
332        }
333    
334        private static void migrateNavi(boolean simulate, Properties props) {
335            String navLoc = props.getProperty("MCR.navigationFile");
336            Document nav = loadXML(navLoc);
337            // save original navi
338            String backupLoc = navLoc + ".orig";
339            if (!simulate)
340                saveXML(backupLoc, nav);
341            // set needed @href's
342            nav.getRootElement().setAttribute("href", nav.getRootElement().getName());
343            Iterator menuIt = nav.getRootElement().getChildren().iterator();
344            while (menuIt.hasNext()) {
345                Element menu = (Element) menuIt.next();
346                menu.setAttribute("href", menu.getName());
347            }
348            if (!simulate)
349                saveXML(navLoc, nav);
350            LOGGER.info(getSimText(simulate) + "navi migrated successfully, original navi backed up under " + backupLoc + " \n");
351        }
352    
353        private static String getSimText(boolean simulate) {
354            if (simulate) {
355                return "NO ACTION - SIMULATION: ";
356            } else {
357                return "";
358            }
359        }
360    
361        private static Document loadXML(String location) {
362            SAXBuilder sax = new SAXBuilder();
363            Document nav = null;
364            try {
365                nav = sax.build(new File(location));
366            } catch (JDOMException e) {
367                e.printStackTrace();
368            } catch (IOException e) {
369                e.printStackTrace();
370            }
371            return nav;
372        }
373    
374        private static void saveXML(String location, Document document) {
375            XMLOutputter out = new XMLOutputter();
376            try {
377                out.output(document, new FileOutputStream(new File(location)));
378            } catch (FileNotFoundException e) {
379                e.printStackTrace();
380            } catch (IOException e) {
381                e.printStackTrace();
382            }
383        }
384    }