KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > jcorporate > expresso > core > dbobj > RowSecuredDBObject


1 /* ====================================================================
2  * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
3  *
4  * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  *
18  * 3. The end-user documentation included with the redistribution,
19  * if any, must include the following acknowledgment:
20  * "This product includes software developed by Jcorporate Ltd.
21  * (http://www.jcorporate.com/)."
22  * Alternately, this acknowledgment may appear in the software itself,
23  * if and wherever such third-party acknowledgments normally appear.
24  *
25  * 4. "Jcorporate" and product names such as "Expresso" must
26  * not be used to endorse or promote products derived from this
27  * software without prior written permission. For written permission,
28  * please contact info@jcorporate.com.
29  *
30  * 5. Products derived from this software may not be called "Expresso",
31  * or other Jcorporate product names; nor may "Expresso" or other
32  * Jcorporate product names appear in their name, without prior
33  * written permission of Jcorporate Ltd.
34  *
35  * 6. No product derived from this software may compete in the same
36  * market space, i.e. framework, without prior written permission
37  * of Jcorporate Ltd. For written permission, please contact
38  * partners@jcorporate.com.
39  *
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
41  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
42  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43  * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
44  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
45  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
46  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
47  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
48  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
49  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
50  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This software consists of voluntary contributions made by many
55  * individuals on behalf of the Jcorporate Ltd. Contributions back
56  * to the project(s) are encouraged when you make modifications.
57  * Please send them to support@jcorporate.com. For more information
58  * on Jcorporate Ltd. and its products, please see
59  * <http://www.jcorporate.com/>.
60  *
61  * Portions of this software are based upon other open source
62  * products and are subject to their respective licenses.
63  */

64 package com.jcorporate.expresso.core.dbobj;
65
66 import com.jcorporate.expresso.core.db.DBConnection;
67 import com.jcorporate.expresso.core.db.DBException;
68 import com.jcorporate.expresso.core.registry.MutableRequestRegistry;
69 import com.jcorporate.expresso.core.registry.RequestRegistry;
70 import com.jcorporate.expresso.core.security.User;
71 import com.jcorporate.expresso.services.dbobj.RowGroupPerms;
72 import com.jcorporate.expresso.services.dbobj.RowPermissions;
73 import com.jcorporate.expresso.services.dbobj.UserGroup;
74
75 import java.util.ArrayList JavaDoc;
76 import java.util.Collection JavaDoc;
77 import java.util.Iterator JavaDoc;
78 import java.util.List JavaDoc;
79
80
81 /**
82  * subclass this for support of row-level Authorization. <br>
83  * <br>
84  * Typically, you construct RowSecuredDBObject passing in userId or the
85  * controller request to set the requesting identity. Or, after construction, call
86  * setRequestingUid() so that user of this DBObject is known. Otherwise,
87  * security checks will always return false (and access methods will throw
88  * SecurityException). <br>
89  * <br>
90  * LIMITATION: the primary key for a given row is persisted, along with the
91  * name of the table, to identify the permissions for that row. In other
92  * words, the primary key for permissions is the row's table name plus the
93  * row's primary key: permissionPK = targetTable + targetPrimKey.
94  * Each database vendor (MySQL,
95  * Oracle, etc.) has its own limit for the longest field that can be indexed
96  * as a primary key. If a target row has a very long primary key, the database may
97  * not be able to accommodate the primary key for its permissions. In that
98  * case, a runtime exception is thrown when trying to persist the permissions.
99  * If (length(permissionPK) > MAXIMUM for database) { throw runtime exception}
100  * In most cases, the primary key is an integer or some other short field, so
101  * this limitation is not a problem.
102  * <p/>
103  * LIMITATION: all permissions are stored in a two tables: RowPermissions & RowGroupPerms.
104  * If your application
105  * has many tables (dbobjects) which all subclass RowSecuredDBObject,
106  * and also has many rows in these tables, you could run into scaling issues
107  * first with permissions. Use RowSecuredDBObject subclasses for only the
108  * important objects. Roll your own security scheme if you expect a very large
109  * database... and tell us how you did it. :-)
110  *
111  * @author larry hamel, CodeGuild, Inc.
112  * @see com.jcorporate.expresso.services.dbobj.RowPermissions
113  * @see com.jcorporate.expresso.services.dbobj.RowGroupPerms
114  */

