View Javadoc
1   /*
2    * Copyright 2009 The Kuali Foundation.
3    *
4    * Licensed under the Educational Community License, Version 1.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/ecl1.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.ole.sec.document;
17  
18  import java.util.HashMap;
19  import java.util.Map;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.kuali.ole.sec.businessobject.SecurityDefinition;
23  import org.kuali.ole.sec.businessobject.SecurityDefinitionDocumentType;
24  import org.kuali.ole.sec.service.AccessSecurityService;
25  import org.kuali.ole.sys.OLEConstants;
26  import org.kuali.ole.sys.context.SpringContext;
27  import org.kuali.rice.kew.api.exception.WorkflowException;
28  import org.kuali.rice.kim.api.KimConstants;
29  import org.kuali.rice.kim.api.common.template.Template;
30  import org.kuali.rice.kim.api.permission.Permission;
31  import org.kuali.rice.kim.api.role.Role;
32  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
33  import org.kuali.rice.kns.document.MaintenanceDocument;
34  import org.kuali.rice.krad.bo.DocumentHeader;
35  import org.kuali.rice.krad.service.DocumentService;
36  import org.kuali.rice.krad.util.KRADConstants;
37  
38  
39  /**
40   * Maintainable implementation for the Security Definition maintenance document. Hooks into Post processing to create the KIM permissions from the definition records
41   */
42  public class SecurityDefinitionMaintainableImpl extends AbstractSecurityModuleMaintainable {
43      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SecurityDefinitionMaintainableImpl.class);
44  
45      /**
46       * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#doRouteStatusChange(org.kuali.rice.krad.bo.DocumentHeader)
47       */
48      @Override
49      public void doRouteStatusChange(DocumentHeader documentHeader) {
50          super.doRouteStatusChange(documentHeader);
51  
52          if (documentHeader.getWorkflowDocument().isProcessed()) {
53              DocumentService documentService = SpringContext.getBean(DocumentService.class);
54              try {
55                  MaintenanceDocument document = (MaintenanceDocument) documentService.getByDocumentHeaderId(documentHeader.getDocumentNumber());
56                  SecurityDefinition oldSecurityDefinition = (SecurityDefinition) document.getOldMaintainableObject().getBusinessObject();
57                  SecurityDefinition newSecurityDefinition = (SecurityDefinition) document.getNewMaintainableObject().getBusinessObject();
58  
59                  oldSecurityDefinition.refreshNonUpdateableReferences();
60                  newSecurityDefinition.refreshNonUpdateableReferences();
61  
62                  boolean newMaintenanceAction = getMaintenanceAction().equalsIgnoreCase(KRADConstants.MAINTENANCE_NEW_ACTION) || getMaintenanceAction().equalsIgnoreCase(KRADConstants.MAINTENANCE_COPY_ACTION);
63  
64                  createOrUpdateDefinitionRole(oldSecurityDefinition, newSecurityDefinition);
65  
66                  createOrUpdateDocumentPermissions(newSecurityDefinition);
67                  createOrUpdateLookupPermission(newSecurityDefinition);
68                  createOrUpdateInquiryPermissions(newSecurityDefinition);
69              }
70              catch (WorkflowException e) {
71                  LOG.error("caught exception while handling handleRouteStatusChange -> documentService.getByDocumentHeaderId(" + documentHeader.getDocumentNumber() + "). ", e);
72                  throw new RuntimeException("caught exception while handling handleRouteStatusChange -> documentService.getByDocumentHeaderId(" + documentHeader.getDocumentNumber() + "). ", e);
73              }
74          }
75      }
76  
77      /**
78       * Creates a new role for the definition (if the definition is new), then grants to the role any new permissions granted for the definition. Also update role active indicator
79       * if indicator changed values on the definition
80       *
81       * @param oldSecurityDefinition SecurityDefinition record before updates
82       * @param newSecurityDefinition SecurityDefinition after updates
83       */
84      protected void createOrUpdateDefinitionRole(SecurityDefinition oldSecurityDefinition, SecurityDefinition newSecurityDefinition ) { //, List<Permission> permissionsToAssign ) {
85          Role oldRole = null;
86          if ( StringUtils.isNotBlank(oldSecurityDefinition.getRoleId()) ) {
87              oldRole = KimApiServiceLocator.getRoleService().getRole(oldSecurityDefinition.getRoleId());
88          }
89  
90          if ( oldRole == null ) {
91              Role.Builder newRole = Role.Builder.create();
92              newRole.setNamespaceCode(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY);
93              newRole.setName(newSecurityDefinition.getName());
94              newRole.setDescription(newSecurityDefinition.getDescription());
95              newRole.setActive(newSecurityDefinition.isActive());
96              newRole.setKimTypeId(getDefaultRoleTypeId());
97              Role createdRole = KimApiServiceLocator.getRoleService().createRole(newRole.build());
98              newSecurityDefinition.setRoleId(createdRole.getId());
99          } else {
100             // update role active indicator if it has been updated on the definition
101             if ( oldSecurityDefinition.isActive() != newSecurityDefinition.isActive() ) {
102                 Role.Builder updatedRole = Role.Builder.create(oldRole);
103                 updatedRole.setActive(newSecurityDefinition.isActive());
104                 KimApiServiceLocator.getRoleService().updateRole(updatedRole.build());
105             }
106         }
107     }
108 
109     /**
110      * Iterates through the document types and creates any new document permissions necessary or updates old permissions setting inactive if needed
111      *
112      * @param oldSecurityDefinition SecurityDefiniton record before requested changes (old side of maintenance document)
113      * @param newSecurityDefinition SecurityDefinition record with requested changes (new side of maintenance document)
114      * @param newMaintenanceAction Indicates whether this is a new maintenance record (old side in empty)
115      */
116     protected void createOrUpdateDocumentPermissions(SecurityDefinition securityDefinition) {
117         for (SecurityDefinitionDocumentType definitionDocumentType : securityDefinition.getDefinitionDocumentTypes()) {
118             String documentType = definitionDocumentType.getFinancialSystemDocumentTypeCode();
119             boolean documentTypePermissionActive = securityDefinition.isActive() && definitionDocumentType.isActive();
120 
121             createOrUpdateDocumentTypePermissions(documentType, documentTypePermissionActive, securityDefinition);
122         }
123     }
124 
125     /**
126      * First tries to retrieve a lookup permission previously setup for this definition. If old permission found it will be updated with the new details and its active indicator
127      * will be set based on the definition active indicator and restrict lookup indicator value. If old permission does not exist but restrict lookup indicator is true on new side
128      * then a new permission will be created and will be active if definition is active on new side.
129      *
130      * @param oldSecurityDefinition SecurityDefiniton record before requested changes (old side of maintenance document)
131      * @param newSecurityDefinition SecurityDefinition record with requested changes (new side of maintenance document)
132      * @param newMaintenanceAction Indicates whether this is a new maintenance record (old side in empty)
133      */
134     protected void createOrUpdateLookupPermission(SecurityDefinition securityDefinition) {
135         Template lookupTemplate = getAccessSecurityService().getLookupWithFieldValueTemplate();
136 
137         String permissionName = securityDefinition.getName() + "/" + lookupTemplate.getName();
138 
139         createOrUpdatePermissionAndAssignToRole(permissionName, securityDefinition.getRoleId(), securityDefinition.getDescription(), securityDefinition.isRestrictLookup(), lookupTemplate, getLookupPermissionDetails(securityDefinition));
140     }
141 
142     /**
143      * First tries to find inquiry permissions for GL namespace and LD namespace. If old permissions are found they will be updated with the new details and active indicator will
144      * be set based on the definition active indicator and restrict gl indicator (for gl inqury permission) and restrict ld inquiry (for ld inquiry permission). If an old
145      * permission does not exist for one or both of the namespaces and the corresponding indicators are set to true on new side then new permissions will be created with active
146      * indicator set to true if definition is active on new side.
147      *
148      * @param oldSecurityDefinition SecurityDefiniton record before requested changes (old side of maintenance document)
149      * @param newSecurityDefinition SecurityDefinition record with requested changes (new side of maintenance document)
150      * @param newMaintenanceAction Indicates whether this is a new maintenance record (old side in empty)
151      */
152     protected void createOrUpdateInquiryPermissions(SecurityDefinition securityDefinition) {
153         // find old inquiry permissions
154         Template inquiryTemplate = getAccessSecurityService().getInquiryWithFieldValueTemplate();
155         String glPermissionName = securityDefinition.getName() + "/" + inquiryTemplate.getName() + "/" + OLEConstants.CoreModuleNamespaces.GL;
156 
157         Permission glPermission = KimApiServiceLocator.getPermissionService().findPermByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, glPermissionName );
158 
159         // need to save gl inquiry permission if new side indicator is true or already has a permission in which case we need to update details and active indicator
160         createOrUpdatePermissionAndAssignToRole(glPermissionName, securityDefinition.getRoleId(), securityDefinition.getDescription(), securityDefinition.isRestrictGLInquiry(), inquiryTemplate, getInquiryPermissionDetails(OLEConstants.CoreModuleNamespaces.GL,securityDefinition));
161     }
162 
163     /**
164      * For each of the document templates ids calls helper method to create or update corresponding permission
165      *
166      * @param documentType workflow document type name for permission detail
167      * @param active boolean indicating whether the permissions should be set to active (true) or non-active (false)
168      * @param oldSecurityDefinition SecurityDefiniton record before requested changes (old side of maintenance document)
169      * @param newSecurityDefinition SecurityDefinition record with requested changes (new side of maintenance document)
170      */
171     protected void createOrUpdateDocumentTypePermissions(String documentType, boolean active, SecurityDefinition securityDefinition) {
172         Map<String,String> permissionDetails = populateDocumentTypePermissionDetails(documentType, securityDefinition);
173         // Permission Names must be unique
174         // So - Security Definition Name/template name/document type
175         // view document
176         Template permissionTemplate = getAccessSecurityService().getViewDocumentWithFieldValueTemplate();
177         String permissionName = securityDefinition.getName() + "/" + permissionTemplate.getName() + "/" + documentType;
178         createOrUpdatePermissionAndAssignToRole(permissionName, securityDefinition.getRoleId(), securityDefinition.getDescription(),
179                 active && securityDefinition.isRestrictViewDocument(), permissionTemplate, permissionDetails);
180 
181         // view accounting line
182         permissionTemplate = getAccessSecurityService().getViewAccountingLineWithFieldValueTemplate();
183         permissionName = securityDefinition.getName() + "/" + permissionTemplate.getName() + "/" + documentType;
184         createOrUpdatePermissionAndAssignToRole(permissionName, securityDefinition.getRoleId(), securityDefinition.getDescription(),
185                 active && securityDefinition.isRestrictViewAccountingLine(),  permissionTemplate, permissionDetails);
186 
187         // view notes/attachments
188         permissionTemplate = getAccessSecurityService().getViewNotesAttachmentsWithFieldValueTemplate();
189         permissionName = securityDefinition.getName() + "/" + permissionTemplate.getName() + "/" + documentType;
190         createOrUpdatePermissionAndAssignToRole(permissionName, securityDefinition.getRoleId(), securityDefinition.getDescription(),
191                 active && securityDefinition.isRestrictViewNotesAndAttachments(),  permissionTemplate, permissionDetails);
192 
193         // edit accounting line
194         permissionTemplate = getAccessSecurityService().getEditAccountingLineWithFieldValueTemplate();
195         permissionName = securityDefinition.getName() + "/" + permissionTemplate.getName() + "/" + documentType;
196         createOrUpdatePermissionAndAssignToRole(permissionName, securityDefinition.getRoleId(), securityDefinition.getDescription(),
197                 active && securityDefinition.isRestrictEditAccountingLine(),  permissionTemplate, permissionDetails);
198 
199         // edit document
200         permissionTemplate = getAccessSecurityService().getEditDocumentWithFieldValueTemplate();
201         permissionName = securityDefinition.getName() + "/" + permissionTemplate.getName() + "/" + documentType;
202         createOrUpdatePermissionAndAssignToRole(permissionName, securityDefinition.getRoleId(), securityDefinition.getDescription(),
203                 active && securityDefinition.isRestrictEditDocument(),  permissionTemplate, permissionDetails);
204     }
205 
206     /**
207      * Builds an Map<String,String> populated from the given method parameters. Details are set based on the KIM 'Security Document Permission' type.
208      *
209      * @param documentType workflow document type name
210      * @param securityDefinition SecurityDefiniton record
211      * @return Map<String,String> populated with document type name, property name, operator, and property value details
212      */
213     protected Map<String,String> populateDocumentTypePermissionDetails(String documentType, SecurityDefinition securityDefinition) {
214         Map<String,String> permissionDetails = new HashMap<String,String>();
215         permissionDetails.put(KimConstants.AttributeConstants.DOCUMENT_TYPE_NAME, documentType);
216         permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, securityDefinition.getSecurityAttribute().getName());
217 
218         return permissionDetails;
219     }
220 
221     /**
222      * Builds an Map<String,String> populated from the given method parameters. Details are set based on the KIM 'Security Lookup Permission' type.
223      *
224      * @param securityDefinition SecurityDefiniton record
225      * @return Map<String,String> populated with property name, operator, and property value details
226      */
227     protected Map<String,String> getLookupPermissionDetails(SecurityDefinition securityDefinition) {
228         Map<String,String> permissionDetails = new HashMap<String,String>();
229         permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, securityDefinition.getSecurityAttribute().getName());
230 
231         return permissionDetails;
232     }
233 
234     /**
235      * Builds an Map<String,String> populated from the given method parameters. Details are set based on the KIM 'Security Inquiry Permission' type.
236      *
237      * @param namespaceCode KIM namespace code
238      * @param securityDefinition SecurityDefiniton record
239      * @return Map<String,String> populated with namespace, property name, operator, and property value details
240      */
241     protected Map<String,String> getInquiryPermissionDetails(String namespaceCode, SecurityDefinition securityDefinition) {
242         Map<String,String> permissionDetails = new HashMap<String,String>();
243         permissionDetails.put(KimConstants.AttributeConstants.NAMESPACE_CODE, namespaceCode);
244         permissionDetails.put(KimConstants.AttributeConstants.PROPERTY_NAME, securityDefinition.getSecurityAttribute().getName());
245 
246         return permissionDetails;
247     }
248 
249     /**
250      * Determines whether a given document type name is included in the document type list for the given security definition
251      *
252      * @param documentType KEW document type name
253      * @param oldSecurityDefinition SecurityDefinition record
254      * @return boolean indicating whether the document type is associated with the given security definition
255      */
256     protected boolean isDocumentTypeInDefinition(String documentType, SecurityDefinition oldSecurityDefinition) {
257         for (SecurityDefinitionDocumentType definitionDocumentType : oldSecurityDefinition.getDefinitionDocumentTypes()) {
258             String oldDocumentType = definitionDocumentType.getFinancialSystemDocumentTypeCode();
259             if (StringUtils.equals(documentType, oldDocumentType)) {
260                 return true;
261             }
262         }
263 
264         return false;
265     }
266 
267     /**
268      * Calls PermissionUpdateService to save a permission.
269      *
270      * @param securityDefinition SecurityDefinition record
271      * @param permissionId ID for the permission being saved, or empty for new permission
272      * @param permissionTemplateId KIM template ID for permission to save
273      * @param active boolean indicating whether the permission should be set to active (true) or non-active (false)
274      * @param permissionDetails Map<String,String> representing the permission details
275      * @see org.kuali.rice.kim.service.PermissionUpdateService#savePermission()
276      */
277     protected void createOrUpdatePermissionAndAssignToRole(String permissionName, String roleId, String permissionDescription, boolean active, Template permissionTemplate, Map<String,String> permissionDetails) {
278         // Get the existing permission
279         Permission perm = KimApiServiceLocator.getPermissionService().findPermByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, permissionName);
280 
281         if ( perm == null ) {
282             if ( active ) {
283                 Permission.Builder newPerm = Permission.Builder.create(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, permissionName);
284                 newPerm.setTemplate( Template.Builder.create(permissionTemplate) );
285                 newPerm.setDescription(permissionDescription );
286                 newPerm.setAttributes(permissionDetails);
287                 newPerm.setActive(true);
288                 if ( LOG.isDebugEnabled() ) {
289                     LOG.debug( "About to save new permission: " + newPerm);
290                 }
291                 perm = KimApiServiceLocator.getPermissionService().createPermission(newPerm.build());
292             }
293         } else {
294             if ( perm.isActive() != active ) {
295                 Permission.Builder updatedPerm = Permission.Builder.create(perm);
296                 updatedPerm.setActive(active);
297                 perm = KimApiServiceLocator.getPermissionService().updatePermission(updatedPerm.build());
298             }
299         }
300 
301         assignPermissionToRole(perm, roleId);
302     }
303 
304     protected void assignPermissionToRole( Permission perm, String roleId ) {
305         if ( perm != null ) {
306             if ( perm.isActive() ) {
307                 KimApiServiceLocator.getRoleService().assignPermissionToRole(perm.getId(), roleId );
308             } else {
309                 KimApiServiceLocator.getRoleService().revokePermissionFromRole(perm.getId(), roleId );
310             }
311         }
312     }
313 
314     /**
315      * Override to clear out KIM role id on copy
316      *
317      * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterCopy(org.kuali.rice.kns.document.MaintenanceDocument,
318      *      java.util.Map)
319      */
320     @Override
321     public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> parameters) {
322         SecurityDefinition securityDefinition = (SecurityDefinition) document.getNewMaintainableObject().getBusinessObject();
323         securityDefinition.setRoleId("");
324 
325         super.processAfterCopy(document, parameters);
326     }
327 
328     private static AccessSecurityService accessSecurityService;
329 
330     public static AccessSecurityService getAccessSecurityService() {
331         if ( accessSecurityService == null ) {
332             accessSecurityService = SpringContext.getBean(AccessSecurityService.class);
333         }
334         return accessSecurityService;
335     }
336 }