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 }