KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > security > implementation > cloudcontext > builders > Contexts


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10 package org.mmbase.security.implementation.cloudcontext.builders;
11
12 import org.mmbase.security.implementation.cloudcontext.*;
13 import org.mmbase.security.SecurityException;
14 import org.mmbase.security.Authorization;
15 import org.mmbase.bridge.Query;
16
17 import java.util.*;
18
19 import org.mmbase.storage.search.*;
20 import org.mmbase.storage.search.implementation.*;
21 import org.mmbase.module.core.*;
22 import org.mmbase.module.corebuilders.InsRel;
23 import org.mmbase.cache.Cache;
24 import org.mmbase.security.*;
25 import org.mmbase.util.logging.Logger;
26 import org.mmbase.util.logging.Logging;
27 import org.mmbase.util.functions.*;
28 import org.mmbase.cache.AggregatedResultCache;
29
30 /**
31  * Representation of a 'context', which can be read as a valid value of the 'owner' field of any
32  * object in MMBase. Rights are distributed using this thing. This is part of cloud context
33  * security, so the 'context' values need to be present in the cloud.
34  *
35  * @author Eduard Witteveen
36  * @author Pierre van Rooden
37  * @author Michiel Meeuwissen
38  * @version $Id: Contexts.java,v 1.48.2.1 2006/09/07 12:46:49 pierre Exp $
39  * @see org.mmbase.security.implementation.cloudcontext.Verify
40  * @see org.mmbase.security.Authorization
41  */

