001 /*
002 *
003 * $Revision$ $Date$
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.access.mcrimpl;
025
026 import java.net.UnknownHostException;
027 import java.util.ArrayList;
028 import java.util.Collection;
029 import java.util.Collections;
030 import java.util.Comparator;
031 import java.util.Date;
032 import java.util.HashMap;
033 import java.util.Hashtable;
034 import java.util.Iterator;
035 import java.util.List;
036
037 import org.jdom.Attribute;
038 import org.jdom.Element;
039
040 import org.mycore.access.MCRAccessInterface;
041 import org.mycore.access.MCRAccessBaseImpl;
042 import org.mycore.common.MCRCache;
043 import org.mycore.common.MCRConfiguration;
044 import org.mycore.common.MCRException;
045 import org.mycore.common.MCRSession;
046 import org.mycore.common.MCRSessionMgr;
047 import org.mycore.user.MCRUser;
048 import org.mycore.user.MCRUserMgr;
049
050 /**
051 * MyCoRe-Standard Implementation of the MCRAccessInterface
052 *
053 * Maps object ids to rules
054 *
055 * @author Matthias Kramm
056 * @author Heiko Helmbrecht
057 */
058 public class MCRAccessControlSystem extends MCRAccessBaseImpl {
059
060 public static final String systemRulePrefix = "SYSTEMRULE";
061
062 public static final String poolPrivilegeID = "POOLPRIVILEGE";
063
064 public static final String lexicographicalPattern = "0000000000";
065
066 static String superuserID = MCRConfiguration.instance().getString("MCR.Users.Superuser.UserName", "mcradmin");
067
068 static MCRCache cache;
069
070 MCRAccessStore accessStore;
071
072 MCRRuleStore ruleStore;
073
074 MCRAccessRule dummyRule;
075
076 boolean disabled = false;
077
078 static Hashtable<String, String> ruleIDTable = new Hashtable<String, String>();
079
080 private MCRAccessControlSystem() {
081 MCRConfiguration config = MCRConfiguration.instance();
082 int size = config.getInt("MCR.AccessPool.CacheSize", 2048);
083 String pools = config.getString("MCR.Access.AccessPermissions", "read,write,delete");
084
085 if (pools.trim().length() == 0) {
086 disabled = true;
087 }
088
089 cache = new MCRCache(size, "Access Rules");
090 accessStore = MCRAccessStore.getInstance();
091 ruleStore = MCRRuleStore.getInstance();
092 accessComp = new MCRAccessConditionsComparator();
093
094 nextFreeRuleID = new HashMap<String, Integer>();
095
096 dummyRule = new MCRAccessRule(null, null, null, null, "dummy rule, always true");
097 }
098
099 private static MCRAccessControlSystem singleton;
100
101 private static Comparator<Element> accessComp;
102
103 private static HashMap<String, Integer> nextFreeRuleID;
104
105 // extended methods
106 public static synchronized MCRAccessInterface instance() {
107 if (singleton == null) {
108 singleton = new MCRAccessControlSystem();
109 }
110 return singleton;
111 }
112
113 public void createRule(String ruleString, String creator, String description){
114 String ruleID = getNextFreeRuleID(systemRulePrefix);
115 MCRAccessRule accessRule = new MCRAccessRule(ruleID, creator, new Date(), ruleString, description);
116 ruleStore.createRule(accessRule);
117 }
118
119 public void createRule(Element rule, String creator, String description){
120 createRule(getNormalizedRuleString(rule), creator, description);
121 }
122
123 public void addRule(String id, String pool, Element rule, String description) throws MCRException {
124 MCRRuleMapping ruleMapping = getAutoGeneratedRuleMapping(rule, "System", pool, id, description);
125 String oldRuleID = accessStore.getRuleID(id, pool);
126 if (oldRuleID == null || oldRuleID.equals("")) {
127 accessStore.createAccessDefinition(ruleMapping);
128 } else {
129 accessStore.updateAccessDefinition(ruleMapping);
130 }
131 return;
132 }
133
134 public void addRule(String permission, org.jdom.Element rule, String description) {
135 addRule(poolPrivilegeID, permission, rule, description);
136 }
137
138 public void removeRule(String id, String pool) throws MCRException {
139 MCRRuleMapping ruleMapping = accessStore.getAccessDefinition(pool, id);
140 accessStore.deleteAccessDefinition(ruleMapping);
141 }
142
143 public void removeRule(String permission) throws MCRException {
144 removeRule(poolPrivilegeID, permission);
145 }
146
147 public void removeAllRules(String id) throws MCRException {
148 for (String pool: accessStore.getPoolsForObject(id)) {
149 removeRule(id, pool);
150 }
151 }
152
153 public void updateRule(String id, String pool, org.jdom.Element rule, String description) throws MCRException {
154 MCRRuleMapping ruleMapping = getAutoGeneratedRuleMapping(rule, "System", pool, id, description);
155 String oldRuleID = accessStore.getRuleID(id, pool);
156 if (oldRuleID == null || oldRuleID.equals("")) {
157 LOGGER.debug("updateRule called for id <" + id + "> and pool <" + pool + ">, but no rule is existing, so new rule was created");
158 accessStore.createAccessDefinition(ruleMapping);
159 } else {
160 accessStore.updateAccessDefinition(ruleMapping);
161 }
162 return;
163 }
164
165 public void updateRule(String permission, Element rule, String description) throws MCRException {
166 updateRule(poolPrivilegeID, permission, rule, description);
167 }
168
169 public boolean checkPermission(String id, String permission) {
170 long start = System.currentTimeMillis();
171 MCRSession session = MCRSessionMgr.getCurrentSession();
172 MCRUser user = MCRUserMgr.instance().retrieveUser(session.getCurrentUserID());
173 LOGGER.debug("Get current User took: " + (System.currentTimeMillis() - start));
174 try {
175 boolean returns = checkAccess(id, permission, user, new MCRIPAddress(session.getCurrentIP()));
176 LOGGER.debug("Check " + permission + " on " + id + " took:" + (System.currentTimeMillis() - start));
177 return returns;
178 } catch (MCRException e) {
179 // only return true if access is allowed, we dont know this
180 LOGGER.debug("Error while checking rule.", e);
181 return false;
182 } catch (UnknownHostException e) {
183 // only return true if access is allowed, we dont know this
184 LOGGER.debug("Error while checking rule.", e);
185 return false;
186 }
187 }
188
189 public boolean checkPermission(String id, String permission, MCRUser user) {
190 return checkAccess(id, permission, user, null);
191 }
192
193 public boolean checkPermission(String permission) {
194 LOGGER.debug("Execute MCRAccessControlSystem checkPermission for permission " + permission);
195 boolean ret = checkPermission(poolPrivilegeID, permission);
196 LOGGER.debug("Execute MCRAccessControlSystem checkPermission result: " + (new Boolean(ret)).toString());
197 return ret;
198 }
199
200 public boolean checkPermission(String permission, MCRUser user) {
201 return checkAccess(poolPrivilegeID, permission, user, null);
202 }
203
204 public boolean checkPermission(Element rule) {
205 MCRSession session = MCRSessionMgr.getCurrentSession();
206 String ruleStr = getNormalizedRuleString(rule);
207 MCRAccessRule accessRule = new MCRAccessRule(null, "System", new Date(), ruleStr, "");
208 try {
209 return accessRule.checkAccess(MCRUserMgr.instance().retrieveUser(session.getCurrentUserID()), new Date(), new MCRIPAddress(session.getCurrentIP()));
210 } catch (MCRException e) {
211 // only return true if access is allowed, we dont know this
212 LOGGER.debug("Error while checking rule.", e);
213 return false;
214 } catch (UnknownHostException e) {
215 // only return true if access is allowed, we dont know this
216 LOGGER.debug("Error while checking rule.", e);
217 return false;
218 }
219 }
220
221 public Element getRule(String objID, String permission) {
222 MCRAccessRule accessRule = getAccess(objID, permission);
223 MCRRuleParser parser = new MCRRuleParser();
224 Element rule = parser.parse(accessRule.rule).toXML();
225 Element condition = new Element("condition");
226 condition.setAttribute("format", "xml");
227 if (rule != null) {
228 condition.addContent(rule);
229 }
230 return condition;
231 }
232
233 public Element getRule(String permission) {
234 return getRule(poolPrivilegeID, permission);
235 }
236
237 public String getRuleDescription(String permission) {
238 return getRuleDescription(poolPrivilegeID, permission);
239 }
240
241 public String getRuleDescription(String objID, String permission) {
242 MCRAccessRule accessRule = getAccess(objID, permission);
243 if (accessRule != null && accessRule.getDescription() != null)
244 return accessRule.getDescription();
245 return "";
246 }
247
248 public Collection<String> getPermissionsForID(String objid) {
249 Collection<String> ret = accessStore.getPoolsForObject(objid);
250 return ret;
251 }
252
253 public Collection<String> getPermissions() {
254 return accessStore.getPoolsForObject(poolPrivilegeID);
255 }
256
257 public boolean hasRule(String id, String permission) {
258 return accessStore.existsRule(id, permission);
259 }
260
261 public boolean hasRule(String id) {
262 return hasRule(id, null);
263 }
264
265 public Collection<String> getAllControlledIDs() {
266 return accessStore.getDistinctStringIDs();
267 }
268
269 // not extended methods
270
271 public boolean isDisabled() {
272 return disabled;
273 }
274
275 public MCRAccessRule getAccess(String objID, String pool) {
276 if (disabled) {
277 return dummyRule;
278 }
279 LOGGER.debug("accessStore.getRuleID()");
280 String ruleID = accessStore.getRuleID(objID, pool);
281 LOGGER.debug("accessStore.getRuleID() done.");
282 if (ruleID == null) {
283 return null;
284 }
285 MCRAccessRule a = (MCRAccessRule) cache.get(ruleID);
286 if (a == null) {
287 LOGGER.debug("ruleStore.getRule()");
288 a = ruleStore.getRule(ruleID);
289 LOGGER.debug("ruleStore.getRule() done");
290 if (a != null) {
291 cache.put(ruleID, a);
292 }
293 }
294 return a;
295 }
296
297 /**
298 * Validator methods to validate access definition for given object and pool
299 *
300 * @param permission
301 * poolname as string
302 * @param objID
303 * MCRObjectID as string
304 * @param user
305 * MCRUser
306 * @param ip
307 * ip-Address
308 * @return true if access is granted according to defined access rules
309 */
310 public boolean checkAccess(String objID, String permission, MCRUser user, MCRIPAddress ip) {
311 Date date = new Date();
312 LOGGER.debug("getAccess()");
313 MCRAccessRule rule = getAccess(objID, permission);
314 LOGGER.debug("getAccess() is done");
315 if (rule == null) {
316 if (user.getID().equals(superuserID)) {
317 return true;
318 }
319 return false;
320 }
321 return rule.checkAccess(user, date, ip);
322 }
323
324 /**
325 * method that delivers the next free ruleID for a given Prefix and sets the
326 * counter to counter + 1
327 *
328 * @param prefix
329 * String
330 * @return String
331 */
332 public synchronized String getNextFreeRuleID(String prefix) {
333 int nextFreeID;
334 String sNextFreeID;
335 if (nextFreeRuleID.containsKey(prefix)) {
336 nextFreeID = nextFreeRuleID.get(prefix).intValue();
337 } else {
338 nextFreeID = ruleStore.getNextFreeRuleID(prefix);
339 }
340 sNextFreeID = lexicographicalPattern + String.valueOf(nextFreeID);
341 sNextFreeID = sNextFreeID.substring(sNextFreeID.length() - lexicographicalPattern.length());
342 nextFreeRuleID.put(prefix, new Integer(nextFreeID + 1));
343 return prefix + sNextFreeID;
344 }
345
346 /**
347 * delivers the rule as string, after normalizing it via sorting with
348 * MCRAccessConditionsComparator
349 *
350 * @param rule
351 * Jdom-Element
352 * @return String
353 */
354 public String getNormalizedRuleString(Element rule) {
355 if (rule.getChildren() == null || rule.getChildren().size() == 0) {
356 return "false";
357 }
358 Element normalizedRule = normalize((Element) rule.getChildren().get(0));
359 MCRRuleParser parser = new MCRRuleParser();
360 return parser.parse(normalizedRule).toString();
361 }
362
363 /**
364 * returns a auto-generated MCRRuleMapping, needed to create Access
365 * Definitions
366 *
367 * @param rule
368 * JDOM-Representation of a MCRAccess Rule
369 * @param creator
370 * String
371 * @param pool
372 * String
373 * @param id
374 * String
375 * @return MCRRuleMapping
376 */
377 public MCRRuleMapping getAutoGeneratedRuleMapping(Element rule, String creator, String pool, String id, String description) {
378 String ruleString = getNormalizedRuleString(rule);
379 String ruleID = ruleIDTable.get(ruleString);
380 if ((ruleID == null) || (ruleID.length() == 0)) {
381 Collection<String> existingIDs = ruleStore.retrieveRuleIDs(ruleString, description);
382 if (existingIDs != null && existingIDs.size() > 0) {
383 // rule yet exists
384 ruleID = existingIDs.iterator().next();
385 } else {
386 ruleID = getNextFreeRuleID(systemRulePrefix);
387 MCRAccessRule accessRule = new MCRAccessRule(ruleID, creator, new Date(), ruleString, description);
388 ruleStore.createRule(accessRule);
389 }
390 ruleIDTable.put(ruleString, ruleID);
391 }
392 MCRRuleMapping ruleMapping = new MCRRuleMapping();
393 ruleMapping.setCreator(creator);
394 ruleMapping.setCreationdate(new Date());
395 ruleMapping.setPool(pool);
396 ruleMapping.setRuleId(ruleID);
397 ruleMapping.setObjId(id);
398 return ruleMapping;
399 }
400
401 /**
402 * method, that normalizes the jdom-representation of a mycore access
403 * condition
404 *
405 * @param rule
406 * condition-JDOM of an access-rule
407 * @return the normalized JDOM-Rule
408 */
409 @SuppressWarnings("unchecked")
410 public Element normalize(Element rule) {
411 Element newRule = new Element(rule.getName());
412 for (Iterator it = rule.getAttributes().iterator(); it.hasNext();) {
413 Attribute att = (Attribute) it.next();
414 newRule.setAttribute((Attribute) att.clone());
415 }
416 List children = rule.getChildren();
417 if (children == null || children.size() == 0)
418 return newRule;
419 List<Element> newList = new ArrayList<Element>();
420 for (Iterator it = children.iterator(); it.hasNext();) {
421 Element el = (Element) it.next();
422 newList.add((Element) el.clone());
423 }
424 Collections.sort(newList, accessComp);
425 for (Iterator<Element> it = newList.iterator(); it.hasNext();) {
426 Element el = it.next();
427 newRule.addContent(normalize(el));
428 }
429 return newRule;
430 }
431
432 /**
433 * A Comparator for the Condition Elements for normalizing the access
434 * conditions
435 *
436 */
437 private class MCRAccessConditionsComparator implements Comparator<Element> {
438
439 public int compare(Element el0, Element el1) {
440 String nameEl0 = el0.getName().toLowerCase();
441 String nameEl1 = el1.getName().toLowerCase();
442 int nameCompare = nameEl0.compareTo(nameEl1);
443 // order "boolean" before "condition"
444 if (nameCompare != 0)
445 return nameCompare;
446 if (nameEl0.equals("boolean")) {
447 String opEl0 = el0.getAttributeValue("operator").toLowerCase();
448 String opEl1 = el0.getAttributeValue("operator").toLowerCase();
449 return opEl0.compareTo(opEl1);
450 } else if (nameEl0.equals("condition")) {
451 String fieldEl0 = el0.getAttributeValue("field").toLowerCase();
452 String fieldEl1 = el1.getAttributeValue("field").toLowerCase();
453 int fieldCompare = fieldEl0.compareTo(fieldEl1);
454 if (fieldCompare != 0)
455 return fieldCompare;
456 String valueEl0 = el0.getAttributeValue("value");
457 String valueEl1 = el1.getAttributeValue("value");
458 return valueEl0.compareTo(valueEl1);
459 }
460 return 0;
461 }
462 }
463
464 public static MCRCache getCache() {
465 return cache;
466 };
467
468 };