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.Collection;
19  import java.util.HashMap;
20  import java.util.HashSet;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.commons.lang.StringUtils;
25  import org.kuali.ole.sec.businessobject.SecurityDefinition;
26  import org.kuali.ole.sec.businessobject.SecurityModel;
27  import org.kuali.ole.sec.businessobject.SecurityModelDefinition;
28  import org.kuali.ole.sec.businessobject.SecurityModelMember;
29  import org.kuali.ole.sec.businessobject.SecurityPrincipal;
30  import org.kuali.ole.sys.OLEConstants;
31  import org.kuali.ole.sys.context.SpringContext;
32  import org.kuali.rice.core.api.membership.MemberType;
33  import org.kuali.rice.kew.api.exception.WorkflowException;
34  import org.kuali.rice.kim.api.group.GroupService;
35  import org.kuali.rice.kim.api.role.Role;
36  import org.kuali.rice.kim.api.role.RoleMember;
37  import org.kuali.rice.kim.api.role.RoleService;
38  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
39  import org.kuali.rice.kns.document.MaintenanceDocument;
40  import org.kuali.rice.krad.bo.DocumentHeader;
41  import org.kuali.rice.krad.service.BusinessObjectService;
42  import org.kuali.rice.krad.service.DocumentService;
43  import org.kuali.rice.krad.util.KRADConstants;
44  import org.springframework.util.ObjectUtils;
45  
46  
47  /**
48   * Maintainable implementation for the Security Model maintenance document. Hooks into Post processing to create a KIM role from
49   * Model and assigns users/permissions to role based on Model
50   */
51  public class SecurityModelMaintainableImpl extends AbstractSecurityModuleMaintainable {
52      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SecurityModelMaintainableImpl.class);
53  
54      /**
55       * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#doRouteStatusChange(org.kuali.rice.krad.bo.DocumentHeader)
56       */
57      @Override
58      public void doRouteStatusChange(DocumentHeader documentHeader) {
59          super.doRouteStatusChange(documentHeader);
60  
61          if (documentHeader.getWorkflowDocument().isProcessed()) {
62              DocumentService documentService = SpringContext.getBean(DocumentService.class);
63              try {
64                  MaintenanceDocument document = (MaintenanceDocument) documentService.getByDocumentHeaderId(documentHeader.getDocumentNumber());
65                  SecurityModel oldSecurityModel = (SecurityModel) document.getOldMaintainableObject().getBusinessObject();
66                  SecurityModel newSecurityModel = (SecurityModel) document.getNewMaintainableObject().getBusinessObject();
67  
68                  boolean newMaintenanceAction = getMaintenanceAction().equalsIgnoreCase(KRADConstants.MAINTENANCE_NEW_ACTION) || getMaintenanceAction().equalsIgnoreCase(KRADConstants.MAINTENANCE_COPY_ACTION);
69  
70                  Role modelRole = createOrUpdateModelRole(newSecurityModel);
71                  assignOrUpdateModelMembershipToDefinitionRoles(modelRole, oldSecurityModel, newSecurityModel, newMaintenanceAction);
72                  assignOrUpdateModelMembers(modelRole, newSecurityModel);
73  
74                  if (!newSecurityModel.isActive()) {
75                      inactivateModelRole(modelRole);
76                  }
77              }
78              catch (WorkflowException e) {
79                  LOG.error("caught exception while handling handleRouteStatusChange -> documentService.getByDocumentHeaderId(" + documentHeader.getDocumentNumber() + "). ", e);
80                  throw new RuntimeException("caught exception while handling handleRouteStatusChange -> documentService.getByDocumentHeaderId(" + documentHeader.getDocumentNumber() + "). ", e);
81              }
82          }
83      }
84  
85      /**
86       * Creates a new role for the model (if the model is new), otherwise updates the role
87       * 
88       * @param oldSecurityModel SecurityModel record before updates
89       * @param newSecurityModel SecurityModel after updates
90       */
91      protected Role createOrUpdateModelRole(SecurityModel newSecurityModel) {
92          RoleService roleService = KimApiServiceLocator.getRoleService();
93  
94          // the roles are created in the KFS-SEC namespace with the same name as the model
95          Role modelRole = roleService.getRoleByNamespaceCodeAndName(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY, newSecurityModel.getName());
96  
97          if ( modelRole != null ) {
98          // always set the role as active so we can add members and definitions, after processing the indicator will be updated to
99          // the appropriate value
100             Role.Builder updatedRole = Role.Builder.create(modelRole);
101             updatedRole.setActive(true);
102             updatedRole.setDescription(newSecurityModel.getDescription());
103             modelRole = roleService.updateRole(updatedRole.build());
104         } else {
105             String roleId = OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY+"-"+newSecurityModel.getId();
106             Role.Builder newRole = Role.Builder.create();
107             newRole.setId(roleId);
108             newRole.setName(newSecurityModel.getName());
109             newRole.setNamespaceCode(OLEConstants.CoreModuleNamespaces.ACCESS_SECURITY);
110             newRole.setDescription(newSecurityModel.getDescription());
111             newRole.setKimTypeId(getDefaultRoleTypeId());
112             newRole.setActive(true);
113             modelRole = roleService.createRole(newRole.build());
114         }
115         newSecurityModel.setRoleId(modelRole.getId());
116         return modelRole;
117     }
118 
119     /**
120      * Saves the given security model setting the active indicator to false
121      * 
122      * @param newSecurityModel SecurityModel to inactivate
123      */
124     protected void inactivateModelRole(Role modelRole) {
125         RoleService roleService = KimApiServiceLocator.getRoleService();
126 
127         if ( modelRole != null ) {
128             Role.Builder updatedRole = Role.Builder.create(modelRole);
129             updatedRole.setActive(false);
130             KimApiServiceLocator.getRoleService().updateRole(updatedRole.build());
131         }
132     }
133 
134     /**
135      * Iterates through the model definition list and assigns the model role to the definition role if necessary or updates the
136      * current member assignment
137      * 
138      * @param oldSecurityModel SecurityModel record before updates
139      * @param newSecurityModel SecurityModel whose membership should be updated
140      * @param newMaintenanceAction boolean indicating whether this is a new record (old side will not contain data)
141      */
142     protected void assignOrUpdateModelMembershipToDefinitionRoles(Role modelRole, SecurityModel oldSecurityModel, SecurityModel newSecurityModel, boolean newMaintenanceAction) {
143         RoleService roleService = KimApiServiceLocator.getRoleService();
144 
145         if ( modelRole == null ) {
146             LOG.error( "Model Role does not exist for SecurityModel: " + newSecurityModel );
147             throw new RuntimeException("Model Role does not exist for SecurityModel: " + newSecurityModel );
148         }
149 
150         for (SecurityModelDefinition securityModelDefinition : newSecurityModel.getModelDefinitions()) {
151             SecurityDefinition securityDefinition = securityModelDefinition.getSecurityDefinition();
152 
153             Role definitionRole = roleService.getRole(securityDefinition.getRoleId());
154 
155             if ( definitionRole == null ) {
156                 LOG.error( "Definition Role does not exist for SecurityModelDefinition: " + securityDefinition );
157                 throw new RuntimeException("Definition Role does not exist for SecurityModelDefinition: " + securityDefinition );
158             }
159 
160             RoleMember modelRoleMembership = null;
161             if (!newMaintenanceAction) {
162                 SecurityModelDefinition oldSecurityModelDefinition = null;
163                 for (SecurityModelDefinition modelDefinition : oldSecurityModel.getModelDefinitions()) {
164                     if ( ObjectUtils.nullSafeEquals(modelDefinition.getModelDefinitionId(), securityModelDefinition.getModelDefinitionId()) ) {
165                         oldSecurityModelDefinition = modelDefinition;
166                         break;
167                     }
168                 }
169 
170                 if (oldSecurityModelDefinition != null) {
171                     modelRoleMembership = getRoleMembershipForMemberType(definitionRole.getId(),
172                             modelRole.getId(), MemberType.ROLE.getCode(),
173                             getRoleQualifiersFromSecurityModelDefinition(oldSecurityModelDefinition));
174                 }
175             }
176 
177             // only create membership if model is active and the model definition record is active
178             boolean membershipActive = newSecurityModel.isActive() && securityModelDefinition.isActive();
179 
180             // if membership already exists, need to remove if the model definition record is now inactive or the qualifications
181             // need updated
182             if (modelRoleMembership != null) {
183                 if (!membershipActive) {
184                     roleService.removeRoleFromRole(modelRoleMembership.getMemberId(), definitionRole.getNamespaceCode(), definitionRole.getName(), modelRoleMembership.getAttributes());
185                 }
186             }
187 
188             // create of update role if membership should be active
189             if (membershipActive) {
190                 if ( modelRoleMembership == null ) {
191                     modelRoleMembership = roleService.assignRoleToRole(modelRole.getId(), definitionRole.getNamespaceCode(), definitionRole.getName(), getRoleQualifiersFromSecurityModelDefinition(securityModelDefinition));
192                 } else {
193                     RoleMember.Builder updatedRoleMember = RoleMember.Builder.create(modelRoleMembership);
194                     updatedRoleMember.setActiveToDate(null);
195                     updatedRoleMember.setAttributes(getRoleQualifiersFromSecurityModelDefinition(securityModelDefinition));
196                     modelRoleMembership = roleService.updateRoleMember(updatedRoleMember.build());
197                 }
198             }
199         }
200     }
201 
202     /**
203      * Iterates through the model member list and assign members to the model role or updates the membership
204      * 
205      * @param securityModel SecurityModel whose member list should be updated
206      */
207     protected void assignOrUpdateModelMembers( Role modelRole, SecurityModel securityModel) {
208         if (modelRole == null) {
209             // this should throw an elegant error if either are null
210             String error = "Data problem with access security. KIM Role backing the security model is missing.  SecurityModel: " + securityModel;
211             LOG.error(error);
212             throw new RuntimeException(error);
213                 }
214     
215         for (SecurityModelMember modelMember : securityModel.getModelMembers()) {
216             updateSecurityModelRoleMember(modelRole, modelMember, modelMember.getMemberTypeCode(), modelMember.getMemberId(), new HashMap<String, String>(0));
217     
218                 createPrincipalSecurityRecords(modelMember.getMemberId(), modelMember.getMemberTypeCode());
219             }
220         }
221 
222     /**
223      * Creates security principal records for model members (if necessary) so that they will appear on security principal lookup for
224      * editing
225      * 
226      * @param memberId String member id of model role
227      * @param memberTypeCode String member type code for member
228      */
229     protected void createPrincipalSecurityRecords(String memberId, String memberTypeCode) {
230         Collection<String> principalIds = new HashSet<String>();
231 
232         if (MemberType.PRINCIPAL.getCode().equals(memberTypeCode)) {
233             principalIds.add(memberId);
234         }
235         else if (MemberType.ROLE.getCode().equals(memberTypeCode)) {
236             Role roleInfo = KimApiServiceLocator.getRoleService().getRole(memberId);
237             Collection<String> rolePrincipalIds = KimApiServiceLocator.getRoleService().getRoleMemberPrincipalIds(roleInfo.getNamespaceCode(), roleInfo.getName(), null);
238             principalIds.addAll(rolePrincipalIds);
239         }
240         else if (MemberType.GROUP.getCode().equals(memberTypeCode)) {
241             List<String> groupPrincipalIds = KimApiServiceLocator.getGroupService().getMemberPrincipalIds(memberId);
242             principalIds.addAll(groupPrincipalIds);
243         }
244 
245         BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class);
246         for (String principalId : principalIds) {
247             SecurityPrincipal securityPrincipal = businessObjectService.findBySinglePrimaryKey(SecurityPrincipal.class, principalId);
248             if (securityPrincipal == null) {
249                 SecurityPrincipal newSecurityPrincipal = new SecurityPrincipal();
250                 newSecurityPrincipal.setPrincipalId(principalId);
251 
252                 businessObjectService.save(newSecurityPrincipal);
253             }
254         }
255     }
256 
257     /**
258      * Determines whether the given definition is part of the SecurityModel associated definitions
259      * 
260      * @param definitionName name of definition to look for
261      * @param securityModel SecurityModel to check
262      * @return boolean true if the definition is in the security model, false if not
263      */
264     protected boolean isDefinitionInModel(String definitionName, SecurityModel securityModel) {
265         for (SecurityModelDefinition securityModelDefinition : securityModel.getModelDefinitions()) {
266             if (StringUtils.equalsIgnoreCase(definitionName, securityModelDefinition.getSecurityDefinition().getName())) {
267                 return true;
268             }
269         }
270 
271         return false;
272     }
273 
274     /**
275      * Override to clear out KIM role id on copy
276      * 
277      * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterCopy(org.kuali.rice.kns.document.MaintenanceDocument,
278      *      java.util.Map)
279      */
280     @Override
281     public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> parameters) {
282         SecurityModel securityModel = (SecurityModel) document.getNewMaintainableObject().getBusinessObject();
283         securityModel.setRoleId("");
284         
285         super.processAfterCopy(document, parameters);
286     }
287 
288 
289 }