115 public class RowSecuredDBObject extends SecuredDBObject {
116
117     /**
118      * Flag for getPermission() if it must create new.
119      */

120     private static final String JavaDoc CREATED_NEW = "CREATED_NEW";
121
122
123     /**
124      * Constructor without parameters. This constructor will attempt to use servlet filter
125      * to set data context and user ID from expresso v.5.6. However, if you
126      * have not set up this filter in web.xml, be sure to set dbname and user id
127      * after constructing.
128      *
129      * @throws DBException upon database communication error
130      * @todo after expresso v.5.7, change impl to throw exception if servlet
131      * filter is not found. Introducing this in v5.6, so legacy
132      * does not want exceptions
133      */

134     public RowSecuredDBObject() throws DBException {
135         try {
136             User user = RequestRegistry.getUser();
137             try {
138                 user.getUid();
139                 this.setRequestingUid(user.getUid());
140             } catch (Throwable JavaDoc e) { // npe if user is null
141
getLogger().warn("Cannot get user from request registry for class: '"
142                         + getClass().getName()
143                         + "'. No user has been associated with this thread yet. (see "
144                         + MutableRequestRegistry.class.getName() + ")");
145
146                 // by default, the superclass sets requesting user ID
147
// to 'system' == superuser,
148
// so reset the requesting user here
149
super.setRequestingUid(User.getIdFromLogin(User.UNKNOWN_USER,
150                         getDataContext()));
151             }
152
153         } catch (Exception JavaDoc ex) {
154
155             // by default, the superclass sets requesting user ID
156
// to 'system' == superuser,
157
// so reset the requesting user here
158
super.setRequestingUid(User.getIdFromLogin(User.UNKNOWN_USER,
159                     getDataContext()));
160
161             // no error for now if filter not found.
162
// @todo after expresso v.5.7, change impl to throw exception
163
// if servlet filter is not found. Introducing this in v5.6,
164
// so legacy does not want exceptions
165
getLogger().warn("Problem automatically setting user ID: "
166                     + getClass().getName() + ", err: " + ex.getClass().getName()
167                     + ", msg: " + ex.getMessage());
168         }
169
170         // warn if key of this row may be too long
171
checkKeyLength();
172     }
173
174     /**
175      * Constructor: Specify a DB connection AND user id.
176      *
177      * @param theConnection A DBConnection that this object should use to
178      * connect to the database
179      * @param theUser User name attempting to use this object. If this is
180      * "SYSTEM", then full permissions are granted. Note that you
181      * cannot log in as "SYSTEM", it can only be used from within a
182      * method.
183      * @throws DBException If the object cannot be created
184      */

185     public RowSecuredDBObject(DBConnection theConnection, int theUser)
186             throws DBException {
187         super(theConnection, theUser);
188
189         // check that key is not too long
190
checkKeyLength();
191     }
192
193     /**
194      * Creates a new RowSecuredDBObject object.
195      * This constructor will attempt to use servlet filter
196      * to set data context--from expresso v.5.6. However, if you
197      * have not set up this filter in web.xml, be sure to set dbname and user id
198      * after constructing.
199      *
200      * @param theUser requesting user
201      * @throws DBException upon database communication error
202      * @todo after expresso v.5.7, change impl to throw exception if servlet filter is not found. Introducing this in v5.6, so legacy does not want exceptions *
203      */

204     public RowSecuredDBObject(int theUser) throws DBException {
205         super(theUser);
206
207         try {
208             setDataContext(RequestRegistry.getDataContext());
209         } catch (IllegalStateException JavaDoc e) {
210             // no error for now if filter not found.
211
// @todo after expresso v.5.7, change impl to throw exception if servlet filter is not found. Introducing this in v5.6, so legacy does not want exceptions
212
}
213
214         // check that key is not too long
215
checkKeyLength();
216     }
217
218     /**
219      * Creates a new RowSecuredDBObject object.
220      *
221      * @param request context for using this object
222      * @throws DBException upon database communication error
223      */

224     public RowSecuredDBObject(RequestContext request) throws DBException {
225         super(request);
226
227         // check that key is not too long
228
checkKeyLength();
229     }
230
231     /**
232      * @return list of a group names (strings) which have permission to change
233      * permission
234      * @throws DBException upon database communication error
235      */

236     public List JavaDoc getAdministrateGroups() throws DBException {
237         List JavaDoc allGroups = this.getGroups();
238
239         if (allGroups.size() == 0) {
240             return allGroups;
241         }
242
243         List JavaDoc permGroups = new ArrayList JavaDoc();
244
245         for (Iterator JavaDoc iterator = allGroups.iterator(); iterator.hasNext();) {
246             RowGroupPerms perms = (RowGroupPerms) iterator.next();
247
248             if (perms.canGroupAdministrate()) {
249                 permGroups.add(perms.group());
250             }
251         }
252
253         return permGroups;
254     }
255
256     /**
257      * find any existing permission groups for this object. Typically,
258      * subclasses will call getReadGroups and getWriteGroups instead of this
259      * method
260      *
261      * @return list of RowGroupPerms
262      * @throws DBException upon database communication error
263      */

264     public List JavaDoc getGroups() throws DBException {
265         String JavaDoc primeKey = getKey();
266
267         RowGroupPerms rowGroupPerms = new RowGroupPerms(getJDBCMetaData()
268                 .getTargetTable(),
269                 primeKey);
270         rowGroupPerms.setDataContext(getDataContext());
271
272         if (getLocalConnection() != null) {
273             rowGroupPerms.setConnection(getLocalConnection());
274         }
275
276         return rowGroupPerms.searchAndRetrieveList();
277     }
278
279     /**
280      * set the group and permissions for this object; owner id is taken from
281      * getRequestingUid() before permissions can be set, caller's permission
282      * to change permissions is tested
283      *
284      * @param group name of group
285      * @param perm value of permissions
286      * @throws DBException upon database communication error
287      */

288     public void setPermissions(String JavaDoc group, int perm)
289             throws DBException {
290         if (!canRequesterAdministrate()) {
291             throw new SecurityException JavaDoc("cannot set permissions by user_id: " +
292                     getRequestingUid() + " for db object in table: " +
293                     getJDBCMetaData().getTargetTable() + " with primary key: " +
294                     getKey());
295         }
296
297         setPermissions(getRequestingUid(), group, perm);
298     }
299
300     /**
301      * set the permissions for this object; group bits are ignored; only owner
302      * & "other" permissions apply with this method owner id is taken from
303      * getRequestingUid()
304      *
305      * @param perm permissions to set
306      * @throws DBException upon database communication error
307      */

308     public void setPermissions(int perm) throws DBException {
309         setPermissions(getRequestingUid(), null, perm);
310     }
311
312     /**
313      * finds row permissions for the target row of this DBObject. if no row
314      * permissions are already persisted, the returned permissions object will
315      * be constructed and keyed to generating object, but all permissions will
316      * be false
317      *
318      * @return RowPermissions of the current perms
319      * @throws DBException if keys are not set on this object
320      */

321     public RowPermissions getPermissions() throws DBException {
322         if (!this.haveAllKeys()) {
323             throw new DBException("cannot get/set permissions on item before item's primary key(s) are available.");
324         }
325
326         String JavaDoc primeKey = getKey();
327         RowPermissions rowPermissions = new
328                 RowPermissions(getJDBCMetaData().getTargetTable(), primeKey);
329         rowPermissions.setDataContext(getDataContext());
330         if (getLocalConnection() != null) {
331             rowPermissions.setConnection(getLocalConnection());
332         }
333
334         boolean found = rowPermissions.find();
335
336         // set owner if this is a new set of permissions
337
if (!found) {
338             rowPermissions.setAttribute(CREATED_NEW, "1");
339             rowPermissions.owner(getRequestingUid());
340         }
341
342         return rowPermissions;
343     }
344
345     /**
346      * @return list of group names (strings) which have read permission
347      * @throws DBException upon database communication error
348      */

349     public List JavaDoc getReadGroups() throws DBException {
350         List JavaDoc allGroups = this.getGroups();
351
352         if (allGroups.size() == 0) {
353             return allGroups;
354         }
355
356         List JavaDoc readGroups = new ArrayList JavaDoc();
357
358         for (Iterator JavaDoc iterator = allGroups.iterator(); iterator.hasNext();) {
359             RowGroupPerms perms = (RowGroupPerms) iterator.next();
360
361             if (perms.canGroupRead()) {
362                 readGroups.add(perms.group());
363             }
364         }
365
366         return readGroups;
367     }
368
369     /**
370      * determine if this function is allowed for this requesting user
371      *
372      * @param requestedFunction code for function -- Add, Update, Delete, Search (read)
373      * @return true if this function is allowed for this requesting user
374      * @throws SecurityException (unchecked) if not allowed
375      * @throws DBException for other data-related errors.
376      */

377     public boolean isRowAllowed(String JavaDoc requestedFunction)
378             throws DBException {
379         boolean result = false;
380
381         if (SEARCH.equals(requestedFunction)) {
382             result = canRequesterRead();
383         } else if (UPDATE.equals(requestedFunction) ||
384                 DELETE.equals(requestedFunction)) {
385             result = canRequesterWrite();
386         } else if (ADD.equals(requestedFunction)) {
387             // we do not test "add", since the row isn't written yet!
388
// instead, superclass will test as part of update call
389
result = true;
390         }
391
392         if (!result) {
393             throw new SecurityException JavaDoc("User '" +
394                     User.getLoginFromId(getRequestingUid(), getDataContext()) +
395                     "' does not have permission to perform function '" +
396                     requestedFunction + "' on database object in table '" +
397                     getJDBCMetaData().getTargetTable() + "', row: '" + getKey() +
398                     "' in db/context '" + getDataContext() +
399                     "'. Please contact your system administrator if you feel this is incorrect.");
400         }
401
402         return result;
403     }
404
405     /**
406      * iterate through collection, testing each row's privileges remove any row
407      * which does not have privileges; (do not throw security exception, just
408      * remove row)
409      *
410      * @param requestedFunction code for function -- Add, Update, Delete, Search (read)
411      * @param items is a collection of RowSecuredDBObjects
412      * @return true if AT LEAST ONE item in collectino is allowed; also manipulates input list, removing unallowed items
413      * @throws DBException upon database communication error
414      */

415     public boolean isRowAllowed(String JavaDoc requestedFunction, Collection JavaDoc items)
416             throws DBException {
417         if (items.size() == 0) {
418             return false;
419         }
420
421         boolean result = false;
422         List JavaDoc goodItems = new ArrayList JavaDoc();
423
424         for (Iterator JavaDoc iterator = items.iterator(); iterator.hasNext();) {
425             RowSecuredDBObject rowSecureDBObject = (RowSecuredDBObject) iterator.next();
426
427             // key point: this is not set by superclass
428
rowSecureDBObject.setRequestingUid(getRequestingUid());
429
430             try {
431                 if (rowSecureDBObject.isRowAllowed(requestedFunction)) {
432                     result = true; // at least one row is allowed
433
goodItems.add(rowSecureDBObject);
434                 }
435             } catch (SecurityException JavaDoc e) {
436                 if (getLogger().isDebugEnabled()) {
437                     getLogger().debug(
438                             "Security exception checking for isRowAllowed: denying a row with key: " + getKey() + " , removing it from list of approved.");
439                 }
440                 // do nothing; this Row will be removed
441
}
442         }
443
444         if (result) {
445             // keep only good items
446
items.retainAll(goodItems);
447
448             /**
449              * @todo does this retain ordering?
450              */

451         } else {
452             items.clear();
453         }
454
455         return result;
456     }
457
458     /**
459      * @return list of group names (strings) which have write permission
460      * @throws DBException upon database communication error
461      */

462     public List JavaDoc getWriteGroups() throws DBException {
463         List JavaDoc allGroups = this.getGroups();
464
465         if (allGroups.size() == 0) {
466             return allGroups;
467         }
468
469         List JavaDoc writeGroups = new ArrayList JavaDoc();
470
471         for (Iterator JavaDoc iterator = allGroups.iterator(); iterator.hasNext();) {
472             RowGroupPerms perms = (RowGroupPerms) iterator.next();
473
474             if (perms.canGroupWrite()) {
475                 writeGroups.add(perms.group());
476             }
477         }
478
479         return writeGroups;
480     }
481
482     /**
483      * we override not to check permissions (which is done at the table level
484      * by superclass) but rather to add default permissions
485      *
486      * @throws DBException upon database communication error
487      * @see #add(String group, int permissions ) for a way to add() with more
488      * specific permissions
489      */

490     public void add() throws DBException {
491         super.add();
492         setDefaultPermissions();
493     }
494     /* add() */
495
496     /*
497     * add row with given permissions, with group permissions applying
498     to provided group.
499     * @param group the name of the group that has group privileges
500     * @param permissions for owner, group & others
501     * @see com.jcorporate.expresso.services.dbobj.RowPermissions
502     * @throws DBException upon database communication error
503     */

504     public synchronized void add(String JavaDoc group, int permissions)
505             throws DBException {
506         super.add();
507         setPermissions(getRequestingUid(), group, permissions);
508     }
509     /* add() */
510
511     /**
512      * add permissions for a group; will only ADD permissions, not replace will
513      * add row or update existing row (logical OR of bits) as necessary
514      *
515      * @param group to be added
516      * @param perm to be added
517      * @throws DBException upon database communication error
518      */

519     public void addGroupPerm(String JavaDoc group, int perm) throws DBException {
520         if (group == null) {
521             throw new DBException("null group name");
522         }
523
524         // test if group is kosher
525
UserGroup grp = new UserGroup();
526         grp.setDataContext(getDataContext());
527         grp.setField("GroupName", group);
528
529         if (!grp.find()) {
530             throw new DBException("cannot find group: " + group);
531         }
532
533         RowGroupPerms rowGroupPerms = new RowGroupPerms(getJDBCMetaData()
534                 .getTargetTable(),
535                 getKey());
536         rowGroupPerms.setDataContext(getDataContext());
537         if (getLocalConnection() != null) {
538             rowGroupPerms.setConnection(getLocalConnection());
539         }
540         rowGroupPerms.group(group);
541
542         if (rowGroupPerms.find()) {
543             // careful just to ADD permissions, which means bitwise OR
544
int addperm = perm | rowGroupPerms.permissions();
545
546             if (addperm != rowGroupPerms.permissions()) {
547                 rowGroupPerms.permissions(addperm);
548                 rowGroupPerms.update();
549             }
550         } else {
551             rowGroupPerms.permissions(perm);
552             rowGroupPerms.add();
553         }
554     }
555
556     /**
557      * @return true if requesting id has permission to administrate (change
558      * permissions)
559      * @throws DBException upon database communication error
560      */

561     public boolean canRequesterAdministrate() throws DBException {
562         boolean result = false;
563
564         int userId = this.getRequestingUid();
565
566         if (userId == SYSTEM_ACCOUNT) {
567             return true;
568         }
569
570         User user = new User();
571         user.setUid(userId);
572         user.setDataContext(this.getDataContext());
573
574         if (user.isAdmin()) {
575             return true;
576         }
577
578         if (!user.find()) {
579             throw new DBException("cannot find requesting user.");
580         }
581
582         RowPermissions rowPermissions = this.getPermissions();
583
584         // do tests by cpu cheapness: easiest tests first;
585
// we're finished at first "true"
586
// public readability?
587
result = rowPermissions.canOthersAdministrate() ||
588                 ((userId == rowPermissions.owner()) &&
589                 rowPermissions.canOwnerAdministrate());
590
591         if (!result) {
592             // check groups
593
result = doUsersGroupsIntersect(user, getAdministrateGroups());
594         }
595
596         return result;
597     }
598
599     /**
600      * determine if getRequestingUid has rights to read this row
601      *
602      * @return true if getRequestingUid has rights to read this row
603      * @throws DBException upon database communication error
604      */

605     public boolean canRequesterRead() throws DBException {
606         boolean result = false;
607
608         int userId = this.getRequestingUid();
609
610         if (userId == SYSTEM_ACCOUNT) {
611             return true;
612         }
613
614         User user = new User();
615         user.setUid(userId);
616         user.setDataContext(this.getDataContext());
617
618         if (user.isAdmin()) {
619             return true;
620         }
621
622         if (!user.find()) {
623             throw new DBException("cannot find requesting user.");
624         }
625
626         RowPermissions rowPermissions = this.getPermissions();
627
628         // do tests by cpu cheapness: easiest tests first;
629
// we're finished at first "true"
630
// public readability?
631
result = rowPermissions.canOthersRead() ||
632                 ((userId == rowPermissions.owner()) &&
633                 rowPermissions.canOwnerRead());
634
635         if (!result) {
636             // check groups
637
/**
638              * @todo rewrite as a multidbobject join
639              */

640             result = doUsersGroupsIntersect(user, getReadGroups());
641         }
642
643         return result;
644     }
645
646     private boolean doUsersGroupsIntersect(User user, List JavaDoc groups) throws DBException {
647         boolean result = false;
648         for (Iterator JavaDoc iterator = user.getGroups().iterator(); iterator.hasNext();) {
649             String JavaDoc userGrpname = (String JavaDoc) iterator.next();
650             for (Iterator JavaDoc iterator1 = groups.iterator(); iterator1.hasNext();) {
651                 String JavaDoc readGrpName = (String JavaDoc) iterator1.next();
652                 result = readGrpName.equals(userGrpname);
653                 if ( result ) return true;
654             }
655         }
656         return false;
657     }
658
659     /**
660      * @return true if requesting id has permission to write
661      * @throws DBException upon database communication error
662      */

663     public boolean canRequesterWrite() throws DBException {
664         boolean result = false;
665
666         int userId = this.getRequestingUid();
667
668         if (userId == SYSTEM_ACCOUNT) {
669             return true;
670         }
671
672         User user = new User();
673         user.setUid(userId);
674         user.setDataContext(this.getDataContext());
675
676         if (user.isAdmin()) {
677             return true;
678         }
679
680         if (!user.find()) {
681             throw new DBException("cannot find requesting user.");
682         }
683
684         RowPermissions rowPermissions = this.getPermissions();
685
686         // do tests by cpu cheapness: easiest tests first;
687
// we're finished at first "true"
688
// public readability?
689
result = rowPermissions.canOthersWrite() ||
690                 ((userId == rowPermissions.owner()) &&
691                 rowPermissions.canOwnerWrite());
692
693         if (!result) {
694             // check groups
695
result = doUsersGroupsIntersect(user, getWriteGroups());
696         }
697
698         return result;
699     }
700
701     /**
702      * @return the default group for this requesting user; null if user has no
703      * groups
704      * @throws DBException upon database communication error
705      */

706     public String JavaDoc defaultGroup() throws DBException {
707         User user = new User();
708         user.setUid(getRequestingUid());
709         user.setDataContext(getDataContext());
710
711         if (!user.find()) {
712             return null;
713         } else {
714             return user.getPrimaryGroup();
715         }
716     }
717
718     /**
719      * override this to change default
720      *
721      * @return the default permissions
722      * @todo move this into ConfigManager
723      */

724     public int defaultPermissions() {
725         return RowPermissions.DEFAULT_PERMISSIONS;
726     }
727
728     /**
729      * delete row. always delete permission records too
730      *
731      * @param deleteDetails set to true if related details should be deleted also
732      * @throws DBException upon database communication error
733      */

734     public synchronized void delete(boolean deleteDetails)
735             throws DBException {
736         isRowAllowed(DELETE);
737         super.delete(deleteDetails);
738
739         // delete all permissions associated with this row
740
RowPermissions rowPermissions = getPermissions();
741
742         if (rowPermissions.find()) {
743             rowPermissions.delete();
744         }
745
746         List JavaDoc groups = getGroups();
747
748         for (Iterator JavaDoc iterator = groups.iterator(); iterator.hasNext();) {
749             RowGroupPerms rowGroupPerms = (RowGroupPerms) iterator.next();
750             rowGroupPerms.delete();
751         }
752     }
753     /* delete() */
754
755     /**
756      * check that all objects can be deleted; must retrieve all objects to
757      * check individually
758      *
759      * @throws DBException upon database communication error
760      */

761     public synchronized void deleteAll() throws DBException {
762         List JavaDoc list = searchAndRetrieveList();
763
764         for (Iterator JavaDoc iter = list.iterator(); iter.hasNext();) {
765             RowSecuredDBObject object = (RowSecuredDBObject) iter.next();
766             object.isRowAllowed(DELETE);
767         }
768
769         super.deleteAll();
770     }
771     /* deleteAll() */
772
773     /**
774      * find object on criteria provided in fields
775      *
776      * @return true if found AND allowed
777      * @throws DBException if user does not have rights to read found item
778      */

779     public boolean find() throws DBException {
780         boolean result = super.find();
781
782         // must test AFTER search, since we do not necessarily have row keys yet
783
// to find permissions
784
if (result == true) {
785             isRowAllowed(SEARCH);
786         }
787
788         return result;
789     }
790
791     /**
792      * @return uid of owner of this object
793      * @throws DBException upon database communication error
794      */

795     public int ownerID() throws DBException {
796         // here is the inserted part of override
797
RowPermissions perms = this.getPermissions();
798
799         return perms.getFieldInt(RowPermissions.OWNER_ID);
800     }
801
802     /**
803      * remove a permissions group
804      *
805      * @param group to be removed
806      * @throws DBException upon database communication error
807      */

808     public void removeGroup(String JavaDoc group) throws DBException {
809         if (group == null) {
810             throw new DBException("null group name");
811         }
812
813         RowGroupPerms rowGroupPerms = new RowGroupPerms(getJDBCMetaData()
814                 .getTargetTable(),
815                 getKey());
816         rowGroupPerms.setDataContext(getDataContext());
817         rowGroupPerms.group(group);
818
819         if (getLocalConnection() != null) {
820             rowGroupPerms.setConnection(getLocalConnection());
821         }
822
823         if (rowGroupPerms.find()) {
824             rowGroupPerms.delete();
825         }
826     }
827
828     /**
829      * retrieve object on criteria provided in fields
830      *
831      * @throws DBException upon database communication error
832      */

833     public void retrieve() throws DBException {
834         super.retrieve();
835         this.isRowAllowed(SEARCH); // test second in case we have a new object which will not have any permissions yet, and therefore always be disallowed
836
}
837
838     /**
839      * search on criteria provided in fields, and after search phase iterate
840      * through collection, testing each row's privileges remove any row which
841      * does not have privileges; (do not throw security exception, just remove
842      * row)
843      *
844      * @return list of RowSecuredDBObject's which are both found AND allowed for this requester
845      * @throws DBException upon database communication error
846      */

847     public synchronized ArrayList JavaDoc searchAndRetrieveList()
848             throws DBException {
849         //return super.searchAndRetrieveList();
850
ArrayList JavaDoc result = super.searchAndRetrieveList();
851         this.isRowAllowed(SEARCH, result);
852
853         return result;
854     }
855
856     /**
857      * search on criteria provided in fields, and after search phase iterate
858      * through collection, testing each row's privileges remove any row which
859      * does not have privileges; (do not throw security exception, just remove
860      * row) sort results by sortKeys
861      *
862      * @param sortKeys sort field(s)
863      * @return list of RowSecuredDBObject's which are both found AND allowed for this requester
864      * @throws DBException upon database communication error
865      */

866     public synchronized ArrayList JavaDoc searchAndRetrieveList(String JavaDoc sortKeys)
867             throws DBException {
868         ArrayList JavaDoc result = super.searchAndRetrieveList(sortKeys);
869         this.isRowAllowed(SEARCH, result);
870
871         return result;
872     }
873     /* searchAndRetrieve(String) */
874
875     /**
876      * before allowing update, check permission
877      *
878      * @throws DBException upon database communication error
879      */

880     public void update() throws DBException {
881         isRowAllowed(UPDATE);
882         super.update();
883     }
884     /* update() */
885
886     /**
887      * check delete privilege for all detail records; different than superclass
888      * because we must set uid
889      *
890      * @param obj Object to be checked for deletion of detail records
891      * @throws DBException upon database communication error
892      */

893     protected void checkDeleteDetailPerm(DBObject obj)
894             throws DBException {
895         // here is the inserted part of override
896
if (obj instanceof RowSecuredDBObject) {
897             RowSecuredDBObject row = (RowSecuredDBObject) obj;
898             row.setRequestingUid(getRequestingUid());
899         }
900
901         super.checkDeleteDetailPerm(obj);
902     }
903
904     /**
905      * // warn if key of this row may be too long
906      *
907      * @throws DBException upon error
908      * @todo test this impl and uncomment
909      */

910     protected void checkKeyLength() throws DBException {
911         // int totalLength = 0;
912
// Iterator iter = getDef().getAllKeysIterator();
913
// while (iter.hasNext()) {
914
// DBField field = (DBField) iter.next();
915
// totalLength += field.getLengthInt();
916
// }
917
//
918
// if ( totalLength > RowPermissions.getMaxKeyLen() ) {
919
// getLogger().debug("objects in table: " + getTargetTable()
920
// + " have a potential key length of: " + totalLength
921
// + " which is greater than the maximum that can be stored for "
922
// + RowSecuredDBObject.class.getName()
923
// + " so consider subclassing just SecuredDBObject instead." );
924
// }
925
}
926
927     /**
928      * called by add() does NOT check as to whether caller has permission-admin
929      * rights--assumes that caller is only calling add(), so therefore caller
930      * has rights on the new row.
931      *
932      * @throws DBException upon database communication error
933      */

934     private void setDefaultPermissions() throws DBException {
935         RowPermissions rowPermissions = getPermissions();
936         rowPermissions.owner(getRequestingUid());
937         rowPermissions.permissions(defaultPermissions());
938         rowPermissions.addOrUpdate();
939
940         // default group may be null
941
String JavaDoc group = defaultGroup();
942
943         if ((group != null) && (group.length() > 0)) {
944             addGroupPerm(group, defaultPermissions());
945         }
946     }
947
948     /**
949      * set permissions as indicated and persist, either calling db actions add() or update()
950      *
951      * @param owner new owner
952      * @param group new group for group perms
953      * @param perm permissions to set
954      * @throws DBException upon database communication error
955      */

956     private void setPermissions(int owner, String JavaDoc group, int perm)
957             throws DBException {
958         RowPermissions rowPermissions = getPermissions();
959         rowPermissions.owner(owner);
960         rowPermissions.permissions(perm);
961         if (rowPermissions.getAttribute(CREATED_NEW) != null) {
962             rowPermissions.add();
963             rowPermissions.removeAttribute(CREATED_NEW);
964         } else {
965             rowPermissions.update();
966         }
967
968         if (group != null) {
969             addGroupPerm(group, perm);
970         }
971     }
972 }
973
Popular Tags