42 public class Contexts extends MMObjectBuilder {
43     private static final Logger log = Logging.getLoggerInstance(Contexts.class);
44
45     /**
46      *
47      * @javadoc
48      */

49     static final String JavaDoc DEFAULT_CONTEXT = "default"; // default used to be 'admin', but does that make sense?
50
static final int DEFAULT_MAX_CONTEXTS_IN_QUERY = 50;
51     public final static Parameter PARAMETER_OPERATION = new Parameter("operation", String JavaDoc.class);
52     public final static Parameter PARAMETER_GROUPORUSER = new Parameter("grouporuser", String JavaDoc.class);
53
54     public final static Parameter[] ALLOWS_PARAMETERS = {
55         PARAMETER_GROUPORUSER,
56         PARAMETER_OPERATION
57     };
58
59     public final static Parameter[] PARENTSALLOW_PARAMETERS = ALLOWS_PARAMETERS;
60
61
62     public final static Parameter[] GRANT_PARAMETERS = {
63         PARAMETER_GROUPORUSER,
64         PARAMETER_OPERATION,
65         Parameter.USER
66     };
67
68     public final static Parameter[] REVOKE_PARAMETERS = GRANT_PARAMETERS;
69     public final static Parameter[] MAYGRANT_PARAMETERS = GRANT_PARAMETERS;
70     public final static Parameter[] MAYREVOKE_PARAMETERS = REVOKE_PARAMETERS;
71
72
73     public final static Parameter[] MAY_PARAMETERS = {
74         Parameter.USER,
75         new Parameter("usertocheck", String JavaDoc.class),
76         PARAMETER_OPERATION
77
78     };
79
80
81     protected static Cache contextCache = new Cache(30) { // 30 'contexts' (organisations or so)
82
public String JavaDoc getName() { return "CCS:ContextCache"; }
83             public String JavaDoc getDescription() { return "Links owner field to Contexts MMObjectNodes"; }
84         };
85
86
87     protected static Cache allowingContextsCache = new Cache(200) { // 200 users.
88
public String JavaDoc getName() { return "CCS:AllowingContextsCache"; }
89             public String JavaDoc getDescription() { return "Links user id to a set of contexts"; }
90         };
91     protected static class OperationsCache extends Cache {
92         OperationsCache() {
93             super(100);
94         }
95         public String JavaDoc getName() { return "CCS:SecurityOperations"; }
96         public String JavaDoc getDescription() { return "The groups associated with a security operation";}
97
98         public Object JavaDoc put(MMObjectNode context, Operation op, Set groups) {
99             return super.put(op.toString() + context.getNumber(), groups);
100         }
101         public Set get(MMObjectNode context, Operation op) {
102             return (Set) super.get(op.toString() + context.getNumber());
103         }
104
105     };
106
107     protected static OperationsCache operationsCache = new OperationsCache();
108
109
110
111     /*
112      * Things which must be cleared when some security objects change, can all be collected in this map
113      */

114
115     protected static Map invalidableObjects = new HashMap();
116
117     private boolean readAll = false;
118     private boolean allContextsPossible = true; // if you want to use security for workflow, then you want this to be false
119

120
121     private int maxContextsInQuery = DEFAULT_MAX_CONTEXTS_IN_QUERY;
122
123
124     /**
125      * @javadoc
126      */

127     public boolean init() {
128         String JavaDoc s = (String JavaDoc) getInitParameters().get("readall");
129         readAll = "true".equals(s);
130
131         s = (String JavaDoc) getInitParameters().get("allcontextspossible");
132         allContextsPossible = ! "false".equals(s);
133
134         s = (String JavaDoc) getInitParameters().get("maxcontextsinquery");
135         if (! "".equals(s) && s != null) {
136             maxContextsInQuery = Integer.parseInt(s);
137         }
138
139         contextCache.putCache();
140         allowingContextsCache.putCache();
141         operationsCache.putCache();
142
143         CacheInvalidator.getInstance().addCache(operationsCache);
144         CacheInvalidator.getInstance().addCache(contextCache);
145         CacheInvalidator.getInstance().addCache(allowingContextsCache);
146         CacheInvalidator.getInstance().addCache(invalidableObjects);
147         mmb.addLocalObserver(getTableName(), CacheInvalidator.getInstance());
148         mmb.addRemoteObserver(getTableName(), CacheInvalidator.getInstance());
149
150         return super.init();
151     }
152
153     /**
154      * Staticly receives the MMObjectBuilder instance (cast to Contexts). A utility function.
155      */

156     public static Contexts getBuilder() {
157         return (Contexts) MMBase.getMMBase().getBuilder("mmbasecontexts");
158     }
159
160
161     /**
162      * Implements check function with same arguments of Authorisation security implementation.
163      * @see Verify#check(UserContext, int, int, int, Operation)
164      */

165
166     public boolean mayDo(User user, int nodeId, int sourceNodeId, int destinationNodeId, Operation operation) throws SecurityException JavaDoc {
167         // admin bypasses security system
168
if (user.getRank().getInt() >= Rank.ADMIN_INT) {
169             return true;
170         }
171
172         // interesting right-righs are implemented in mayDo.
173

174         return mayDo(user, nodeId, operation);
175
176     }
177
178
179     /**
180      * Implements check function with same arguments of Authorisation security implementation
181      * @see Verify#check(UserContext, int, Operation)
182      */

183     public boolean mayDo(User user, int nodeId, Operation operation) throws SecurityException JavaDoc {
184
185         // retrieve the node
186
MMObjectNode node = getNode(nodeId);
187
188         MMObjectBuilder builder = null;
189         if (node != null) { // perhaps a node of inactive type
190
builder = node.getBuilder();
191         }
192
193         if (operation == Operation.DELETE) {
194             if (user.getNode() != null && user.getNode().getNumber() == nodeId && operation == Operation.DELETE) return false; // nobody may delete own node
195
if (builder instanceof Contexts) {
196                 try {
197                     Users users = Users.getBuilder();
198                     BasicSearchQuery query = new BasicSearchQuery(true);
199                     Step step = query.addStep(users);
200                     BasicFieldValueConstraint constraint = new BasicFieldValueConstraint(new BasicStepField(step, users.getField("defaultcontext")), new Integer JavaDoc(nodeId));
201                     query.setConstraint(constraint);
202                     BasicAggregatedField baf = query.addAggregatedField((Step) query.getSteps().get(0), users.getField("defaultcontext"), AggregatedField.AGGREGATION_TYPE_COUNT);
203                     baf.setAlias("count");
204
205                     AggregatedResultCache cache = AggregatedResultCache.getCache();
206                     List resultList = (List) cache.get(query);
207                     if (resultList == null) {
208                         ResultBuilder resultBuilder = new ResultBuilder(mmb, query);
209                         resultList = mmb.getSearchQueryHandler().getNodes(query, resultBuilder);
210                         cache.put(query, resultList);
211                     }
212
213                     ResultNode result = (ResultNode) resultList.get(0);
214                     int count = result.getIntValue("count");
215                     if (count > 0) return false;
216
217                     // perhaps should also return false if there are still nodes with this context?
218
// this check is not done in editors, but perhaps it should be bit harder!
219

220                 } catch (SearchQueryException sqe) {
221                     // leave to rest of impl.
222
}
223             }
224
225         }
226
227         // admin bypasses security system
228
if (user.getRank().getInt() >= Rank.ADMIN_INT) {
229             return true;
230         }
231
232
233         if (node == null) {
234             log.warn("node #" + nodeId + " not found");
235             return false;
236         }
237
238         if (readAll && operation == Operation.READ) {
239             return true;
240         }
241
242
243
244         // security-implmentation related issues
245
if (builder instanceof InsRel) {
246             MMObjectNode source = getNode(node.getIntValue("snumber"));
247             MMObjectNode destination = getNode(node.getIntValue("dnumber"));
248
249             if (source.getBuilder() instanceof Users && destination.getBuilder() instanceof Ranks) {
250
251                 // forbid hackery
252
if (operation == Operation.WRITE || operation == Operation.CHANGE_RELATION) {
253                     return false;
254                 }
255                 if (operation == Operation.DELETE || operation == Operation.CREATE) {
256
257                     // only 'high rank' user may change rank at all:
258
if(user.getRank().getInt() <= Rank.BASICUSER.getInt()) return false;
259
260                     // may never unlink/link relation with own rank
261
if (user.getNode() != null && user.getNode().equals(source)) {
262                         log.debug("May not unlink rank with own user object");
263                         return false;
264                     }
265
266                     // may not change rank of higher rank users
267
if (user.getRank().getInt() <= destination.getIntValue("rank")) {
268                         return false;
269                     }
270                 }
271                 // otherwise: ok:
272
return true;
273             }
274             if (builder instanceof RightsRel) {
275                 // forbid hackery
276
if (operation == Operation.WRITE || operation == Operation.CHANGE_RELATION) {
277                     return false;
278                 }
279                 if (operation == Operation.CREATE) {
280                     return mayGrant(source, destination, Operation.getOperation(node.getStringValue("operation")), user.getNode());
281                 }
282                 if (operation == Operation.DELETE) {
283                     return mayRevoke(source, destination, Operation.getOperation(node.getStringValue("operation")), user.getNode());
284                 }
285             }
286             if (source.getBuilder() instanceof Groups && destination.getBuilder() instanceof Users && operation != Operation.READ) {
287                 if (getNode(node.getIntValue("rnumber")).getStringValue("sname").equals("contains")) {
288                     // may not change groups of higher rank users
289
Rank destRank = ((Users) destination.getBuilder()).getRank(destination);
290                     if (user.getRank().getInt() <= destRank.getInt()) {
291                         return false;
292                     }
293                 }
294             }
295         }
296
297
298         // if this is a group node, then you may do anything on it, if you are member of the group,
299
// and you rank is higher then 'basic user'.
300
/*
301         if (builder instanceof Groups) {
302             boolean res = Groups.getBuilder().contains(node, user); // members may see the group
303             if (operation != Operation.READ) {
304                 return res && user.getRank().getInt() > Rank.BASICUSER.getInt();
305             } else {
306                 return res;
307             }
308         }
309         */

310
311         // when it is our user node, and you are this user, you may do anything on it (change password)
312
if (isOwnNode(user, node)) {
313             if ((operation == Operation.READ || operation == Operation.WRITE)) {
314                 if (log.isDebugEnabled()) {
315                     log.debug("May always " + operation + " on own user node: " + nodeId);
316                 }
317                 return true;
318             }
319             if (operation == Operation.DELETE || operation == Operation.CHANGE_CONTEXT) {
320                 // may not delete/give away own user.
321
return false;
322             }
323         }
324
325
326         MMObjectNode contextNode = getContextNode(node); // the mmbasecontext node associated with this node
327
if (contextNode == null) {
328             log.warn("Did not find context node for " + node);
329             return false;
330         }
331         return mayDo(user, contextNode, operation);
332     }
333
334     /**
335      * Returns wether the given node is an 'own' node. It should return true if the node is representing the mmbaseusers object which represents the current user.
336      * Extensions could e.g. also implement returning true for the associated people node.
337      */

338     protected boolean isOwnNode(User user, MMObjectNode node) {
339         MMObjectNode userNode = user.getNode();
340         return (userNode != null && userNode.getBuilder() instanceof Users && userNode.equals(node));
341     }
342
343     /**
344      * Returns wether user may do operation on a node with given context.
345      */

346
347     protected boolean mayDo(User user, MMObjectNode contextNode, Operation operation) {
348         return mayDo(user.getNode(), contextNode, operation, true);
349     }
350
351     protected boolean mayDo(MMObjectNode user, MMObjectNode contextNode, Operation operation, boolean checkOwnRights) {
352
353         Set groupsAndUsers = getGroupsAndUsers(contextNode, operation);
354
355         if (checkOwnRights) {
356             if (groupsAndUsers.contains(user)) return true;
357         }
358
359         Iterator iter = groupsAndUsers.iterator();
360         // now checking if this user is in one of these groups.
361
while (iter.hasNext()) {
362             MMObjectNode group = (MMObjectNode) iter.next();
363             if (! (group.getBuilder() instanceof Groups)) continue;
364             if (log.isDebugEnabled()) log.trace("checking group " + group);
365             if(Groups.getBuilder().contains(group, user)) {
366                 if (log.isDebugEnabled()) {
367                     log.debug("User " + user.getStringValue("username") + " may " + operation + " according to context " + contextNode);
368                 }
369                 return true;
370             }
371         }
372         if (log.isDebugEnabled()) {
373             log.debug("User " + user.getStringValue("username") + " may not " + operation + " according to context " + contextNode);
374         }
375         return false;
376
377     }
378
379
380     /**
381      * Returns a Set (of Strings) of all existing contexts
382      */

383     protected SortedSet getAllContexts() {
384         SortedSet all = (SortedSet) invalidableObjects.get("ALL");
385         if (all == null) {
386             try {
387                 Iterator i = getNodes(new NodeSearchQuery(this)).iterator(); // list all Contextes simply..
388
all = new TreeSet();
389                 while (i.hasNext()) {
390                     MMObjectNode context = (MMObjectNode) i.next();
391                     all.add(context.getStringValue("name"));
392                 }
393
394                 invalidableObjects.put("ALL", Collections.unmodifiableSortedSet(all));
395             } catch (SearchQueryException sqe) {
396                 log.error( Logging.stackTrace(sqe));
397             }
398         }
399         return all;
400     }
401
402     /**
403      * Returns a Set (of Strings) of all existing contexts for which the given operation is not allowed for the given user.
404      */

405     protected SortedSet getDisallowingContexts(User user, Operation operation) {
406         if (operation != Operation.READ) throw new UnsupportedOperationException JavaDoc("Currently only implemented for READ");
407         SortedSet set = new TreeSet();
408         if (!readAll) {
409             Iterator i = getAllContexts().iterator();
410             while (i.hasNext()) {
411                 String JavaDoc context = (String JavaDoc) i.next();
412                 MMObjectNode contextNode = getContextNode(context);
413                 if (! mayDo(user, contextNode, operation)) {
414                     set.add(context);
415                 }
416             }
417         }
418
419         return Collections.unmodifiableSortedSet(set);
420     }
421
422     protected SortedSet getAllowingContexts(User user, Operation operation) {
423         if (operation != Operation.READ) throw new UnsupportedOperationException JavaDoc("Currently only implemented for READ");
424         if (readAll) { return getAllContexts(); }
425
426         SortedSet set = new TreeSet(getAllContexts());
427         set.removeAll(getDisallowingContexts(user, operation));
428
429         return Collections.unmodifiableSortedSet(set);
430
431     }
432
433
434     /**
435      * Implements check function with same arguments of Authorisation security implementation
436      * @see Verify#check(UserContext, Query, Operation)
437      */

438
439     public Authorization.QueryCheck check(User userContext, Query query, Operation operation) {
440         if (userContext.getRank().getInt() >= Rank.ADMIN_INT) {
441             return Authorization.COMPLETE_CHECK;
442         } else {
443             if (operation == Operation.READ && readAll) {
444                 return Authorization.COMPLETE_CHECK;
445             } else if (operation == Operation.READ) {
446
447                 AllowingContexts ac = (AllowingContexts) allowingContextsCache.get(userContext.getIdentifier());
448                 if (ac == null) {
449                     // smart stuff for query-modification
450
SortedSet disallowing = getDisallowingContexts(userContext, operation);
451                     SortedSet contexts;
452                     boolean inverse;
453                     if (log.isDebugEnabled()) {
454                         log.debug("disallowing: " + disallowing + " all " + getAllContexts());
455                     }
456
457                     // searching which is 'smallest': disallowing contexts, or allowing contexts.
458
if (disallowing.size() < (getAllContexts().size() / 2)) {
459                         contexts = disallowing;
460                         inverse = true;
461                     } else {
462                         contexts = new TreeSet(getAllContexts());
463                         contexts.removeAll(disallowing);
464                         inverse = false;
465                     }
466                     ac = new AllowingContexts(contexts, inverse);
467                     allowingContextsCache.put(userContext.getIdentifier(), ac);
468                 }
469
470                 List steps = query.getSteps();
471                 Constraint constraint = null;
472
473                 // constraints on security objects
474
{
475                     Iterator i = steps.iterator();
476                     while (i.hasNext()) {
477                         Step step = (Step) i.next();
478                         Constraint newConstraint = null;
479                         if (step.getTableName().equals("mmbasegroups")) {
480                             newConstraint = query.createConstraint(query.createStepField(step, "number"), userContext.getGroups()); // must be member of group to see group
481
if(operation != Operation.READ) { //
482
if (userContext.getRank().getInt() <= Rank.BASICUSER.getInt()) { // may no nothing, simply making the query result nothing: number = -1
483
Constraint mayNothing = query.createConstraint(query.createStepField(step, "number"), new Integer JavaDoc(-1));
484                                     return new Authorization.QueryCheck(true, mayNothing);
485                                 }
486                             }
487                         } else if (step.getTableName().equals("mmbaseranks")) { // higher ranks are none of your businuess (especially usefull for editors)
488
newConstraint = query.createConstraint(query.createStepField(step, "rank"), FieldCompareConstraint.LESS_EQUAL, new Integer JavaDoc(userContext.getRank().getInt()));
489                         } else {
490                             continue;
491                         }
492
493                         if (constraint == null) {
494                             constraint = newConstraint;
495                         } else {
496                             constraint = query.createConstraint(constraint, CompositeConstraint.LOGICAL_AND, newConstraint);
497                         }
498
499                     }
500                 }
501
502                 if (ac.contexts.size() == 0) {
503                     if (ac.inverse) {
504                         if (constraint == null) {
505                             return Authorization.COMPLETE_CHECK;
506                         } else {
507                             return new Authorization.QueryCheck(true, constraint);
508                         }
509                     } else {
510                         // may read nothing, simply making the query result nothing: number = -1
511
Constraint mayNothing = query.createConstraint(query.createStepField((Step) query.getSteps().get(0), "number"), new Integer JavaDoc(-1));
512                         return new Authorization.QueryCheck(true, mayNothing);
513                     }
514                 }
515
516
517                 if (steps.size() * ac.contexts.size() < maxContextsInQuery) {
518                     Iterator i = steps.iterator();
519                     while (i.hasNext()) {
520                         Step step = (Step) i.next();
521                         StepField field = query.createStepField(step, "owner");
522                         Constraint newConstraint = query.createConstraint(field, ac.contexts);
523                         if (ac.inverse) query.setInverse(newConstraint, true);
524
525                         if (step.getTableName().equals("mmbaseusers")) { // anybody may see own node
526
Users users = Users.getBuilder();
527                             Constraint own = query.createConstraint(query.createStepField(step, "number"),
528                                                                     new Integer JavaDoc(users.getUser(userContext.getIdentifier()).getNumber()));
529                             newConstraint = query.createConstraint(newConstraint, CompositeConstraint.LOGICAL_OR, own);
530                         }
531
532
533                         if (constraint == null) {
534                             constraint = newConstraint;
535                         } else {
536                             constraint = query.createConstraint(constraint, CompositeConstraint.LOGICAL_AND, newConstraint);
537                         }
538                     }
539                     return new Authorization.QueryCheck(true, constraint);
540                 } else { // query would grow too large
541
return Authorization.NO_CHECK;
542                 }
543
544             } else {
545                 //not checking for READ: never mind, this is only used for read checks any way
546
return Authorization.NO_CHECK;
547             }
548         }
549     }
550
551
552     /**
553      * @return The MMObjectNode presenting the context of the given node.
554      */

555     private final MMObjectNode getContextNode(MMObjectNode node) {
556         String JavaDoc s = node.getStringValue("owner");
557         return getContextNode(s);
558
559     }
560
561
562
563
564     /**
565      *
566      * @return A Collection of groups or users which are allowed for the given operation (not recursively)
567      */

568
569     protected Collection getGroupsOrUsers(MMObjectNode contextNode, Operation operation, MMObjectBuilder groupsOrUsers) {
570         InsRel rights = RightsRel.getBuilder();
571
572         BasicSearchQuery query = new BasicSearchQuery();
573         Step step = query.addStep(this);
574         BasicStepField numberStepField = new BasicStepField(step, getField("number"));
575         BasicFieldValueConstraint numberConstraint = new BasicFieldValueConstraint(numberStepField, new Integer JavaDoc(contextNode.getNumber()));
576
577         BasicRelationStep relationStep = query.addRelationStep(rights, groupsOrUsers);
578         relationStep.setDirectionality(RelationStep.DIRECTIONS_DESTINATION);
579
580         BasicStepField operationStepField = new BasicStepField(relationStep, rights.getField("operation"));
581         BasicFieldValueConstraint operationConstraint = new BasicFieldValueConstraint(operationStepField, operation.toString());
582
583         BasicCompositeConstraint constraint = new BasicCompositeConstraint(CompositeConstraint.LOGICAL_AND);
584         constraint.addChild(numberConstraint);
585         constraint.addChild(operationConstraint);
586
587         query.setConstraint(constraint);
588
589         query.addFields(relationStep.getNext());
590
591         try {
592             return groupsOrUsers.getStorageConnector().getNodes(query, false);
593         } catch (SearchQueryException sqe) {
594             log.error(sqe.getMessage());
595             return new ArrayList();
596         }
597
598     }
599
600     /**
601      * @return a Set of all groups and users which are allowed for the given operation (not recursively).
602      */

603     protected Set getGroupsAndUsers(MMObjectNode contextNode, Operation operation) {
604         Set found = operationsCache.get(contextNode, operation);
605         if (found == null) {
606             found = new HashSet();
607
608             found.addAll(getGroupsOrUsers(contextNode, operation, Users.getBuilder()));
609             found.addAll(getGroupsOrUsers(contextNode, operation, Groups.getBuilder()));
610             operationsCache.put(contextNode, operation, found);
611         }
612
613         return found;
614     }
615
616
617
618     protected final MMObjectNode getContextNode(String JavaDoc context) {
619         MMObjectNode contextNode = (MMObjectNode) contextCache.get(context);
620         if (contextNode == null && ! contextCache.contains(context)) {
621             try {
622                 NodeSearchQuery query = new NodeSearchQuery(this);
623                 BasicFieldValueConstraint constraint = new BasicFieldValueConstraint(query.getField(getField("name")), context);
624                 query.setConstraint(constraint);
625                 Iterator i = getNodes(query).iterator();
626                 if (i.hasNext()) {
627                     contextNode = (MMObjectNode)i.next();
628                 } else {
629                     if (! DEFAULT_CONTEXT.equals(context)) {
630                         log.warn("Could not find context '" + context + "' using default context '" + DEFAULT_CONTEXT + "'");
631                         contextNode = getContextNode(DEFAULT_CONTEXT);
632                         if (contextNode == null) {
633                             log.error("Could not find default context '" + DEFAULT_CONTEXT + "'.");
634                         }
635                     }
636                 }
637             } catch (SearchQueryException sqe) {
638                 log.error(sqe.toString());
639                 contextNode = null;
640
641             }
642             contextCache.put(context, contextNode);
643         }
644         return contextNode;
645
646     }
647
648
649
650     /**
651      * Returns this Context node as a String (so the name field)
652      */

653     public String JavaDoc getContext(User user, int nodeId) throws SecurityException JavaDoc {
654         MMObjectNode node = getNode(nodeId);
655         if (node == null) {
656             throw new SecurityException JavaDoc("node #" + nodeId + " not found");
657         }
658         return getContextNode(node).getStringValue("name");
659     }
660
661     /**
662      * Sets the context of a node to a certain String Value
663      * @param user The user doing this.
664      * @param nodeId The number of the node which' context must be changed
665      * @param context The String describing the desired new context
666      * @return The MMObjectNode
667      */

668     public MMObjectNode setContext(User user, int nodeId, String JavaDoc context) throws SecurityException JavaDoc {
669         MMObjectNode node = getNode(nodeId);
670
671         // during creation of a node, the context is set twice!
672
// (in createNode of BasicNodeManager, but also in create of Authorisation implemetentation called
673
// via basicclou .createSecurityInfo(getNumber());)
674
// so not changing must be allowed always.
675
if (node.getStringValue("owner").equals(context)) return node;
676
677         if (node == null) {
678             throw new SecurityException JavaDoc("node #" + nodeId + " not found");
679         }
680
681         /*
682            mm: I think we should try securing groups as well, so removed this (id did not quit understand it any way)
683
684            if (node.getBuilder() instanceof Groups) {
685               node.setValue("owner", "system");
686               node.commit();
687               return node;
688         }
689         */

690         if (context == null || context.equals("")) {
691             //|| context.equals("null")) { // dirty work around bug in editwizard
692
log.warn("Tried to set context to '" + context + "' WRONG!");
693         } else {
694             if (!getPossibleContexts(user, nodeId).contains(context)) {
695                 throw new SecurityException JavaDoc("could not set the context from '" + node.getStringValue("owner") + "' to '" + context + "' for node #" + nodeId + "(context name:" + context + " is not a valid context" + (context == null ? ", but null" : "") + ")");
696             }
697         }
698         node.setValue("owner", context);
699         node.commit();
700         return node;
701     }
702
703     /**
704      * Wraps getPossibleContexts(User, int) of Authorisation implementation Verify.
705      * @see Verify#getPossibleContexts(User, int)
706      * @todo Perhaps we need a possibleContextCache.
707      */

708     public SortedSet getPossibleContexts(User user, int nodeId) throws SecurityException JavaDoc {
709         if (user.getRank().getInt() >= Rank.ADMIN_INT) {
710             // admin may do everything
711
return getAllContexts();
712         }
713
714         MMObjectNode node = getNode(nodeId);
715         if (node == null) {
716             throw new SecurityException JavaDoc("node #" + nodeId + " not found");
717         }
718         if (node.getBuilder() instanceof Groups) {
719             return new TreeSet(); // why?
720
}
721
722         if (allContextsPossible) {
723             return getAllowingContexts(user, Operation.READ);
724         } else {
725             List possibleContexts = getContextNode(node).getRelatedNodes("mmbasecontexts", "allowed", RelationStep.DIRECTIONS_DESTINATION);
726             SortedSet set = new TreeSet();
727             Iterator i = possibleContexts.iterator();
728             while (i.hasNext()) {
729                 MMObjectNode context = (MMObjectNode) i.next();
730                 if (mayDo(user, context.getNumber(), Operation.READ )) {
731                     set.add(context.getStringValue("name"));
732                 } else {
733                     if (log.isDebugEnabled()) {
734                         log.debug("context with name:" + context.getStringValue("name") + " could not be added to possible contexes, since we had no read rights");
735                     }
736                 }
737             }
738             return set;
739         }
740     }
741
742     /**
743      * Wraps getPossibleContexts(User) of Authorisation implementation Verify.
744      * @see Verify#getPossibleContexts(User)
745      * @todo Perhaps we need a possibleContextCache.
746      */

747     public SortedSet getPossibleContexts(User user) throws SecurityException JavaDoc {
748         if (user.getRank().getInt() >= Rank.ADMIN_INT) {
749             // admin may do everything
750
return getAllContexts();
751         } else {
752             return getAllowingContexts(user, Operation.READ);
753         }
754     }
755
756     //********************************************************************************
757
// EDIT FUNCTIONS
758
//********************************************************************************
759

760     /**
761      * Wether users of the given group may do operation on a node of given context (so no following)
762      * @return boolean
763      */

764     protected boolean allows(MMObjectNode contextNode, MMObjectNode groupOrUserNode, Operation operation) {
765         return getGroupsAndUsers(contextNode, operation).contains(groupOrUserNode);
766     }
767
768     /**
769      * Wether users of the given group may do operation on a node of given context, because
770      * (one of) the parents of this group allow it.
771      *
772      * @return boolean
773      */

774     protected boolean parentsAllow(MMObjectNode contextNode, MMObjectNode groupOrUserNode, Operation operation) {
775         try {
776             Groups groups = Groups.getBuilder();
777
778             Set groupsAndUsers = getGroupsAndUsers(contextNode, operation);
779             Iterator i = groupsAndUsers.iterator();
780             while (i.hasNext()) {
781                 MMObjectNode containingGroup = (MMObjectNode) i.next();
782                 if (groups.contains(containingGroup, groupOrUserNode)) return true;
783             }
784         } catch (Throwable JavaDoc e) {
785             log.error(Logging.stackTrace(e));
786         }
787         return false;
788     }
789
790     /**
791      */

792     protected boolean mayGrant(MMObjectNode contextNode, MMObjectNode groupOrUserNode, Operation operation, MMObjectNode user) {
793         Users users = Users.getBuilder();
794         if (users.getRank(user).getInt() >= Rank.ADMIN.getInt()) return true; // admin may do everything
795
Groups groups = Groups.getBuilder();
796
797         if (groupOrUserNode.getBuilder() instanceof Groups) {
798             if (! groups.contains(groupOrUserNode, user.getNumber()) || users.getRank(user).getInt() <= Rank.BASICUSER.getInt()) return false; // must be 'high rank' member of group
799
} else {
800             if (groupOrUserNode.equals(user)) return false; // if not admin, you may not grant yourself rights. (and for admin it is not necessary)
801
if (users.getRank(groupOrUserNode).getInt() >= users.getRank(user).getInt()) return false; // may not grant to users of higher/equal rank than yourself
802
}
803         return mayDo(user, contextNode, operation, true); // you need to have the right yourself to grant it.
804
}
805
806
807
808     /**
809      */

810     protected boolean grant(MMObjectNode contextNode, MMObjectNode groupOrUserNode, Operation operation, MMObjectNode user) {
811         if (allows(contextNode, groupOrUserNode, operation)) return true; // already allowed
812
// create a relation..
813
if (mayGrant(contextNode, groupOrUserNode, operation, user)) {
814             if (log.isServiceEnabled()) {
815                 log.service("Granting right " + operation + " on context " + contextNode + " to group/user " + groupOrUserNode + " by " + user);
816             }
817             RightsRel rightsRel = RightsRel.getBuilder();
818             MMObjectNode ownerContextNode = user.getNodeValue("defaultcontext");
819             String JavaDoc ownerString;
820             if (ownerContextNode != null) {
821                 ownerString = ownerContextNode.getStringValue("name");
822             } else {
823                 ownerString = DEFAULT_CONTEXT;
824             }
825             MMObjectNode newRight = rightsRel.getNewNode(ownerString, contextNode.getNumber(), groupOrUserNode.getNumber(), operation);
826             boolean res = newRight.insert(ownerString) > 0;
827             if (! res) {
828                 log.error("Failed to grant " + newRight);
829             } else {
830                 log.debug("Granted " + newRight);
831             }
832             return res;
833
834         } else {
835             log.service("Granting right " + operation + " on context " + contextNode + " to group/user " + groupOrUserNode + " by " + user + " failed because it it not allowed");
836             return false;
837         }
838     }
839
840
841     /**
842      * Makes sure unique values and not-null's are filed
843      */

844     public void setDefaults(MMObjectNode node) {
845         setUniqueValue(node, "name", "context");
846     }
847
848
849
850
851
852     /**
853      * @todo untested
854      */

855
856     protected boolean mayRevoke(MMObjectNode contextNode, MMObjectNode groupOrUserNode, Operation operation, MMObjectNode user) {
857         Users users = Users.getBuilder();
858         if (users.getRank(user).getInt() >= Rank.ADMIN.getInt()) return true; // admin may do everything
859
if (groupOrUserNode.getBuilder() instanceof Groups) {
860             if (! Groups.getBuilder().contains(groupOrUserNode, user.getNumber()) || users.getRank(user).getInt() <= Rank.BASICUSER.getInt()) return false; // must be 'high rank' member of group
861
} else {
862             if (groupOrUserNode.equals(user)) return false; // if not admin, you may not revoke yourself rights. (and for admin it does not make sense
863
if (users.getRank(groupOrUserNode).getInt() >= users.getRank(user).getInt()) return false; // may not revoke from users of higher/equal rank than yourself
864
}
865         return mayDo(user, contextNode, operation, true); // you need to have the right yourself to revoke it (otherwise you could not grant it back)
866
}
867
868
869
870     /**
871      */

872
873     protected boolean revoke(MMObjectNode contextNode, MMObjectNode groupOrUserNode, Operation operation, MMObjectNode user) {
874         if (!allows(contextNode, groupOrUserNode, operation)) return true; // already disallowed
875

876         if (mayRevoke(contextNode, groupOrUserNode, operation, user)) {
877             if (log.isServiceEnabled()) {
878                 log.service("Revoking right " + operation + " on context " + contextNode + " to group " + groupOrUserNode + " by " + user);
879             }
880             RightsRel rights = RightsRel.getBuilder();
881             NodeSearchQuery q = new NodeSearchQuery(rights);
882             BasicStepField snumber = q.getField(rights.getField("snumber"));
883             BasicStepField dnumber = q.getField(rights.getField("dnumber"));
884             BasicStepField op = q.getField(rights.getField("operation"));
885             BasicFieldValueConstraint c1 = new BasicFieldValueConstraint(snumber, new Integer JavaDoc(contextNode.getNumber()));
886             BasicFieldValueConstraint c2 = new BasicFieldValueConstraint(dnumber, new Integer JavaDoc(groupOrUserNode.getNumber()));
887             BasicFieldValueConstraint c3 = new BasicFieldValueConstraint(op, operation.toString());
888             BasicCompositeConstraint cons = new BasicCompositeConstraint(BasicCompositeConstraint.LOGICAL_AND);
889             cons.addChild(c1);
890             cons.addChild(c2);
891             cons.addChild(c3);
892             q.setConstraint(cons);
893             try {
894                 List r = rights.getNodes(q);
895                 Iterator i = r.iterator();
896                 while (i.hasNext()) {
897                     MMObjectNode right = (MMObjectNode) i.next();
898                     rights.removeNode(right);
899                 }
900             } catch (Exception JavaDoc sqe) {
901                 log.error(sqe.toString());
902                 return false;
903             }
904             return true;
905         } else {
906             log.service("Revoking right " + operation + " on context " + contextNode + " to group/user " + groupOrUserNode + " by " + user + " failed because it it not allowed");
907             return false;
908         }
909
910     }
911
912     /**
913      * util
914      */

915     protected MMObjectNode getUserNode(UserContext user) {
916         Users users = Users.getBuilder();
917         return users.getUser(user.getIdentifier());
918     }
919
920     protected MMObjectNode getGroupOrUserNode(Parameters a) {
921         MMObjectNode groupOrUser = getNode(a.getString(PARAMETER_GROUPORUSER));
922         if (groupOrUser == null) throw new IllegalArgumentException JavaDoc("There is no node with id '" + a.get(PARAMETER_GROUPORUSER) + "'");
923         MMObjectBuilder parent = groupOrUser.getBuilder();
924         if (! (parent instanceof Groups || parent instanceof Users)) {
925             throw new IllegalArgumentException JavaDoc("Node '" + a.get(PARAMETER_GROUPORUSER) + "' does not represent a group or a user");
926         }
927         return groupOrUser;
928     }
929
930     protected Object JavaDoc executeFunction(MMObjectNode node, String JavaDoc function, List args) {
931         if (log.isDebugEnabled()) {
932             log.trace("executefunction of contexts " + function + " " + args);
933         }
934         if (function.equals("info")) {
935             List empty = new ArrayList();
936             Map info = (Map) super.executeFunction(node, function, empty);
937             info.put("allows", "" + ALLOWS_PARAMETERS + " Wether operation may be done according to this context");
938             info.put("parentsallow", "" + PARENTSALLOW_PARAMETERS + " Wether operation may be done by members of this group, also because of parents");
939             info.put("grant", "" + GRANT_PARAMETERS + " Grant a right");
940             info.put("revoke", "" + REVOKE_PARAMETERS + " Revoke a right");
941             info.put("maygrant", "" + MAYGRANT_PARAMETERS + " Check if user may grant a right");
942             info.put("mayrevoke", "" + MAYREVOKE_PARAMETERS + " Check if user may revoke a right");
943             info.put("may", "" + MAY_PARAMETERS + " Checks a right for another user than yourself");
944
945             if (args == null || args.size() == 0) {
946                 return info;
947             } else {
948                 return info.get(args.get(0));
949             }
950         } else if (function.equals("allows")) {
951             Parameters a = Functions.buildParameters(ALLOWS_PARAMETERS, args); // 'ALLOW' argument would be more logical, but don't when because of the extra argument (practical can use several functions with same arguments list)
952
if (allows(node, getNode(a.getString(PARAMETER_GROUPORUSER)), Operation.getOperation(a.getString(PARAMETER_OPERATION)))) {
953                 return Boolean.TRUE;
954             } else {
955                 return Boolean.FALSE;
956             }
957         } else if (function.equals("parentsallow")) { // 'ALLOW' argument would be more logical, but don't when because of the extra argument (practical can use several functions with same arguments list)
958
Parameters a = Functions.buildParameters(PARENTSALLOW_PARAMETERS, args);
959             if (parentsAllow(node, getGroupOrUserNode(a), Operation.getOperation(a.getString(PARAMETER_OPERATION)))) {
960                 return Boolean.TRUE;
961             } else {
962                 return Boolean.FALSE;
963             }
964         } else if (function.equals("grant")) {
965             Parameters a = Functions.buildParameters(GRANT_PARAMETERS, args);
966             if (grant(node, getGroupOrUserNode(a), Operation.getOperation(a.getString(PARAMETER_OPERATION)), getUserNode((UserContext) a.get("user")))) {
967                 return Boolean.TRUE;
968             } else {
969                 return Boolean.FALSE;
970             }
971         } else if (function.equals("revoke")) {
972             Parameters a = Functions.buildParameters(REVOKE_PARAMETERS, args);
973             if (revoke(node, getGroupOrUserNode(a), Operation.getOperation(a.getString(PARAMETER_OPERATION)), getUserNode((UserContext) a.get("user")))) {
974                 return Boolean.TRUE;
975             } else {
976                 return Boolean.FALSE;
977             }
978         } else if (function.equals("maygrant")) {
979             Parameters a = Functions.buildParameters(MAYGRANT_PARAMETERS, args);
980             if (mayGrant(node, getGroupOrUserNode(a), Operation.getOperation(a.getString(PARAMETER_OPERATION)), getUserNode((UserContext) a.get("user")))) {
981                 return Boolean.TRUE;
982             } else {
983                 return Boolean.FALSE;
984             }
985         } else if (function.equals("mayrevoke")) {
986             Parameters a = Functions.buildParameters(MAYREVOKE_PARAMETERS, args);
987             if (mayRevoke(node, getGroupOrUserNode(a), Operation.getOperation(a.getString(PARAMETER_OPERATION)), getUserNode((UserContext) a.get("user")))) {
988                 return Boolean.TRUE;
989             } else {
990                 return Boolean.FALSE;
991             }
992         } else if (function.equals("may")) {
993             Parameters a = Functions.buildParameters(MAY_PARAMETERS, args);
994             MMObjectNode checkingUser = getUserNode((UserContext) a.get(Parameter.USER));
995             if (checkingUser == null) {
996                 throw new SecurityException JavaDoc("Self was not supplied");
997             }
998             // find the user first, the check if the current user actually has rights on the object
999
MMObjectNode userToCheck = Users.getBuilder().getNode(a.getString("usertocheck"));
1000            if (userToCheck == null) { // the user is null?
1001
// I don't know then,
1002
// yes perhaps?
1003
return Boolean.TRUE;
1004            }
1005
1006            // admin bypasses security system (maydo(mmobjectnode ... does not check for this)
1007
if (Users.getBuilder().getRank(checkingUser).getInt() < Rank.ADMIN_INT) {
1008                if ((! mayDo(checkingUser, getContextNode(userToCheck), Operation.READ, true))) {
1009                    throw new SecurityException JavaDoc("You " + checkingUser + " / " + Users.getBuilder().getRank(checkingUser) + " are not allowed to check user '" + userToCheck + "' of context '" + getContextNode(userToCheck) + "' (you have no read rights on that context)");
1010                }
1011
1012            }
1013            // MMObjectNode contextNode = getContextNode(node);
1014

1015            if (mayDo(userToCheck, node, Operation.getOperation(a.getString(PARAMETER_OPERATION)), true)) {
1016                return Boolean.TRUE;
1017            } else {
1018                return Boolean.FALSE;
1019            }
1020        } else {
1021            return super.executeFunction(node, function, args);
1022        }
1023    }
1024
1025
1026
1027
1028    public String JavaDoc toString(MMObjectNode n) {
1029        return n.getStringValue("name");
1030    }
1031
1032    private static class AllowingContexts {
1033        SortedSet contexts;
1034        boolean inverse;
1035        AllowingContexts(SortedSet c, boolean i) {
1036            contexts = c;
1037            inverse = i;
1038        }
1039        public String JavaDoc toString() {
1040            return (inverse ? "NOT IN " : "IN ") + contexts;
1041        }
1042
1043    }
1044
1045
1046
1047}
1048
Popular Tags