001 /* 002 * Copyright 2011 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 */ 016 package org.kuali.kfs.sec.document; 017 018 import java.util.ArrayList; 019 import java.util.Collection; 020 import java.util.HashMap; 021 import java.util.HashSet; 022 import java.util.Iterator; 023 import java.util.List; 024 import java.util.Map; 025 026 import org.apache.commons.lang.StringUtils; 027 import org.kuali.kfs.sec.SecConstants; 028 import org.kuali.kfs.sec.businessobject.SecurityDefinition; 029 import org.kuali.kfs.sec.businessobject.SecurityModel; 030 import org.kuali.kfs.sec.businessobject.SecurityModelDefinition; 031 import org.kuali.kfs.sec.businessobject.SecurityModelMember; 032 import org.kuali.kfs.sec.businessobject.SecurityPrincipal; 033 import org.kuali.kfs.sec.identity.SecKimAttributes; 034 import org.kuali.kfs.sec.util.KimUtil; 035 import org.kuali.kfs.sys.KFSPropertyConstants; 036 import org.kuali.kfs.sys.context.SpringContext; 037 import org.kuali.kfs.sys.document.FinancialSystemMaintainable; 038 import org.kuali.rice.kew.exception.WorkflowException; 039 import org.kuali.rice.kim.bo.role.dto.KimRoleInfo; 040 import org.kuali.rice.kim.bo.role.dto.RoleMembershipInfo; 041 import org.kuali.rice.kim.bo.types.dto.AttributeSet; 042 import org.kuali.rice.kim.service.GroupService; 043 import org.kuali.rice.kim.service.IdentityManagementService; 044 import org.kuali.rice.kim.service.RoleManagementService; 045 import org.kuali.rice.kim.util.KIMPropertyConstants; 046 import org.kuali.rice.kim.util.KimConstants; 047 import org.kuali.rice.kns.bo.DocumentHeader; 048 import org.kuali.rice.kns.bo.PersistableBusinessObject; 049 import org.kuali.rice.kns.document.MaintenanceDocument; 050 import org.kuali.rice.kns.service.BusinessObjectService; 051 import org.kuali.rice.kns.service.DocumentService; 052 import org.kuali.rice.kns.util.KNSConstants; 053 054 055 /** 056 * Maintainable implementation for the Security Model maintenance document. Hooks into Post processing to create a KIM role from 057 * Model and assigns users/permissions to role based on Model 058 */ 059 public class SecurityModelMaintainableImpl extends FinancialSystemMaintainable { 060 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SecurityModelMaintainableImpl.class); 061 062 /** 063 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#refresh(java.lang.String, java.util.Map, 064 * org.kuali.rice.kns.document.MaintenanceDocument) 065 */ 066 @Override 067 public void refresh(String refreshCaller, Map fieldValues, MaintenanceDocument document) { 068 super.refresh(refreshCaller, fieldValues, document); 069 070 this.getBusinessObject().refreshNonUpdateableReferences(); 071 for (Iterator iterator = newCollectionLines.values().iterator(); iterator.hasNext();) { 072 PersistableBusinessObject businessObject = (PersistableBusinessObject) iterator.next(); 073 businessObject.refreshNonUpdateableReferences(); 074 } 075 } 076 077 /** 078 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#doRouteStatusChange(org.kuali.rice.kns.bo.DocumentHeader) 079 */ 080 @Override 081 public void doRouteStatusChange(DocumentHeader documentHeader) { 082 super.doRouteStatusChange(documentHeader); 083 084 if (documentHeader.getWorkflowDocument().stateIsProcessed()) { 085 DocumentService documentService = SpringContext.getBean(DocumentService.class); 086 try { 087 MaintenanceDocument document = (MaintenanceDocument) documentService.getByDocumentHeaderId(documentHeader.getDocumentNumber()); 088 SecurityModel oldSecurityModel = (SecurityModel) document.getOldMaintainableObject().getBusinessObject(); 089 SecurityModel newSecurityModel = (SecurityModel) document.getNewMaintainableObject().getBusinessObject(); 090 091 boolean newMaintenanceAction = getMaintenanceAction().equalsIgnoreCase(KNSConstants.MAINTENANCE_NEW_ACTION) || getMaintenanceAction().equalsIgnoreCase(KNSConstants.MAINTENANCE_COPY_ACTION); 092 093 createOrUpdateModelRole(oldSecurityModel, newSecurityModel); 094 assignOrUpdateModelMembershipToDefinitionRoles(oldSecurityModel, newSecurityModel, newMaintenanceAction); 095 assignOrUpdateModelMembers(newSecurityModel); 096 097 if (!newSecurityModel.isActive()) { 098 inactivateModelRole(newSecurityModel); 099 } 100 101 SpringContext.getBean(IdentityManagementService.class).flushAllCaches(); 102 } 103 catch (WorkflowException e) { 104 LOG.error("caught exception while handling handleRouteStatusChange -> documentService.getByDocumentHeaderId(" + documentHeader.getDocumentNumber() + "). ", e); 105 throw new RuntimeException("caught exception while handling handleRouteStatusChange -> documentService.getByDocumentHeaderId(" + documentHeader.getDocumentNumber() + "). ", e); 106 } 107 } 108 } 109 110 /** 111 * Creates a new role for the model (if the model is new), otherwise updates the role 112 * 113 * @param oldSecurityModel SecurityModel record before updates 114 * @param newSecurityModel SecurityModel after updates 115 */ 116 protected void createOrUpdateModelRole(SecurityModel oldSecurityModel, SecurityModel newSecurityModel) { 117 RoleManagementService roleService = SpringContext.getBean(RoleManagementService.class); 118 119 String roleName = newSecurityModel.getName(); 120 121 String roleId = newSecurityModel.getRoleId(); 122 if (StringUtils.isBlank(roleId)) { 123 // get new id for role 124 roleId = roleService.getNextAvailableRoleId(); 125 newSecurityModel.setRoleId(roleId); 126 } 127 128 // always set the role as active so we can add members and definitions, after processing the indicator will be updated to 129 // the appropriate value 130 roleService.saveRole(roleId, roleName, newSecurityModel.getDescription(), true, SecConstants.SecurityTypes.DEFAULT_ROLE_TYPE, SecConstants.ACCESS_SECURITY_NAMESPACE_CODE); 131 roleService.flushRoleCaches(); 132 } 133 134 /** 135 * Saves the given security model setting the active indicator to false 136 * 137 * @param newSecurityModel SecurityModel to inactivate 138 */ 139 protected void inactivateModelRole(SecurityModel newSecurityModel) { 140 RoleManagementService roleService = SpringContext.getBean(RoleManagementService.class); 141 142 KimRoleInfo roleInfo = roleService.getRole(newSecurityModel.getRoleId()); 143 144 roleService.saveRole(roleInfo.getRoleId(), roleInfo.getRoleName(), newSecurityModel.getDescription(), false, SecConstants.SecurityTypes.DEFAULT_ROLE_TYPE, SecConstants.ACCESS_SECURITY_NAMESPACE_CODE); 145 } 146 147 /** 148 * Iterates through the model definition list and assigns the model role to the definition role if necessary or updates the 149 * current member assignment 150 * 151 * @param oldSecurityModel SecurityModel record before updates 152 * @param newSecurityModel SecurityModel whose membership should be updated 153 * @param newMaintenanceAction boolean indicating whether this is a new record (old side will not contain data) 154 */ 155 protected void assignOrUpdateModelMembershipToDefinitionRoles(SecurityModel oldSecurityModel, SecurityModel newSecurityModel, boolean newMaintenanceAction) { 156 RoleManagementService roleService = SpringContext.getBean(RoleManagementService.class); 157 158 KimRoleInfo modelRoleInfo = roleService.getRole(newSecurityModel.getRoleId()); 159 160 for (SecurityModelDefinition securityModelDefinition : newSecurityModel.getModelDefinitions()) { 161 SecurityDefinition securityDefinition = securityModelDefinition.getSecurityDefinition(); 162 163 KimRoleInfo definitionRoleInfo = roleService.getRole(securityDefinition.getRoleId()); 164 165 RoleMembershipInfo modelMembershipInfo = null; 166 if (!newMaintenanceAction) { 167 SecurityModelDefinition oldSecurityModelDefinition = null; 168 for (SecurityModelDefinition modelDefinition : oldSecurityModel.getModelDefinitions()) { 169 if (modelDefinition.getModelDefinitionId() != null && securityModelDefinition.getModelDefinitionId() != null && modelDefinition.getModelDefinitionId().equals(securityModelDefinition.getModelDefinitionId())) { 170 oldSecurityModelDefinition = modelDefinition; 171 } 172 } 173 174 if (oldSecurityModelDefinition != null) { 175 AttributeSet membershipQualifications = new AttributeSet(); 176 membershipQualifications.put(SecKimAttributes.CONSTRAINT_CODE, oldSecurityModelDefinition.getConstraintCode()); 177 membershipQualifications.put(SecKimAttributes.OPERATOR, oldSecurityModelDefinition.getOperatorCode()); 178 membershipQualifications.put(SecKimAttributes.PROPERTY_VALUE, oldSecurityModelDefinition.getAttributeValue()); 179 membershipQualifications.put(SecKimAttributes.OVERRIDE_DENY, Boolean.toString(oldSecurityModelDefinition.isOverrideDeny())); 180 181 if (modelRoleInfo == null || definitionRoleInfo == null) { 182 // this should throw an elegant error if either are null 183 String error = "Apparent data problem with access security. model or definition is null. this should not happen"; 184 LOG.error(error); 185 throw new RuntimeException(error); 186 } else { 187 modelMembershipInfo = KimUtil.getRoleMembershipInfoForMemberType(definitionRoleInfo.getRoleId(), modelRoleInfo.getRoleId(), KimConstants.KimUIConstants.MEMBER_TYPE_ROLE_CODE, membershipQualifications); 188 } 189 } 190 } 191 192 // only create membership if model is active and the model definition record is active 193 boolean membershipActive = newSecurityModel.isActive() && securityModelDefinition.isActive(); 194 195 // if membership already exists, need to remove if the model definition record is now inactive or the qualifications 196 // need updated 197 String modelMembershipId = ""; 198 if (modelMembershipInfo != null) { 199 modelMembershipId = modelMembershipInfo.getRoleMemberId(); 200 if (!membershipActive) { 201 roleService.removeRoleFromRole(modelMembershipInfo.getMemberId(), definitionRoleInfo.getNamespaceCode(), definitionRoleInfo.getRoleName(), modelMembershipInfo.getQualifier()); 202 } 203 } 204 205 // create of update role if membership should be active 206 if (membershipActive) { 207 AttributeSet membershipQualifications = new AttributeSet(); 208 membershipQualifications.put(SecKimAttributes.CONSTRAINT_CODE, securityModelDefinition.getConstraintCode()); 209 membershipQualifications.put(SecKimAttributes.OPERATOR, securityModelDefinition.getOperatorCode()); 210 membershipQualifications.put(SecKimAttributes.PROPERTY_VALUE, securityModelDefinition.getAttributeValue()); 211 membershipQualifications.put(SecKimAttributes.OVERRIDE_DENY, Boolean.toString(securityModelDefinition.isOverrideDeny())); 212 213 if (modelRoleInfo == null || definitionRoleInfo == null) { 214 // this should throw an elegant error if either are null 215 String error = "Apparent data problem with access security. model or definition is null. this should not happen"; 216 LOG.error(error); 217 throw new RuntimeException(error); 218 } else { 219 roleService.saveRoleMemberForRole(modelMembershipId, modelRoleInfo.getRoleId(), KimConstants.KimUIConstants.MEMBER_TYPE_ROLE_CODE, definitionRoleInfo.getRoleId(), membershipQualifications, null, null); 220 } 221 } 222 } 223 } 224 225 /** 226 * Iterates through the model member list and assign members to the model role or updates the membership 227 * 228 * @param securityModel SecurityModel whose member list should be updated 229 */ 230 protected void assignOrUpdateModelMembers(SecurityModel securityModel) { 231 RoleManagementService roleService = SpringContext.getBean(RoleManagementService.class); 232 233 KimRoleInfo modelRoleInfo = roleService.getRole(securityModel.getRoleId()); 234 235 if (modelRoleInfo == null) { 236 // this should throw an elegant error if either are null 237 String error = "Apparent data problem with access security. model is null. this should not happen"; 238 LOG.error(error); 239 throw new RuntimeException(error); 240 } else { 241 242 for (SecurityModelMember modelMember : securityModel.getModelMembers()) { 243 RoleMembershipInfo membershipInfo = KimUtil.getRoleMembershipInfoForMemberType(modelRoleInfo.getRoleId(), modelMember.getMemberId(), modelMember.getMemberTypeCode(), null); 244 245 String membershipId = ""; 246 if (membershipInfo != null) { 247 membershipId = membershipInfo.getRoleMemberId(); 248 } 249 250 java.sql.Date fromDate = null; 251 java.sql.Date toDate = null; 252 if ( modelMember.getActiveFromDate() != null ) { 253 fromDate = new java.sql.Date( modelMember.getActiveFromDate().getTime() ); 254 } 255 if ( modelMember.getActiveToDate() != null ) { 256 toDate = new java.sql.Date( modelMember.getActiveToDate().getTime() ); 257 } 258 roleService.saveRoleMemberForRole(membershipId, modelMember.getMemberId(), modelMember.getMemberTypeCode(), modelRoleInfo.getRoleId(), new AttributeSet(), fromDate, toDate); 259 260 createPrincipalSecurityRecords(modelMember.getMemberId(), modelMember.getMemberTypeCode()); 261 } 262 } 263 } 264 265 /** 266 * Creates security principal records for model members (if necessary) so that they will appear on security principal lookup for 267 * editing 268 * 269 * @param memberId String member id of model role 270 * @param memberTypeCode String member type code for member 271 */ 272 protected void createPrincipalSecurityRecords(String memberId, String memberTypeCode) { 273 Collection<String> principalIds = new HashSet<String>(); 274 275 if (KimConstants.KimUIConstants.MEMBER_TYPE_PRINCIPAL_CODE.equals(memberTypeCode)) { 276 principalIds.add(memberId); 277 } 278 else if (KimConstants.KimUIConstants.MEMBER_TYPE_ROLE_CODE.equals(memberTypeCode)) { 279 KimRoleInfo roleInfo = SpringContext.getBean(RoleManagementService.class).getRole(memberId); 280 Collection<String> rolePrincipalIds = SpringContext.getBean(RoleManagementService.class).getRoleMemberPrincipalIds(roleInfo.getNamespaceCode(), roleInfo.getRoleName(), new AttributeSet()); 281 principalIds.addAll(rolePrincipalIds); 282 } 283 else if (KimConstants.KimUIConstants.MEMBER_TYPE_GROUP_CODE.equals(memberTypeCode)) { 284 List<String> groupPrincipalIds = SpringContext.getBean(GroupService.class).getMemberPrincipalIds(memberId); 285 principalIds.addAll(groupPrincipalIds); 286 } 287 288 BusinessObjectService businessObjectService = SpringContext.getBean(BusinessObjectService.class); 289 for (String principalId : principalIds) { 290 SecurityPrincipal securityPrincipal = businessObjectService.findBySinglePrimaryKey(SecurityPrincipal.class, principalId); 291 if (securityPrincipal == null) { 292 SecurityPrincipal newSecurityPrincipal = new SecurityPrincipal(); 293 newSecurityPrincipal.setPrincipalId(principalId); 294 295 businessObjectService.save(newSecurityPrincipal); 296 } 297 } 298 } 299 300 /** 301 * Determines whether the given definition is part of the SecurityModel associated definitions 302 * 303 * @param definitionName name of definition to look for 304 * @param securityModel SecurityModel to check 305 * @return boolean true if the definition is in the security model, false if not 306 */ 307 protected boolean isDefinitionInModel(String definitionName, SecurityModel securityModel) { 308 for (SecurityModelDefinition securityModelDefinition : securityModel.getModelDefinitions()) { 309 if (StringUtils.equalsIgnoreCase(definitionName, securityModelDefinition.getSecurityDefinition().getName())) { 310 return true; 311 } 312 } 313 314 return false; 315 } 316 317 /** 318 * Override to clear out KIM role id on copy 319 * 320 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterCopy(org.kuali.rice.kns.document.MaintenanceDocument, 321 * java.util.Map) 322 */ 323 @Override 324 public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> parameters) { 325 SecurityModel securityModel = (SecurityModel) document.getNewMaintainableObject().getBusinessObject(); 326 securityModel.setRoleId(""); 327 328 super.processAfterCopy(document, parameters); 329 } 330 331 332 }