View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.jetspeed.om.page.psml;
19  
20  import java.security.AccessController;
21  import java.security.Principal;
22  import java.util.ArrayList;
23  import java.util.Iterator;
24  import java.util.LinkedList;
25  import java.util.List;
26  
27  import javax.security.auth.Subject;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.jetspeed.JetspeedActions;
32  import org.apache.jetspeed.om.common.SecuredResource;
33  import org.apache.jetspeed.om.common.SecurityConstraint;
34  import org.apache.jetspeed.om.common.SecurityConstraints;
35  import org.apache.jetspeed.om.page.BaseElement;
36  import org.apache.jetspeed.om.page.PageSecurity;
37  import org.apache.jetspeed.om.page.SecurityConstraintImpl;
38  import org.apache.jetspeed.page.document.DocumentHandlerFactory;
39  import org.apache.jetspeed.page.document.Node;
40  import org.apache.jetspeed.page.document.NodeSet;
41  import org.apache.jetspeed.page.document.psml.NodeSetImpl;
42  import org.apache.jetspeed.security.FolderPermission;
43  import org.apache.jetspeed.security.GroupPrincipal;
44  import org.apache.jetspeed.security.JSSubject;
45  import org.apache.jetspeed.security.PagePermission;
46  import org.apache.jetspeed.security.PortalResourcePermission;
47  import org.apache.jetspeed.security.RolePrincipal;
48  import org.apache.jetspeed.security.UserPrincipal;
49  
50  
51  /***
52   *
53   * @version $Id: AbstractBaseElement.java 516448 2007-03-09 16:25:47Z ate $
54   */
55  public abstract class AbstractBaseElement implements java.io.Serializable, SecuredResource
56  {
57      private final static Log log = LogFactory.getLog(AbstractBaseElement.class);
58  
59      private String id = null;
60  
61      private String title = null;
62  
63      private String shortTitle = null;
64  
65      private boolean constraintsEnabled;
66  
67      private SecurityConstraints constraints = null;
68  
69      private boolean permissionsEnabled;
70      
71      private DocumentHandlerFactory handlerFactory = null;
72  
73      public String getId()
74      {
75           return this.id;
76      }
77  
78      public void setId(String id)
79      {
80          this.id = id;
81      }
82  
83      /***
84       * <p>
85       * getTitle
86       * </p>
87       *
88       * @see org.apache.jetspeed.om.page.BaseElement#getTitle()
89       * @return
90       */
91      public String getTitle()
92      {
93          return this.title;
94      }
95  
96      /***
97       * <p>
98       * setTitle
99       * </p>
100      *
101      * @see org.apache.jetspeed.om.page.BaseElement#setTitle(java.lang.String)
102      * @param title
103      */
104     public void setTitle(String title)
105     {
106         this.title = title;
107     }
108     /***
109      * <p>
110      * getShortTitle
111      * </p>
112      *
113      * @see org.apache.jetspeed.om.page.BaseElement#getShortTitle()
114      * @return short title
115      */
116     public String getShortTitle()
117     {
118         // default to title if not specified
119         String title = this.shortTitle;
120         if (title == null)
121         {
122             title = this.title;
123         }
124         return title;
125     }
126     /***
127      * <p>
128      * setShortTitle
129      * </p>
130      *
131      * @see org.apache.jetspeed.om.page.BaseElement#setShortTitle(java.lang.String)
132      * @param title
133      */
134     public void setShortTitle(String title)
135     {
136         this.shortTitle = title;
137     }
138 
139     /***
140      * <p>
141      * getConstraintsEnabled
142      * </p>
143      *
144      * @see org.apache.jetspeed.om.common.SecureResource#getConstraintsEnabled()
145      * @return whether security relies on PSML constraints
146      */
147     public boolean getConstraintsEnabled()
148     {
149         return constraintsEnabled;
150     }
151 
152     /***
153      * <p>
154      * setConstraintsEnabled
155      * </p>
156      *
157      * @param enabled indicator
158      */
159     public void setConstraintsEnabled(boolean enabled)
160     {
161         constraintsEnabled = enabled;
162     }
163 
164     /***
165      * <p>
166      * getSecurityConstraints
167      * </p>
168      *
169      * @see org.apache.jetspeed.om.common.SecureResource#getSecurityConstraints()
170      * @return the PSML security constraints
171      */
172     public SecurityConstraints getSecurityConstraints()
173     {
174         return constraints;
175     }
176     
177     /***
178      * <p>
179      * newSecurityConstraints
180      * </p>
181      *
182      * @see org.apache.jetspeed.om.common.SecureResource#newSecurityConstraints()
183      * @return  a new security constraints object
184      */
185     public SecurityConstraints newSecurityConstraints()
186     {
187         return new SecurityConstraintsImpl();
188     }
189 
190     /***
191      * <p>
192      * newSecurityConstraint
193      * </p>
194      *
195      * @see org.apache.jetspeed.om.common.SecureResource#newSecurityConstraint()
196      * @return security constraint
197      */
198     public SecurityConstraint newSecurityConstraint()
199     {
200         return new SecurityConstraintImpl();
201     }
202 
203     /***
204      * <p>
205      * setSecurityConstraints
206      * </p>
207      *
208      * @see org.apache.jetspeed.om.common.SecureResource#setSecurityConstraints(org.apache.jetspeed.om.common.SecurityConstraints)
209      * @param constraints
210      */
211     public void setSecurityConstraints(SecurityConstraints constraints)
212     {
213         this.constraints = constraints;
214     }
215 
216     /***
217      * <p>
218      * checkConstraints
219      * </p>
220      *
221      * @see org.apache.jetspeed.om.common.SecureResource#checkConstraints(java.lang.String)
222      * @param actions
223      * @throws SecurityException
224      */
225     public void checkConstraints(String actions) throws SecurityException
226     {
227         // skip checks if not enabled
228         if (!getConstraintsEnabled())
229         {
230             return;
231         }
232 
233         // validate specified actions
234         if (actions == null)
235         {
236             throw new SecurityException("AbstractBaseElement.checkConstraints(): No actions specified.");
237         }
238 
239         // get action names lists; separate view and other
240         // actions to mimic file system permissions logic
241         List viewActionList = SecurityConstraintImpl.parseCSVList(actions);
242         List otherActionsList = null;
243         if (viewActionList.size() == 1)
244         {
245             if (!viewActionList.contains(JetspeedActions.VIEW))
246             {
247                 otherActionsList = viewActionList;
248                 viewActionList = null;
249             }
250         }
251         else
252         {
253             otherActionsList = viewActionList;
254             viewActionList = null;
255             if (otherActionsList.remove(JetspeedActions.VIEW))
256             {
257                 viewActionList = new ArrayList(1);
258                 viewActionList.add(JetspeedActions.VIEW);
259             }
260         }
261 
262         // get current request context subject
263         Subject subject = JSSubject.getSubject(AccessController.getContext());
264         if (subject == null)
265         {
266             throw new SecurityException("AbstractBaseElement.checkConstraints(): Missing JSSubject");
267         }
268 
269         // get user/group/role principal names
270         List userPrincipals = null;
271         List rolePrincipals = null;
272         List groupPrincipals = null;
273         Iterator principals = subject.getPrincipals().iterator();
274         while (principals.hasNext())
275         {
276             Principal principal = (Principal) principals.next();
277             if (principal instanceof UserPrincipal)
278             {
279                 if (userPrincipals == null)
280                 {
281                     userPrincipals = new LinkedList();
282                 }
283                 userPrincipals.add(principal.getName());
284             }
285             else if (principal instanceof RolePrincipal)
286             {
287                 if (rolePrincipals == null)
288                 {
289                     rolePrincipals = new LinkedList();
290                 }
291                 rolePrincipals.add(principal.getName());
292             }
293             else if (principal instanceof GroupPrincipal)
294             {
295                 if (groupPrincipals == null)
296                 {
297                     groupPrincipals = new LinkedList();
298                 }
299                 groupPrincipals.add(principal.getName());
300             }
301         }
302 
303         // check constraints using parsed action and access lists
304         if (viewActionList != null)
305         {
306             checkConstraints(viewActionList, userPrincipals, rolePrincipals, groupPrincipals, false, grantViewActionAccess());
307         }
308         if (otherActionsList != null)
309         {
310             checkConstraints(otherActionsList, userPrincipals, rolePrincipals, groupPrincipals, true, false);
311         }
312     }
313 
314     /***
315      * <p>
316      * checkConstraints
317      * </p>
318      *
319      * @param actions
320      * @param userPrincipals
321      * @param rolePrincipals
322      * @param groupPrincipals
323      * @param checkNodeOnly
324      * @param checkParentsOnly
325      * @throws SecurityException
326      */
327     public void checkConstraints(List actions, List userPrincipals, List rolePrincipals, List groupPrincipals, boolean checkNodeOnly, boolean checkParentsOnly) throws SecurityException
328     {
329         // check node constraints if available
330         if ((constraints != null) && !constraints.isEmpty())
331         {
332             ((SecurityConstraintsImpl)constraints).checkConstraints(actions, userPrincipals, rolePrincipals, groupPrincipals, getEffectivePageSecurity());
333         }
334     }
335 
336     /***
337      * <p>
338      * getPermissionsEnabled
339      * </p>
340      *
341      * @see org.apache.jetspeed.om.common.SecureResource#getPermissionsEnabled()
342      * @return
343      */
344     public boolean getPermissionsEnabled()
345     {
346         return permissionsEnabled;
347     }
348 
349     /***
350      * <p>
351      * setPermissionsEnabled
352      * </p>
353      *
354      * @param enabled indicator
355      */
356     public void setPermissionsEnabled(boolean enabled)
357     {
358         permissionsEnabled = enabled;
359     }
360 
361     /***
362      * <p>
363      * checkPermissions
364      * </p>
365      *
366      * @see org.apache.jetspeed.om.common.SecuredResource#checkPermissions(int)
367      * @param mask Mask of actions requested
368      * @throws SecurityException
369      */
370     public void checkPermissions(int mask) throws SecurityException
371     {
372         // skip checks if not enabled
373         if (!getPermissionsEnabled())
374         {
375             return;
376         }
377 
378         // separate view and other actions to mimic file system permissions logic
379         boolean viewAction = (mask & JetspeedActions.MASK_VIEW) == JetspeedActions.MASK_VIEW;
380         int otherMask = mask & ~JetspeedActions.MASK_VIEW;
381 
382         // check permissions using parsed actions
383         if (viewAction)
384         {
385             checkPermissions(JetspeedActions.MASK_VIEW, false, grantViewActionAccess());
386         }
387         if (otherMask != 0)
388         {
389             checkPermissions(otherMask, true, false);
390         }
391     }
392     /***
393      * <p>
394      * checkPermissions
395      * </p>
396      *
397      * @param mask of actions
398      * @param checkNodeOnly
399      * @param checkParentsOnly
400      * @throws SecurityException
401      */
402     public void checkPermissions(int mask, boolean checkNodeOnly, boolean checkParentsOnly) throws SecurityException
403     {
404         // check page and folder permissions
405         String physicalPermissionPath = getPhysicalPermissionPath();
406         if (physicalPermissionPath != null)
407         {
408             // check permissions using physical path
409             try
410             {
411                 checkPermissions(physicalPermissionPath, mask, checkNodeOnly, checkParentsOnly);
412             }
413             catch (SecurityException physicalSE)
414             {
415                 // fallback check using logical path if available and different
416                 String logicalPermissionPath = getLogicalPermissionPath();
417                 if ((logicalPermissionPath != null) && !logicalPermissionPath.equals(physicalPermissionPath))
418                 {
419                     checkPermissions(logicalPermissionPath, mask, checkNodeOnly, checkParentsOnly);
420                 }
421                 else
422                 {
423                     throw physicalSE;
424                 }
425             }
426         }
427     }
428     /***
429      * <p>
430      * checkPermissions
431      * </p>
432      *
433      * @param path
434      * @param mask Mask of actions requested
435      * @param checkNodeOnly
436      * @param checkParentsOnly
437      * @throws SecurityException
438      */
439     public void checkPermissions(String path, int mask, boolean checkNodeOnly, boolean checkParentsOnly) throws SecurityException
440     {
441         // check actions permissions
442         try
443         {
444             // check for granted page permissions
445             PagePermission permission = new PagePermission(path, mask);
446             AccessController.checkPermission(permission);
447         }
448         catch (SecurityException se)
449         {
450             // fallback check for granted folder permissions
451             FolderPermission permission = new FolderPermission(path, mask);
452             AccessController.checkPermission(permission);
453         }
454     }
455 
456     /***
457      * <p>
458      * getLogicalPermissionPath
459      * </p>
460      *
461      * @return path used for permissions checks
462      */
463     public String getLogicalPermissionPath()
464     {
465         return getPhysicalPermissionPath();
466     }
467 
468     /***
469      * <p>
470      * getPhysicalPermissionPath
471      * </p>
472      *
473      * @return path used for permissions checks
474      */
475     public String getPhysicalPermissionPath()
476     {
477         // no permissions path available by default
478         log.warn("getPhysicalPermissionPath(): no permission path available for " + this + " element.");
479         return null;
480     }
481 
482     /***
483      * <p>
484      * checkAccess
485      * </p>
486      *
487      * @see org.apache.jetspeed.om.common.SecureResource#checkAccess(java.lang.String)
488      * @param actions
489      * @throws SecurityException
490      */
491     public void checkAccess(String actions) throws SecurityException
492     {
493         // check access permissions and constraints as enabled
494         if (getPermissionsEnabled())
495         {
496             int mask = PortalResourcePermission.parseActions(actions);
497             checkPermissions(mask);
498         }
499         if (getConstraintsEnabled())
500         {
501             checkConstraints(actions);
502         }
503     }
504 
505     /***
506      * <p>
507      * grantViewActionAccess
508      * </p>
509      *
510      * @return granted access for view action
511      */
512     public boolean grantViewActionAccess()
513     {
514         // by default, access must be checked
515         return false;
516     }
517 
518     /***
519      * getEffectivePageSecurity
520      *
521      * @return effective page security object
522      */
523     public PageSecurity getEffectivePageSecurity()
524     {
525         // no page security available by default
526         return null;
527     }
528 
529     /***
530      * <p>
531      * getHandlerFactory
532      * </p>
533      *
534      * @return element handler factory
535      */
536     public DocumentHandlerFactory getHandlerFactory()
537     {
538         return handlerFactory;
539     }
540 
541     /***
542      * <p>
543      * setHandlerFactory
544      * </p>
545      *
546      * @param factory element handler factory
547      */
548     public void setHandlerFactory(DocumentHandlerFactory factory)
549     {
550         this.handlerFactory = factory;
551     }
552 
553     /***
554      * <p>
555      * equals
556      * </p>
557      *
558      * @see java.lang.Object#equals(java.lang.Object)
559      * @param obj
560      * @return whether the supplied object equals this one
561      */
562     public boolean equals( Object obj )
563     {
564         if(obj instanceof BaseElement)
565         {
566             AbstractBaseElement element = (AbstractBaseElement) obj;
567             return id != null && element.getId() != null && id.equals(element.getId());            
568         }
569         else
570         {
571             return false;
572         }
573     }
574     
575     /***
576      * <p>
577      * hashCode
578      * </p>
579      *
580      * @see java.lang.Object#hashCode()
581      * @return the hashcode for this object
582      */
583     public int hashCode()
584     {
585         return ((null != id) ? id.hashCode() : -1);
586     }
587     
588     /***
589      * <p>
590      * toString
591      * </p>
592      *
593      * @see java.lang.Object#toString()
594      * @return the id as a string representation of this object
595      */
596     public String toString()
597     {      
598         return getId();
599     }
600 
601     /***
602      * <p>
603      * checkAccess returns a set of nodes we can access.  It may be the passed in node set or a partial copy.
604      * </p>
605      *
606      * @param nodes
607      * @param actions
608      * @return a NodeSet containing the nodes allowing access
609      */
610     public static NodeSet checkAccess(NodeSet nodes, String actions)
611     {
612         if ((nodes != null) && !nodes.isEmpty())
613         {
614             // check permissions and constraints, filter nodes as required
615             NodeSetImpl filteredNodes = null;
616             Iterator checkAccessIter = nodes.iterator();
617             while (checkAccessIter.hasNext())
618             {
619                 AbstractBaseElement node = (AbstractBaseElement)checkAccessIter.next();
620                 try
621                 {
622                     // check access
623                     node.checkAccess(actions);
624 
625                     // add to filteredNodes nodes if copying
626                     if (filteredNodes != null)
627                     {
628                         // permitted, add to filteredNodes nodes
629                         filteredNodes.add((Node)node);
630                     }
631                 }
632                 catch (SecurityException se)
633                 {
634                     // create filteredNodes nodes if not already copying
635                     if (filteredNodes == null)
636                     {
637                         // not permitted, copy previously permitted nodes
638                         // to new filteredNodes node set with same comparator
639                         filteredNodes = new NodeSetImpl(null, ((NodeSetImpl) nodes).getComparator());
640                         Iterator copyIter = nodes.iterator();
641                         while (copyIter.hasNext())
642                         {
643                             Node copyNode = (Node)copyIter.next();
644                             if (copyNode != node)
645                             {
646                                 filteredNodes.add(copyNode);
647                             }
648                             else
649                             {
650                                 break;
651                             }
652                         }
653                     }
654                 }
655             }
656 
657             // return filteredNodes nodes if generated
658             if (filteredNodes != null)
659             {
660                 return filteredNodes;
661             }
662         }
663         return nodes;
664     }
665 
666     /***
667      * unmarshalled - notification that this instance has been
668      *                loaded from the persistent store
669      */
670     public void unmarshalled()
671     {
672         // by default, no action required
673     }
674 
675     /***
676      * marshalling - notification that this instance is to
677      *               be saved to the persistent store
678      */
679     public void marshalling()
680     {
681         // by default, no action required
682     }
683 }