View Javadoc
1   /**
2    * Copyright 2005-2016 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.view;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.core.api.CoreApiServiceLocator;
20  import org.kuali.rice.core.api.config.property.ConfigurationService;
21  import org.kuali.rice.kim.api.KimConstants;
22  import org.kuali.rice.kim.api.identity.Person;
23  import org.kuali.rice.krad.bo.DataObjectAuthorizerBase;
24  import org.kuali.rice.krad.datadictionary.AttributeSecurity;
25  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
26  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
27  import org.kuali.rice.krad.uif.component.Component;
28  import org.kuali.rice.krad.uif.component.ComponentSecurity;
29  import org.kuali.rice.krad.uif.component.DataBinding;
30  import org.kuali.rice.krad.uif.container.CollectionGroup;
31  import org.kuali.rice.krad.uif.container.Group;
32  import org.kuali.rice.krad.uif.element.Action;
33  import org.kuali.rice.krad.uif.field.DataField;
34  import org.kuali.rice.krad.uif.field.DataFieldSecurity;
35  import org.kuali.rice.krad.uif.field.Field;
36  import org.kuali.rice.krad.uif.field.FieldSecurity;
37  import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
38  import org.kuali.rice.krad.uif.widget.Widget;
39  import org.kuali.rice.krad.util.KRADConstants;
40  import org.kuali.rice.krad.util.KRADUtils;
41  
42  import java.util.HashMap;
43  import java.util.HashSet;
44  import java.util.Map;
45  import java.util.Set;
46  
47  /**
48   * Implementation of {@link ViewAuthorizer} that verifies authorization with KIM permission checks
49   *
50   * <p>
51   * Each permission goes through one of the isAuthorized methods provided by
52   * {@link org.kuali.rice.krad.bo.DataObjectAuthorizer}, these in turn call {@link #addPermissionDetails(Object, java.util.Map)}
53   * and {@link #addRoleQualification(Object, java.util.Map)} for building the permission and role maps to send with
54   * the permission check. Subclasses can override these methods to add additional attributes
55   * </p>
56   *
57   * @author Kuali Rice Team (rice.collab@kuali.org)
58   */
59  @BeanTag(name = "viewAuthorizer")
60  public class ViewAuthorizerBase extends DataObjectAuthorizerBase implements ViewAuthorizer {
61      private static final long serialVersionUID = -2687378084630965412L;
62      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ViewAuthorizerBase.class);
63  
64      private ConfigurationService configurationService;
65  
66      private RequestAuthorizationCache requestAuthorizationCache;
67  
68      /**
69       * @see ViewAuthorizer#getActionFlags(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
70       *      org.kuali.rice.kim.api.identity.Person, java.util.Set<java.lang.String>)
71       */
72      public Set<String> getActionFlags(View view, ViewModel model, Person user, Set<String> actions) {
73          if (actions.contains(KRADConstants.KUALI_ACTION_CAN_EDIT) && !canEditView(view, model, user)) {
74              actions.remove(KRADConstants.KUALI_ACTION_CAN_EDIT);
75          }
76  
77          return actions;
78      }
79  
80      /**
81       * @see ViewAuthorizer#getEditModes(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
82       *      org.kuali.rice.kim.api.identity.Person, java.util.Set<java.lang.String>)
83       */
84      public Set<String> getEditModes(View view, ViewModel model, Person user, Set<String> editModes) {
85          Set<String> unauthorizedEditModes = new HashSet<String>();
86  
87          Object dataObjectForContext = getDataObjectContext(view, model);
88  
89          // loop through supplied editModes and make sure KIM permission exists
90          for (String editMode : editModes) {
91              Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
92              additionalPermissionDetails.put(KimConstants.AttributeConstants.EDIT_MODE, editMode);
93              additionalPermissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, view.getId());
94              boolean exists = permissionExistsByTemplate(dataObjectForContext, KRADConstants.KRAD_NAMESPACE,
95                      KimConstants.PermissionTemplateNames.USE_VIEW, additionalPermissionDetails);
96              if (exists) {
97                  boolean authorized = isAuthorizedByTemplate(dataObjectForContext, KRADConstants.KRAD_NAMESPACE,
98                          KimConstants.PermissionTemplateNames.USE_VIEW, user.getPrincipalId(),
99                          additionalPermissionDetails, null);
100                 if (!authorized) {
101                     unauthorizedEditModes.add(editMode);
102                 }
103             }
104         }
105         editModes.removeAll(unauthorizedEditModes);
106 
107         return editModes;
108     }
109 
110     /**
111      * Checks for an open view permission for the view id, and if found verifies the user has that permission
112      *
113      * @see ViewAuthorizer#canOpenView(View, ViewModel, org.kuali.rice.kim.api.identity.Person)
114      */
115     public boolean canOpenView(View view, ViewModel model, Person user) {
116         Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
117         additionalPermissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, view.getNamespaceCode());
118         additionalPermissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, model.getViewId());
119 
120         if (permissionExistsByTemplate(model, KRADConstants.KRAD_NAMESPACE,
121                 KimConstants.PermissionTemplateNames.OPEN_VIEW, additionalPermissionDetails)) {
122             return isAuthorizedByTemplate(model, KRADConstants.KRAD_NAMESPACE,
123                     KimConstants.PermissionTemplateNames.OPEN_VIEW, user.getPrincipalId(), additionalPermissionDetails,
124                     null);
125         }
126 
127         return true;
128     }
129 
130     /**
131      * Checks for an edit view permission for the view id, and if found verifies the user has that permission
132      *
133      * @see ViewAuthorizer#canEditView(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
134      *      org.kuali.rice.kim.api.identity.Person)
135      */
136     public boolean canEditView(View view, ViewModel model, Person user) {
137         Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
138         additionalPermissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, view.getNamespaceCode());
139         additionalPermissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, model.getViewId());
140 
141         if (permissionExistsByTemplate(model, KRADConstants.KRAD_NAMESPACE,
142                 KimConstants.PermissionTemplateNames.EDIT_VIEW, additionalPermissionDetails)) {
143             return isAuthorizedByTemplate(model, KRADConstants.KRAD_NAMESPACE,
144                     KimConstants.PermissionTemplateNames.EDIT_VIEW, user.getPrincipalId(), additionalPermissionDetails,
145                     null);
146         }
147 
148         return true;
149     }
150 
151     /**
152      * @see ViewAuthorizer#canUnmaskField(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
153      * org.kuali.rice.krad.uif.field.DataField, java.lang.String, org.kuali.rice.kim.api.identity.Person)
154      */
155     public boolean canUnmaskField(View view, ViewModel model, DataField field, String propertyName, Person user) {
156         if (field.getDataFieldSecurity() == null) {
157             return true;
158         }
159 
160         // check mask authz flag is set
161         AttributeSecurity attributeSecurity = field.getDataFieldSecurity().getAttributeSecurity();
162         if (attributeSecurity == null || !attributeSecurity.isMask()) {
163             return true;
164         }
165 
166         // for non-production environments the ability to unmask can be disabled by a system parameter
167         if (isNonProductionEnvAndUnmaskingTurnedOff()) {
168             return false;
169         }
170 
171         Object dataObjectForContext = getDataObjectContext(view, model);
172 
173         Map<String, String> permissionDetails = new HashMap<String, String>();
174         permissionDetails = KRADUtils.getNamespaceAndComponentSimpleName(dataObjectForContext.getClass());
175         permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, propertyName);
176         // TODO: check for namespace, component, attribute override on attribute security
177 
178         if (field.getComponentSecurity().getAdditionalPermissionDetails() != null) {
179             permissionDetails.putAll(field.getComponentSecurity().getAdditionalPermissionDetails());
180         }
181 
182         Map<String, String> roleQualifications = new HashMap<String, String>();
183         if (field.getComponentSecurity().getAdditionalRoleQualifiers() != null) {
184             roleQualifications.putAll(field.getComponentSecurity().getAdditionalRoleQualifiers());
185         }
186 
187         return isAuthorizedByTemplate(dataObjectForContext, KRADConstants.KNS_NAMESPACE,
188                 KimConstants.PermissionTemplateNames.FULL_UNMASK_FIELD, user.getPrincipalId(), permissionDetails,
189                 roleQualifications);
190     }
191 
192     /**
193      * @see ViewAuthorizer#canPartialUnmaskField(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
194      * org.kuali.rice.krad.uif.field.DataField, java.lang.String, org.kuali.rice.kim.api.identity.Person)
195      */
196     public boolean canPartialUnmaskField(View view, ViewModel model, DataField field, String propertyName,
197             Person user) {
198         if (field.getDataFieldSecurity() == null) {
199             return true;
200         }
201 
202         // check partial mask authz flag is set
203         AttributeSecurity attributeSecurity = field.getDataFieldSecurity().getAttributeSecurity();
204         if (attributeSecurity == null || !attributeSecurity.isPartialMask()) {
205             return true;
206         }
207 
208         // for non-production environments the ability to unmask can be disabled by a system parameter
209         if (isNonProductionEnvAndUnmaskingTurnedOff()) {
210             return false;
211         }
212 
213         Object dataObjectForContext = getDataObjectContext(view, model);
214 
215         Map<String, String> permissionDetails = new HashMap<String, String>();
216         permissionDetails = KRADUtils.getNamespaceAndComponentSimpleName(dataObjectForContext.getClass());
217         permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, propertyName);
218         // TODO: check for namespace, component, attribute override on attribute security
219 
220         if (field.getComponentSecurity().getAdditionalPermissionDetails() != null) {
221             permissionDetails.putAll(field.getComponentSecurity().getAdditionalPermissionDetails());
222         }
223 
224         Map<String, String> roleQualifications = new HashMap<String, String>();
225         if (field.getComponentSecurity().getAdditionalRoleQualifiers() != null) {
226             roleQualifications.putAll(field.getComponentSecurity().getAdditionalRoleQualifiers());
227         }
228 
229         return isAuthorizedByTemplate(dataObjectForContext, KRADConstants.KNS_NAMESPACE,
230                 KimConstants.PermissionTemplateNames.PARTIAL_UNMASK_FIELD, user.getPrincipalId(), permissionDetails,
231                 roleQualifications);
232     }
233 
234     /**
235      * @see ViewAuthorizer#canEditField(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
236      * org.kuali.rice.krad.uif.field.Field, java.lang.String, org.kuali.rice.kim.api.identity.Person)
237      */
238     public boolean canEditField(View view, ViewModel model, Field field, String propertyName, Person user) {
239         ComponentSecurity componentSecurity = field.getComponentSecurity();
240 
241         // check component security exists
242         if (componentSecurity == null) {
243             return true;
244         }
245 
246         // first check hide flag is set (lower precedence)
247         if (componentSecurity.isEditAuthz() == null && !isDataFieldAttributeSecurityHide(field)) {
248             return true;
249         }
250 
251         // then check edit authz is set (higher precedence)
252         if (componentSecurity.isEditAuthz() != null && !componentSecurity.isEditAuthz().booleanValue()) {
253             return true;
254         }
255 
256         return isAuthorizedByTemplate(view, field, model, KimConstants.PermissionTemplateNames.EDIT_FIELD, user, null,
257                 null, false);
258     }
259 
260     /**
261      * @see ViewAuthorizer#canViewField(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
262      * org.kuali.rice.krad.uif.field.Field, java.lang.String, org.kuali.rice.kim.api.identity.Person)
263      */
264     public boolean canViewField(View view, ViewModel model, Field field, String propertyName, Person user) {
265         ComponentSecurity componentSecurity = field.getComponentSecurity();
266 
267         // check component security exists
268         if (componentSecurity == null) {
269             return true;
270         }
271 
272         // first check hide flag is set (lower precedence)
273         if (componentSecurity.isViewAuthz() == null && !isDataFieldAttributeSecurityHide(field)) {
274             return true;
275         }
276 
277         // then check view authz is set (higher precedence)
278         if (componentSecurity.isViewAuthz() != null && !componentSecurity.isViewAuthz().booleanValue()) {
279             return true;
280         }
281 
282         return isAuthorizedByTemplate(view, field, model, KimConstants.PermissionTemplateNames.VIEW_FIELD, user, null,
283                 null, false);
284     }
285 
286     /**
287      * @see ViewAuthorizer#canEditGroup(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
288      * org.kuali.rice.krad.uif.container.Group, java.lang.String, org.kuali.rice.kim.api.identity.Person)
289      */
290     public boolean canEditGroup(View view, ViewModel model, Group group, String groupId, Person user) {
291         ComponentSecurity componentSecurity = group.getComponentSecurity();
292 
293         // check component security exists
294         if (componentSecurity == null) {
295             return true;
296         }
297 
298         // check edit group authz flag is set
299         if (componentSecurity.isEditAuthz() == null || !componentSecurity.isEditAuthz().booleanValue()) {
300             return true;
301         }
302 
303         return isAuthorizedByTemplate(view, group, model, KimConstants.PermissionTemplateNames.EDIT_GROUP, user, null,
304                 null, false);
305     }
306 
307     /**
308      * @see ViewAuthorizer#canViewGroup(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
309      * org.kuali.rice.krad.uif.container.Group, java.lang.String, org.kuali.rice.kim.api.identity.Person)
310      */
311     public boolean canViewGroup(View view, ViewModel model, Group group, String groupId, Person user) {
312         ComponentSecurity componentSecurity = group.getComponentSecurity();
313 
314         // check component security exists
315         if (componentSecurity == null) {
316             return true;
317         }
318 
319         // check view group authz flag is set
320         if (componentSecurity.isViewAuthz() == null || !componentSecurity.isViewAuthz().booleanValue()) {
321             return true;
322         }
323 
324         return isAuthorizedByTemplate(view, group, model, KimConstants.PermissionTemplateNames.VIEW_GROUP, user, null,
325                 null, false);
326     }
327 
328     /**
329      * @see ViewAuthorizer#canEditWidget(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
330      * org.kuali.rice.krad.uif.widget.Widget, java.lang.String, org.kuali.rice.kim.api.identity.Person)
331      */
332     public boolean canEditWidget(View view, ViewModel model, Widget widget, String widgetId, Person user) {
333         ComponentSecurity componentSecurity = widget.getComponentSecurity();
334 
335         // check component security exists
336         if (componentSecurity == null) {
337             return true;
338         }
339 
340         // check edit widget authz flag is set
341         if (componentSecurity.isEditAuthz() == null || !componentSecurity.isEditAuthz().booleanValue()) {
342             return true;
343         }
344 
345         return isAuthorizedByTemplate(view, widget, model, KimConstants.PermissionTemplateNames.EDIT_WIDGET, user, null,
346                 null, false);
347     }
348 
349     /**
350      * @see ViewAuthorizer#canViewWidget(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
351      * org.kuali.rice.krad.uif.widget.Widget, java.lang.String, org.kuali.rice.kim.api.identity.Person)
352      */
353     public boolean canViewWidget(View view, ViewModel model, Widget widget, String widgetId, Person user) {
354         ComponentSecurity componentSecurity = widget.getComponentSecurity();
355 
356         // check component security exists
357         if (componentSecurity == null) {
358             return true;
359         }
360 
361         // check view widget authz flag is set
362         if (componentSecurity.isViewAuthz() == null || !componentSecurity.isViewAuthz().booleanValue()) {
363             return true;
364         }
365 
366         return isAuthorizedByTemplate(view, widget, model, KimConstants.PermissionTemplateNames.VIEW_WIDGET, user, null,
367                 null, false);
368     }
369 
370     /**
371      * @see ViewAuthorizer#canPerformAction(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
372      * org.kuali.rice.krad.uif.element.Action, java.lang.String, java.lang.String, org.kuali.rice.kim.api.identity.Person)
373      */
374     public boolean canPerformAction(View view, ViewModel model, Action action, String actionEvent,
375             String actionId, Person user) {
376         // check action authz flag is set
377         if ((action.getActionSecurity() == null) || !action.getActionSecurity().isPerformActionAuthz()) {
378             return true;
379         }
380 
381         Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
382         if (StringUtils.isNotBlank(actionEvent)) {
383             additionalPermissionDetails.put(KimConstants.AttributeConstants.ACTION_EVENT, actionEvent);
384         }
385 
386         return isAuthorizedByTemplate(view, action, model, KimConstants.PermissionTemplateNames.PERFORM_ACTION,
387                 user, additionalPermissionDetails, null, false);
388     }
389 
390     public boolean canEditLine(View view, ViewModel model, CollectionGroup collectionGroup,
391             String collectionPropertyName, Object line, Person user) {
392         // check edit line authz flag is set
393         if ((collectionGroup.getCollectionGroupSecurity() == null) || !collectionGroup.getCollectionGroupSecurity()
394                 .isEditLineAuthz()) {
395             return true;
396         }
397 
398         return isAuthorizedByTemplate(view, collectionGroup, model, KimConstants.PermissionTemplateNames.EDIT_LINE,
399                 user, null, null, false);
400     }
401 
402     public boolean canViewLine(View view, ViewModel model, CollectionGroup collectionGroup,
403             String collectionPropertyName, Object line, Person user) {
404         // check view line authz flag is set
405         if ((collectionGroup.getCollectionGroupSecurity() == null) || !collectionGroup.getCollectionGroupSecurity()
406                 .isViewLineAuthz()) {
407             return true;
408         }
409 
410         return isAuthorizedByTemplate(view, collectionGroup, model, KimConstants.PermissionTemplateNames.VIEW_LINE,
411                 user, null, null, false);
412     }
413 
414     public boolean canEditLineField(View view, ViewModel model, CollectionGroup collectionGroup,
415             String collectionPropertyName, Object line, Field field, String propertyName, Person user) {
416         FieldSecurity fieldSecurity = field.getFieldSecurity();
417 
418         // check field security exists
419         if (fieldSecurity == null) {
420             return true;
421         }
422 
423         // first check hide flag is set (lower precedence)
424         if (fieldSecurity.isEditInLineAuthz() == null && !isDataFieldAttributeSecurityHide(field)) {
425             return true;
426         }
427 
428         // then check edit line field authz flag is set (higher precedence)
429         if (fieldSecurity.isEditInLineAuthz() != null && !fieldSecurity.isEditInLineAuthz().booleanValue()) {
430             return true;
431         }
432 
433         Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
434         additionalPermissionDetails.put(KimConstants.AttributeConstants.GROUP_ID, collectionGroup.getId());
435         additionalPermissionDetails.put(KimConstants.AttributeConstants.COLLECTION_PROPERTY_NAME,
436                 collectionGroup.getPropertyName());
437 
438         return isAuthorizedByTemplate(view, field, model,
439                 KimConstants.PermissionTemplateNames.EDIT_LINE_FIELD, user, additionalPermissionDetails, null, false);
440     }
441 
442     public boolean canViewLineField(View view, ViewModel model, CollectionGroup collectionGroup,
443             String collectionPropertyName, Object line, Field field, String propertyName, Person user) {
444         FieldSecurity fieldSecurity = field.getFieldSecurity();
445 
446         // check field security exists
447         if (fieldSecurity == null) {
448             return true;
449         }
450 
451         // first check hide flag is set (lower precedence)
452         if (fieldSecurity.isViewInLineAuthz() == null && !isDataFieldAttributeSecurityHide(field)) {
453             return true;
454         }
455 
456         // then check view line field authz flag is set (higher precedence)
457         if (fieldSecurity.isViewInLineAuthz() != null && !fieldSecurity.isViewInLineAuthz().booleanValue()) {
458             return true;
459         }
460 
461         Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
462         additionalPermissionDetails.put(KimConstants.AttributeConstants.GROUP_ID, collectionGroup.getId());
463         additionalPermissionDetails.put(KimConstants.AttributeConstants.COLLECTION_PROPERTY_NAME,
464                 collectionGroup.getPropertyName());
465 
466         return isAuthorizedByTemplate(view, field, model,
467                 KimConstants.PermissionTemplateNames.VIEW_LINE_FIELD, user, additionalPermissionDetails, null, false);
468     }
469 
470     public boolean canPerformLineAction(View view, ViewModel model, CollectionGroup collectionGroup,
471             String collectionPropertyName, Object line, Action action, String actionEvent, String actionId,
472             Person user) {
473         // check perform line action authz flag is set
474         if ((action.getActionSecurity() == null) || !action.getActionSecurity().isPerformLineActionAuthz()) {
475             return true;
476         }
477 
478         Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
479         additionalPermissionDetails.put(KimConstants.AttributeConstants.GROUP_ID, collectionGroup.getId());
480         additionalPermissionDetails.put(KimConstants.AttributeConstants.COLLECTION_PROPERTY_NAME,
481                 collectionGroup.getPropertyName());
482         if (StringUtils.isNotBlank(actionEvent)) {
483             additionalPermissionDetails.put(KimConstants.AttributeConstants.ACTION_EVENT, actionEvent);
484         }
485 
486         return isAuthorizedByTemplate(view, action, model,
487                 KimConstants.PermissionTemplateNames.PERFORM_LINE_ACTION, user, additionalPermissionDetails, null,
488                 false);
489     }
490 
491     /**
492      * Retrieves the object from the model that is used as the context for permission checks
493      *
494      * <p>
495      * Used to derive namespace and component details. Subclasses can override to return the object to be used
496      * </p>
497      *
498      * @param view view instance the permission checks are being done for
499      * @param model model object containing the data and from which the data object should be pulled
500      * @return data object instance to use
501      */
502     protected Object getDataObjectContext(View view, ViewModel model) {
503         Object dataObject = model;
504 
505         if (StringUtils.isNotBlank(view.getDefaultBindingObjectPath())) {
506             Object defaultObject = ObjectPropertyUtils.getPropertyValue(model, view.getDefaultBindingObjectPath());
507             if (defaultObject != null) {
508                 dataObject = defaultObject;
509             }
510         }
511 
512         return dataObject;
513     }
514 
515     /**
516      * Builds the permission details map for a field which includes the component namespace, component name, and
517      * field id, in addition to property name for data binding fields
518      *
519      * @param view view instance the field belongs to
520      * @param dataObject default object from the data model (used for subclasses to build details)
521      * @param field field instance the details are being built for
522      * @return permission details for the field
523      */
524     protected Map<String, String> getFieldPermissionDetails(View view, Object dataObject, Field field) {
525         Map<String, String> permissionDetails = new HashMap<String, String>();
526 
527         permissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, view.getNamespaceCode());
528         permissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, view.getId());
529         permissionDetails.put(KimConstants.AttributeConstants.FIELD_ID, field.getId());
530 
531         if (field instanceof DataBinding) {
532             permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME,
533                     ((DataBinding) field).getPropertyName());
534         }
535 
536         return permissionDetails;
537     }
538 
539     /**
540      * Builds the permission details map for a group which includes the component namespace, component name, and
541      * group id, in addition to property name for collection groups
542      *
543      * @param view view instance the group belongs to
544      * @param dataObject default object from the data model (used for subclasses to build details)
545      * @param group group instance the details are being built for
546      * @return permission details for the group
547      */
548     protected Map<String, String> getGroupPermissionDetails(View view, Object dataObject, Group group) {
549         Map<String, String> permissionDetails = new HashMap<String, String>();
550 
551         permissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, view.getNamespaceCode());
552         permissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, view.getId());
553         permissionDetails.put(KimConstants.AttributeConstants.GROUP_ID, group.getId());
554 
555         if (group instanceof CollectionGroup) {
556             permissionDetails.put(KimConstants.AttributeConstants.COLLECTION_PROPERTY_NAME,
557                     ((CollectionGroup) group).getPropertyName());
558         }
559 
560         return permissionDetails;
561     }
562 
563     /**
564      * Builds the permission details map for a widget which includes the namespace, view id, and
565      * widget id
566      *
567      * @param view view instance the widget belongs to
568      * @param dataObject default object from the data model (used for subclasses to build details)
569      * @param widget group instance the details are being built for
570      * @return permission details for group
571      */
572     protected Map<String, String> getWidgetPermissionDetails(View view, Object dataObject, Widget widget) {
573         Map<String, String> permissionDetails = new HashMap<String, String>();
574 
575         permissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, view.getNamespaceCode());
576         permissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, view.getId());
577         permissionDetails.put(KimConstants.AttributeConstants.WIDGET_ID, widget.getId());
578 
579         return permissionDetails;
580     }
581     
582     /**
583      * Builds the permission details map for an action which includes the namespace, view id, and
584      * action id and event
585      *
586      * @param view view instance the widget belongs to
587      * @param dataObject default object from the data model (used for subclasses to build details)
588      * @param action action instance the details are being built for
589      * @return permission details for action
590      */
591     protected Map<String, String> getActionPermissionDetails(View view, Object dataObject, Action action) {
592         Map<String, String> permissionDetails = new HashMap<String, String>();
593 
594         permissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, view.getNamespaceCode());
595         permissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, view.getId());
596         permissionDetails.put(KimConstants.AttributeConstants.FIELD_ID, action.getId());
597 
598         return permissionDetails;
599     }
600 
601     /**
602      * Performs a permission check for the given template name in the context of the given view and component
603      *
604      * <p>
605      * First standard permission details are added based on the type of component the permission check is being
606      * done for.
607      * Then the {@link ComponentSecurity} of the given component is used to pick up additional permission details and
608      * role qualifiers.
609      * </p>
610      *
611      * @param view view instance the component belongs to
612      * @param component component instance the permission check is being done for
613      * @param model object containing the views data
614      * @param permissionTemplateName template name for the permission to check
615      * @param user user to perform the authorization for
616      * @param additionalPermissionDetails additional key/value pairs to pass with the permission details
617      * @param additionalRoleQualifications additional key/value paris to pass with the role qualifiers
618      * @param checkPermissionExistence boolean indicating whether the existence of the permission should be checked
619      * before performing the authorization
620      * @return whether or not the user has authorization; this will be the case if the user has been
621      * granted the permission or checkPermissionExistence is true and the permission does not exist
622      */
623     protected boolean isAuthorizedByTemplate(View view, Component component, ViewModel model,
624             String permissionTemplateName, Person user, Map<String, String> additionalPermissionDetails,
625             Map<String, String> additionalRoleQualifications, boolean checkPermissionExistence) {
626         Map<String, String> permissionDetails = new HashMap<String, String>();
627         Map<String, String> roleQualifications = new HashMap<String, String>();
628 
629         if (additionalPermissionDetails != null) {
630             permissionDetails.putAll(additionalPermissionDetails);
631         }
632 
633         if (additionalRoleQualifications != null) {
634             roleQualifications.putAll(additionalRoleQualifications);
635         }
636 
637         Object dataObjectForContext = getDataObjectContext(view, model);
638 
639         // add permission details depending on the type of component
640         if (component instanceof Field) {
641             permissionDetails.putAll(getFieldPermissionDetails(view, dataObjectForContext, (Field) component));
642         } else if (component instanceof Group) {
643             permissionDetails.putAll(getGroupPermissionDetails(view, dataObjectForContext, (Group) component));
644         } else if (component instanceof Widget) {
645             permissionDetails.putAll(getWidgetPermissionDetails(view, dataObjectForContext, (Widget) component));
646         } else if (component instanceof Action) {
647             permissionDetails.putAll(getActionPermissionDetails(view, dataObjectForContext, (Action) component));
648         }
649 
650         // pick up additional attributes and overrides from component security
651         ComponentSecurity componentSecurity = component.getComponentSecurity();
652 
653         // add configured overrides
654         if (componentSecurity != null) {
655             if (StringUtils.isNotBlank(componentSecurity.getNamespaceAttribute())) {
656                 permissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE,
657                         componentSecurity.getNamespaceAttribute());
658             }
659 
660             if (StringUtils.isNotBlank(componentSecurity.getComponentAttribute())) {
661                 permissionDetails.put(KimConstants.AttributeConstants.COMPONENT_NAME,
662                         componentSecurity.getComponentAttribute());
663             }
664 
665             if (StringUtils.isNotBlank(componentSecurity.getIdAttribute())) {
666                 if (component instanceof Field) {
667                     permissionDetails.put(KimConstants.AttributeConstants.FIELD_ID, componentSecurity.getIdAttribute());
668                 } else if (component instanceof Group) {
669                     permissionDetails.put(KimConstants.AttributeConstants.GROUP_ID, componentSecurity.getIdAttribute());
670                 } else if (component instanceof Widget) {
671                     permissionDetails.put(KimConstants.AttributeConstants.WIDGET_ID,
672                             componentSecurity.getIdAttribute());
673                 }
674             }
675 
676             if (componentSecurity.getAdditionalPermissionDetails() != null) {
677                 permissionDetails.putAll(componentSecurity.getAdditionalPermissionDetails());
678             }
679 
680             if (componentSecurity.getAdditionalRoleQualifiers() != null) {
681                 roleQualifications.putAll(componentSecurity.getAdditionalRoleQualifiers());
682             }
683         }
684 
685         boolean result = true;
686         if (!checkPermissionExistence || (checkPermissionExistence && permissionExistsByTemplate(dataObjectForContext,
687                 KRADConstants.KRAD_NAMESPACE, permissionTemplateName, permissionDetails))) {
688             result = isAuthorizedByTemplate(dataObjectForContext, KRADConstants.KRAD_NAMESPACE, permissionTemplateName,
689                     user.getPrincipalId(), permissionDetails, roleQualifications);
690 
691             if (LOG.isDebugEnabled()) {
692                 LOG.debug("Performed permission check for: " + permissionTemplateName + " and got result: " + result);
693             }
694         }
695 
696         return result;
697     }
698 
699     /**
700      * Indicates whether the environment is non production and unmasking is not enabled by system parameter
701      *
702      * @return true if unmasking is turned off, false if unmasking is allowed
703      */
704     private boolean isNonProductionEnvAndUnmaskingTurnedOff() {
705         return !getConfigurationService().getPropertyValueAsString(KRADConstants.PROD_ENVIRONMENT_CODE_KEY).
706                 equalsIgnoreCase(getConfigurationService().getPropertyValueAsString(KRADConstants.ENVIRONMENT_KEY))
707                 && !getConfigurationService().getPropertyValueAsBoolean(KRADConstants.ENABLE_NONPRODUCTION_UNMASKING);
708     }
709 
710     /**
711      * Determines whether {@code AttributeSecurity} is set on the {@code DataField} and if it is, whether its hide
712      * attribute is enabled.
713      *
714      * @param field the field to check for the hide attribute
715      *
716      * @return true if the hide attribute is enabled, false otherwise
717      */
718     private boolean isDataFieldAttributeSecurityHide(Field field) {
719         if (field instanceof DataField) {
720             DataField dataField = (DataField) field;
721             DataFieldSecurity dataFieldSecurity = dataField.getDataFieldSecurity();
722 
723             if (dataFieldSecurity == null) {
724                 return false;
725             }
726 
727             if (dataFieldSecurity.getAttributeSecurity() == null || !dataFieldSecurity.getAttributeSecurity().isHide()) {
728                 return false;
729             }
730 
731             return true;
732         } else {
733             return false;
734         }
735     }
736 
737     @BeanTagAttribute(name="configurationService",type= BeanTagAttribute.AttributeType.SINGLEBEAN)
738     protected ConfigurationService getConfigurationService() {
739         if (configurationService == null) {
740             return CoreApiServiceLocator.getKualiConfigurationService();
741         }
742         return configurationService;
743     }
744 
745     public void setConfigurationService(ConfigurationService configurationService) {
746         this.configurationService = configurationService;
747     }
748 
749     protected RequestAuthorizationCache getRequestAuthorizationCache() {
750         return requestAuthorizationCache;
751     }
752 
753     /**
754      * {@inheritDoc}
755      */
756     @Override
757     public void setRequestAuthorizationCache(RequestAuthorizationCache requestAuthorizationCache) {
758         this.requestAuthorizationCache = requestAuthorizationCache;
759     }
760 
761 }