View Javadoc

1   /* ===================================================================
2    * Copyright 2002-04 SRI International.
3    * Released under the MOZILLA PUBLIC LICENSE Version 1.1
4    * which can be obtained at http://www.mozilla.org/MPL/MPL-1.1.html
5    * This software is distributed on an "AS IS"
6    * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
7    * See the License for the specific language governing rights and
8    * limitations under the License.
9    * =================================================================== */
10  package com.sri.emo.controller;
11  
12  import com.jcorporate.expresso.core.controller.*;
13  import com.jcorporate.expresso.core.db.DBException;
14  import com.jcorporate.expresso.core.dbobj.RowSecuredDBObject;
15  import com.jcorporate.expresso.core.misc.StringUtil;
16  import com.jcorporate.expresso.core.registry.RequestRegistry;
17  import com.jcorporate.expresso.services.dbobj.RowGroupPerms;
18  import com.jcorporate.expresso.services.dbobj.RowPermissions;
19  import com.jcorporate.expresso.services.dbobj.Setup;
20  import com.jcorporate.expresso.services.dbobj.UserGroup;
21  import com.sri.common.controller.AbstractDBController;
22  import com.sri.common.taglib.InputTag;
23  import com.sri.common.util.PermGroup;
24  import com.sri.emo.dbobj.IViewable;
25  
26  import java.util.*;
27  
28  /***
29   * Handle manipulation of permissions.
30   *
31   * @author larry hamel
32   */
33  public class PermissionController extends AbstractDBController {
34      /***
35  	 * 
36  	 */
37  	private static final long serialVersionUID = 1L;
38  	public static final String PROMPT_EDIT_PERMS = "promptEditPerms";
39      public static final String DO_EDIT_PERMS = "doEditPerms";
40      public static final String PROMPT_EDIT_GROUP = "promptEditGroup";
41      public static final String DO_EDIT_GROUP = "doEditGroup";
42  
43      /***
44       * Name of incoming parameter which has values for key fields of RowSecuredDBObject, delimited by bar (|).
45       */
46      public static final String KEY_FIELD_VALUES = "key";
47      public static final String OBJ_TYPE = "obj";
48      public static final String ASSOC_CHK = "assoc";
49      public static final String READ_CHK = "read";
50      public static final String WRITE_CHK = "write";
51      public static final String ADMIN_CHK = "admin";
52      public static final String VIEW_TRANS = "viewTrans";
53      public static final String DISABLED = "disabled";
54  
55      /***
56       * Setup item in expresso schema for whether others (in unix sense--all other users)
57       * can read always. DEFAULT TRUE.
58       */
59      public static final String OTHERS_READ_ALWAYS = "OthersRead";
60  
61      public PermissionController() {
62          State state = new State(PROMPT_EDIT_PERMS, "Prompt to edit permissions");
63          state.addRequiredParameter(KEY_FIELD_VALUES);
64          state.addRequiredParameter(OBJ_TYPE);
65          addState(state);
66  
67          state = new State(DO_EDIT_PERMS, "submit editing of permissions");
68          state.addRequiredParameter(KEY_FIELD_VALUES);
69          state.addRequiredParameter(OBJ_TYPE);
70          addState(state);
71  
72          state = new State(PROMPT_EDIT_GROUP, "Prompt to edit group");
73          state.addRequiredParameter(UserGroup.GROUP_NAME_FIELD);
74          addState(state);
75  
76          state = new State(DO_EDIT_GROUP, "submit editing of group");
77          state.addRequiredParameter(UserGroup.GROUP_NAME_FIELD);
78          addState(state);
79  
80          setInitialState(PROMPT_EDIT_PERMS);
81      }
82  
83      /***
84       * Returns the title of this controller
85       *
86       * @return java.lang.String
87       */
88      public String getTitle() {
89          return ("Permission Controller");
90      }
91  
92      /***
93       * Prompt for edits.
94       *
95       * @param request  The ControllerRequest object.
96       * @param response The ControllerResponse object.
97       * @throws com.jcorporate.expresso.core.controller.ControllerException
98       *          upon error.
99       */
100     protected void runPromptEditPermsState(final ExpressoRequest request,
101                                            final ExpressoResponse response) throws ControllerException, DBException {
102         try {
103             String allkeys = request.getParameter(KEY_FIELD_VALUES); // guaranteed by framework
104             String objtype = request.getParameter(OBJ_TYPE); // guaranteed by framework
105 
106             RowSecuredDBObject obj = getObj(objtype, allkeys);
107 
108             // can this user administrate?  otherwise, we should be here
109             if (!obj.canRequesterAdministrate()) {
110                 throw new ControllerException(
111                         "User: " + RequestRegistry.getUser().getLoginName() + " does not have administration privileges for object of type: "
112                                 + obj.getClass().getName() + " with key: " + allkeys);
113             }
114 
115             //Of the object is viewable create  a link to it.
116             if (obj instanceof IViewable) {
117                 Transition trans = ((IViewable) obj).getViewTrans();
118                 trans.setName(VIEW_TRANS);
119                 response.add(trans);
120             }
121 
122             // info about all groups already associated
123             Map associatedMap = getAssociatedGroupPermsHashWithAllusers(obj);
124 
125             // include info about other groups
126             List groupsOfUsersMembership = RequestRegistry.getUser().getGroupsList();
127 
128             // listing groups: admin gets ALL groups; average user just gets list of groups in which s/he has membership
129             List groupsForListing;
130             if (request.getUserInfo().isAdmin()) {
131                 List allgrps = UserGroup.getAllGroups();
132                 groupsForListing = new ArrayList(allgrps.size());
133                 for (Iterator iterator = allgrps.iterator(); iterator.hasNext();) {
134                     UserGroup group = (UserGroup) iterator.next();
135                     groupsForListing.add(group.getGroupName());
136                 }
137             } else {
138                 groupsForListing = new ArrayList(groupsOfUsersMembership);
139             }
140 
141             // we don't deal with these pseudo groups
142             groupsForListing.remove(UserGroup.NOT_REG_USERS_GROUP);
143             groupsForListing.remove(UserGroup.UNKNOWN_USERS_GROUP);
144 
145             // alphabetize
146             TreeMap alphaMap = new TreeMap();
147             for (Iterator iterator = groupsForListing.iterator(); iterator.hasNext();) {
148                 String groupname = (String) iterator.next();
149                 // an skip any group the user cannot read (yet why would they be a member?)
150                 PermGroup grp = new PermGroup();
151                 grp.setGroupName(groupname);
152                 if (grp.canRequesterRead()) {
153                     alphaMap.put(groupname, groupname);
154                 }
155             }
156 
157             // double-check that we always include ALL_USERS_GROUP
158             alphaMap.put(UserGroup.ALL_USERS_GROUP, UserGroup.ALL_USERS_GROUP);
159 
160             // add rows to response for each group
161             Block groupBlock = new Block(ROW_BLOCK);
162             response.add(groupBlock);
163             for (Iterator iterator = alphaMap.values().iterator(); iterator.hasNext();) {
164                 String groupname = (String) iterator.next();
165                 RowGroupPerms perms = (RowGroupPerms) associatedMap.get(groupname); // can be null
166                 Block groupRow = null;
167                 if (groupname.equals(UserGroup.ALL_USERS_GROUP)) {
168                     // special handling: the object has a perms for OTHER, not saved as a group perm for grou "Everybody",
169                     // but we display it as a group for 'everybody'; in other words, we translate obj perms into
170                     // a pseudo group perm
171                     if (perms == null) {
172                         perms = new RowGroupPerms();
173                     }
174 
175                     insertPrivForOTHER(obj, perms);
176 
177                     boolean othersReadAlways = othersReadAlways();
178                     if (othersReadAlways) {
179                         // set read as disabled--we always allow reading--user cannot turn off
180                         groupRow = getGroupRow(perms, groupname);
181                         Input read = (Input) groupRow.getInputs().get(0); // first one
182                         read.setAttribute(DISABLED, DISABLED);
183                     }
184                 } else {
185                     groupRow = getGroupRow(perms, groupname);
186                 }
187                 groupBlock.add(groupRow);
188             }
189 
190             //Create the submit button.
191             Transition submit = new Transition(DO_EDIT_PERMS, this);
192             submit.setLabel("Update");
193             submit.addParam(KEY_FIELD_VALUES, allkeys);
194             submit.addParam(OBJ_TYPE, obj.getClass().getName());
195             response.add(submit);
196 
197             // also put obj description and key
198             response.add(new Output(OBJ_TYPE, obj.getMetaData().getDescription()));
199             response.add(new Output(KEY_FIELD_VALUES, allkeys));
200         } catch (Exception e) {
201             throw new ControllerException(e);
202         }
203 
204     }
205 
206     /***
207      * Setup item in expresso schema for whether others (in unix sense--all
208      * other users) can read always. DEFAULT TRUE.
209      */
210     public static boolean othersReadAlways() {
211         boolean othersReadAlways = true; // default if no setting
212         String othersReadSetting = Setup.getValueUnrequired(OTHERS_READ_ALWAYS);
213         if (othersReadSetting != null && !StringUtil.toBoolean(othersReadSetting)) {
214             othersReadAlways = false; // use setting
215         }
216         return othersReadAlways;
217     }
218 
219     /***
220      * including 'fake' Everybody reads
221      *
222      * @param obj The RowSecuredDBObject that is the source of the associated map.
223      * @return sorted map of RowGroupPerms, with group name as key
224      * @throws DBException upon RowSecured access error.
225      */
226     private Map getAssociatedGroupPermsHashWithAllusers(RowSecuredDBObject obj) throws DBException {
227 
228         Map associatedMap = getAssociatedGroupPermsHash(obj);
229         // make sure ALL USERS included, since it is used for 'other' in row permissions
230         if (associatedMap.get(UserGroup.ALL_USERS_GROUP) == null) {
231             RowGroupPerms perms = new RowGroupPerms(obj, UserGroup.ALL_USERS_GROUP);
232             insertPrivForOTHER(obj, perms);
233             associatedMap.put(UserGroup.ALL_USERS_GROUP, perms);
234         }
235         return associatedMap;
236     }
237 
238     /***
239      * not including 'fake' Everybody reads
240      *
241      * @param obj The RowSecuredDBObject that is the source of the associated map.
242      * @return sorted map of RowGroupPerms, with group name as key
243      * @throws DBException upon RowSecured access error.
244      */
245     private Map getAssociatedGroupPermsHash(RowSecuredDBObject obj) throws DBException {
246         List objGroups = obj.getGroups();
247         TreeMap associatedMap = new TreeMap();
248         // alphabetize
249         for (Iterator iterator = objGroups.iterator(); iterator.hasNext();) {
250             RowGroupPerms perms = (RowGroupPerms) iterator.next();
251             PermGroup grp = new PermGroup();
252             grp.setGroupName(perms.group());
253             if (grp.canRequesterRead()) {
254                 associatedMap.put(perms.group(), perms);
255             }
256         }
257 
258         return associatedMap;
259     }
260 
261     /***
262      * Insert privileges for "OTHER" from the object into this group,
263      * ignoring whatever perms were in the group to begin with
264      *
265      * @param obj                 The RowSecuredDBobject to get perms from
266      * @param grouppermsForOthers the group permissions to insert perms into
267      * @throws DBException upon RowSecured access error.
268      */
269     private void insertPrivForOTHER(RowSecuredDBObject obj, RowGroupPerms grouppermsForOthers) throws DBException {
270         int rowperms = obj.getPermissions().permissions();
271         // rowperms for 'other' count for group perms for ALL_USERS
272         boolean isRead = (rowperms & RowPermissions.OTHERS_READ_MASK) > 0 || othersReadAlways();
273         boolean isWrite = (rowperms & RowPermissions.OTHERS_WRITE_MASK) > 0;
274         boolean isAdmin = (rowperms & RowPermissions.OTHERS_PERMISSION_MASK) > 0;
275 
276         int grppermsInt = 0;
277         if (isRead) {
278             grppermsInt |= RowPermissions.GROUP_READ_MASK;
279         }
280         if (isWrite) {
281             grppermsInt |= RowPermissions.GROUP_WRITE_MASK;
282         }
283 
284         if (isAdmin) {
285             grppermsInt |= RowPermissions.GROUP_PERMISSION_MASK;
286         }
287 
288         grouppermsForOthers.permissions(grppermsInt);
289     }
290 
291     private RowSecuredDBObject getObj(String objtype, String allkeys) throws InstantiationException,
292             IllegalAccessException, ClassNotFoundException, ControllerException, DBException {
293         RowSecuredDBObject obj = (RowSecuredDBObject) Class.forName(objtype).newInstance();
294         int numkeys = obj.getMetaData().getAllKeysMap().size();
295         String[] values = allkeys.split("//" + DELIMIT);
296         if (values.length != numkeys) {
297             throw new ControllerException(
298                     "Got values: " + allkeys + " which does not correspond to number of keys expected: " + numkeys);
299         }
300         int i = 0;
301         for (Iterator iterator = obj.getKeyFieldListIterator(); iterator.hasNext();) {
302             String key = (String) iterator.next();
303             obj.setField(key, values[i]);
304             i++;
305         }
306 
307         obj.retrieve();
308         return obj;
309     }
310 
311     /***
312      * Get transition for editing permissions for this group.
313      *
314      * @param perms     pass in permissions for already-associated groups, null for not-associated groups
315      * @param groupname The target group name.
316      * @return the Group Row with checkboxes and edit transitions.
317      * @throws DBException upon database error.
318      */
319     private Block getGroupRow(RowGroupPerms perms, String groupname) throws DBException {
320         PermGroup grp = new PermGroup();
321         grp.setGroupName(groupname);
322         grp.retrieve();
323 
324         Block row = new Block(groupname);
325 
326         // note that these names for checkboxes will be APPENDED with group name in the JSP
327         row.add(getCheckbox(READ_CHK, perms != null && perms.canGroupRead()));
328         row.add(getCheckbox(WRITE_CHK, perms != null && perms.canGroupWrite()));
329         row.add(getCheckbox(ADMIN_CHK, perms != null && perms.canGroupAdministrate()));
330 
331         // we use transition always, even if user cannot edit.  we use label in cases
332         // where user cannot edit
333         Transition trans = new Transition(PROMPT_EDIT_GROUP, this);
334         trans.setLabel(groupname);
335         trans.addParam(KEY_FIELD_VALUES, grp.getGroupName());
336         trans.addParam(OBJ_TYPE, grp.getClass().getName());
337         trans.addParam(UserGroup.GROUP_NAME_FIELD, groupname);
338         row.add(trans);
339 
340         // can this user edit this group?
341         if (grp.canRequesterAdministrate()) {
342             trans.setAttribute(ROW, "Y");
343         }
344 
345         return row;
346     }
347 
348     private Input getCheckbox(String name, boolean checked) {
349         Input input = new Input(name);
350         input.setType(InputTag.TYPE_CHECKBOX);
351         input.setDefaultValue(checked ? "Y" : "N");
352         if (checked) {
353             input.setAttribute(Input.SELECTED, "Y");
354         }
355         return input;
356     }
357 
358 
359     /***
360      * Do for editing single pick item.
361      *
362      * @param request  The ControllerRequest object.
363      * @param response The ControllerResponse object.
364      * @throws com.jcorporate.expresso.core.controller.ControllerException
365      *          upon error.
366      */
367     protected void runDoEditPermsState(final ExpressoRequest request, final ExpressoResponse response) throws
368             ControllerException {
369         try {
370             String allkeys = request.getParameter(KEY_FIELD_VALUES); // guaranteed by framework
371             String objtype = request.getParameter(OBJ_TYPE); // guaranteed by framework
372 
373             RowSecuredDBObject obj = getObj(objtype, allkeys);
374 
375             // info about all groups already associated
376             Map oldAssociatedMap = getAssociatedGroupPermsHash(obj); // raw values; not including 'fake' Everybody reads
377 
378             // map for collecting checkmarks in Privs objects
379             Map foundPermsMap = new HashMap(oldAssociatedMap.size());
380 
381             // collate checkmarks by groupname
382             Iterator enumer = request.getAllParameters().keySet().iterator();
383             while (enumer.hasNext()) {
384                 String grpname = null;
385                 String pkey = (String) enumer.next();
386                 if (pkey.startsWith(READ_CHK)) {
387                     grpname = pkey.substring(READ_CHK.length());
388                     Privs privs = putPrivs(foundPermsMap, grpname);
389                     privs.isRead = true;
390                 }
391                 if (pkey.startsWith(WRITE_CHK)) {
392                     grpname = pkey.substring(WRITE_CHK.length());
393                     Privs privs = putPrivs(foundPermsMap, grpname);
394                     privs.isWrite = true;
395                 }
396                 if (pkey.startsWith(ADMIN_CHK)) {
397                     grpname = pkey.substring(ADMIN_CHK.length());
398                     Privs privs = putPrivs(foundPermsMap, grpname);
399                     privs.isAdmin = true;
400                 }
401             }
402 
403             // add a row for 'everybody' in case perms have been removed
404             putPrivs(foundPermsMap, UserGroup.ALL_USERS_GROUP);
405 
406             // compare with existing perms and add/update
407             for (Iterator iterator = foundPermsMap.keySet().iterator(); iterator.hasNext();) {
408                 String grpname = (String) iterator.next();
409                 Privs privs = (Privs) foundPermsMap.get(grpname); // guaranteed to be found by iterator
410 
411                 // special handling for 'all users' group, which corresponds to
412                 // rowpermission's OTHER
413                 if (grpname.equals(UserGroup.ALL_USERS_GROUP)) {
414                     RowPermissions rowperms = obj.getPermissions();
415                     boolean othersReadAlways = othersReadAlways();
416                     if (!othersReadAlways) {
417                         othersReadAlways = privs.isRead;
418                     }
419                     // this writes to disk for obj perms
420                     setRowPermsForOTHER(rowperms, othersReadAlways, privs.isWrite, privs.isAdmin);
421                     // we don't write group perms--we save OTHER perms in object itself, above
422                     continue;
423                 }
424 
425                 // does this group already have perms saved?
426                 RowGroupPerms perms = (RowGroupPerms) oldAssociatedMap.remove(grpname);
427                 boolean isNew = false;
428                 if (perms == null) {
429                     isNew = true;
430                     perms = new RowGroupPerms(obj.getJDBCMetaData().getTargetTable(), allkeys, grpname);
431                 }
432 
433                 int permInt = perms.permissions();
434 
435                 // this writes to disk
436                 setGroupPerms(privs.isRead, permInt, privs.isWrite, privs.isAdmin, perms, isNew);
437             } // all associated groups
438 
439             // remove any groups which were unassociated, except ALL USERS
440 //            oldAssociatedMap.remove(UserGroup.ALL_USERS_GROUP);
441             for (Iterator iterator = oldAssociatedMap.values().iterator(); iterator.hasNext();) {
442                 RowGroupPerms perms = (RowGroupPerms) iterator.next();
443                 if (perms.find()) {
444                     perms.delete(true);
445                 }
446             }
447 
448             // default is just index page of current servlet
449             String viewUrl = "/" + getServlet().getServletContext().getServletContextName();
450             if (obj instanceof IViewable) {
451                 viewUrl = ((IViewable) obj).getViewTrans().getFullUrl();
452             }
453 
454             redirectRequest(request, response, viewUrl);
455         } catch (Exception e) {
456             throw new ControllerException(e);
457         }
458     }
459 
460     private Privs putPrivs(Map foundPermsMap, String groupname) {
461         Privs result = (Privs) foundPermsMap.get(groupname);
462         if (result == null) {
463             result = new Privs();
464             foundPermsMap.put(groupname, result);
465         }
466         return result;
467     }
468 
469     private void setGroupPerms(boolean read,
470                                int permissions,
471                                boolean write,
472                                boolean admin,
473                                RowGroupPerms perm,
474                                boolean aNew) throws DBException {
475         if (read) {
476             permissions |= RowPermissions.GROUP_READ_MASK;
477         } else {
478             permissions &= ~RowPermissions.GROUP_READ_MASK;
479         }
480 
481         if (write) {
482             permissions |= RowPermissions.GROUP_WRITE_MASK;
483         } else {
484             permissions &= ~RowPermissions.GROUP_WRITE_MASK;
485         }
486 
487         if (admin) {
488             permissions |= RowPermissions.GROUP_PERMISSION_MASK;
489         } else {
490             permissions &= ~RowPermissions.GROUP_PERMISSION_MASK;
491         }
492 
493         perm.permissions(permissions);
494 
495         if (aNew) {
496             perm.add();
497         } else {
498             perm.update(true);
499         }
500     }
501 
502     /***
503      * set perms for OTHER octet
504      */
505     private void setRowPermsForOTHER(
506             RowPermissions rowperms, boolean read, boolean write, boolean admin) throws DBException {
507         int permissions;
508 
509         permissions = rowperms.permissions();
510         if (read) {
511             permissions |= RowPermissions.OTHERS_READ_MASK;
512         } else {
513             permissions &= ~RowPermissions.OTHERS_READ_MASK;
514         }
515 
516         if (write) {
517             permissions |= RowPermissions.OTHERS_WRITE_MASK;
518         } else {
519             permissions &= ~RowPermissions.OTHERS_WRITE_MASK;
520         }
521 
522         if (admin) {
523             permissions |= RowPermissions.OTHERS_PERMISSION_MASK;
524         } else {
525             permissions &= ~RowPermissions.OTHERS_PERMISSION_MASK;
526         }
527 
528         rowperms.permissions(permissions);
529         rowperms.addOrUpdate();
530     }
531 
532     /***
533      * prompt editing of group; just reuses prompt edit for any RowSec object,
534      * using a PermGroup, a wrapper on UserGroup
535      *
536      * @param request  The ControllerRequest object.
537      * @param response The ControllerResponse object.
538      * @throws com.jcorporate.expresso.core.controller.ControllerException
539      *          upon error.
540      */
541     protected void runPromptEditGroupState(ControllerRequest request,
542                                            ControllerResponse response) throws ControllerException {
543         try {
544             String grpname = request.getParameter(UserGroup.GROUP_NAME_FIELD);
545             PermGroup grp = new PermGroup();
546             grp.setGroupName(grpname);
547             grp.retrieve();
548 
549             request.setParameter(KEY_FIELD_VALUES, grp.getKey());
550             request.setParameter(OBJ_TYPE, grp.getClass().getName());
551             runPromptEditPermsState(request, response);
552 
553         } catch (Exception e) {
554             throw new ControllerException(e);
555         }
556     }
557 }