001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krad.uif.view;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.CoreApiServiceLocator;
020import org.kuali.rice.core.api.config.property.ConfigurationService;
021import org.kuali.rice.kim.api.KimConstants;
022import org.kuali.rice.kim.api.identity.Person;
023import org.kuali.rice.krad.bo.DataObjectAuthorizerBase;
024import org.kuali.rice.krad.datadictionary.AttributeSecurity;
025import org.kuali.rice.krad.datadictionary.parse.BeanTag;
026import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
027import org.kuali.rice.krad.uif.component.Component;
028import org.kuali.rice.krad.uif.component.ComponentSecurity;
029import org.kuali.rice.krad.uif.component.DataBinding;
030import org.kuali.rice.krad.uif.container.CollectionGroup;
031import org.kuali.rice.krad.uif.container.Group;
032import org.kuali.rice.krad.uif.element.Action;
033import org.kuali.rice.krad.uif.field.DataField;
034import org.kuali.rice.krad.uif.field.DataFieldSecurity;
035import org.kuali.rice.krad.uif.field.Field;
036import org.kuali.rice.krad.uif.field.FieldSecurity;
037import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
038import org.kuali.rice.krad.uif.widget.Widget;
039import org.kuali.rice.krad.util.KRADConstants;
040import org.kuali.rice.krad.util.KRADUtils;
041
042import java.util.HashMap;
043import java.util.HashSet;
044import java.util.Map;
045import java.util.Set;
046
047/**
048 * Implementation of {@link ViewAuthorizer} that verifies authorization with KIM permission checks
049 *
050 * <p>
051 * Each permission goes through one of the isAuthorized methods provided by
052 * {@link org.kuali.rice.krad.bo.DataObjectAuthorizer}, these in turn call {@link #addPermissionDetails(Object, java.util.Map)}
053 * and {@link #addRoleQualification(Object, java.util.Map)} for building the permission and role maps to send with
054 * the permission check. Subclasses can override these methods to add additional attributes
055 * </p>
056 *
057 * @author Kuali Rice Team (rice.collab@kuali.org)
058 */
059@BeanTag(name = "viewAuthorizer")
060public class ViewAuthorizerBase extends DataObjectAuthorizerBase implements ViewAuthorizer {
061    private static final long serialVersionUID = -2687378084630965412L;
062    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ViewAuthorizerBase.class);
063
064    private ConfigurationService configurationService;
065
066    private RequestAuthorizationCache requestAuthorizationCache;
067
068    /**
069     * @see ViewAuthorizer#getActionFlags(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
070     *      org.kuali.rice.kim.api.identity.Person, java.util.Set<java.lang.String>)
071     */
072    public Set<String> getActionFlags(View view, ViewModel model, Person user, Set<String> actions) {
073        if (actions.contains(KRADConstants.KUALI_ACTION_CAN_EDIT) && !canEditView(view, model, user)) {
074            actions.remove(KRADConstants.KUALI_ACTION_CAN_EDIT);
075        }
076
077        return actions;
078    }
079
080    /**
081     * @see ViewAuthorizer#getEditModes(org.kuali.rice.krad.uif.view.View, org.kuali.rice.krad.uif.view.ViewModel,
082     *      org.kuali.rice.kim.api.identity.Person, java.util.Set<java.lang.String>)
083     */
084    public Set<String> getEditModes(View view, ViewModel model, Person user, Set<String> editModes) {
085        Set<String> unauthorizedEditModes = new HashSet<String>();
086
087        Object dataObjectForContext = getDataObjectContext(view, model);
088
089        // loop through supplied editModes and make sure KIM permission exists
090        for (String editMode : editModes) {
091            Map<String, String> additionalPermissionDetails = new HashMap<String, String>();
092            additionalPermissionDetails.put(KimConstants.AttributeConstants.EDIT_MODE, editMode);
093            additionalPermissionDetails.put(KimConstants.AttributeConstants.VIEW_ID, view.getId());
094            boolean exists = permissionExistsByTemplate(dataObjectForContext, KRADConstants.KRAD_NAMESPACE,
095                    KimConstants.PermissionTemplateNames.USE_VIEW, additionalPermissionDetails);
096            if (exists) {
097                boolean authorized = isAuthorizedByTemplate(dataObjectForContext, KRADConstants.KRAD_NAMESPACE,
098                        KimConstants.PermissionTemplateNames.USE_VIEW, user.getPrincipalId(),
099                        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}