001/*
002 * Copyright 2009 The Kuali Foundation.
003 * 
004 * Licensed under the Educational Community License, Version 1.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/ecl1.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.ole.sec.document;
017
018import java.util.HashMap;
019
020import org.kuali.ole.sec.businessobject.SecurityDefinition;
021import org.kuali.ole.sec.businessobject.SecurityModelMember;
022import org.kuali.ole.sec.businessobject.SecurityPrincipal;
023import org.kuali.ole.sec.businessobject.SecurityPrincipalDefinition;
024import org.kuali.ole.sys.context.SpringContext;
025import org.kuali.rice.core.api.membership.MemberType;
026import org.kuali.rice.kew.api.exception.WorkflowException;
027import org.kuali.rice.kim.api.role.Role;
028import org.kuali.rice.kim.api.role.RoleMember;
029import org.kuali.rice.kim.api.role.RoleService;
030import org.kuali.rice.kim.api.services.KimApiServiceLocator;
031import org.kuali.rice.kns.document.MaintenanceDocument;
032import org.kuali.rice.krad.bo.DocumentHeader;
033import org.kuali.rice.krad.service.DocumentService;
034import org.kuali.rice.krad.util.KRADConstants;
035
036
037/**
038 * Maintainable implementation for the Security Principal maintenance document. Hooks into Post processing to create the KIM permissions for the principal and assign security role
039 * members
040 */
041public class SecurityPrincipalMaintainableImpl extends AbstractSecurityModuleMaintainable {
042    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SecurityPrincipalMaintainableImpl.class);
043
044    /**
045     * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#doRouteStatusChange(org.kuali.rice.krad.bo.DocumentHeader)
046     */
047    @Override
048    public void doRouteStatusChange(DocumentHeader documentHeader) {
049        super.doRouteStatusChange(documentHeader);
050
051        if (documentHeader.getWorkflowDocument().isProcessed()) {
052            DocumentService documentService = SpringContext.getBean(DocumentService.class);
053            try {
054                MaintenanceDocument document = (MaintenanceDocument) documentService.getByDocumentHeaderId(documentHeader.getDocumentNumber());
055                SecurityPrincipal oldSecurityPrincipal = (SecurityPrincipal) document.getOldMaintainableObject().getBusinessObject();
056                SecurityPrincipal newSecurityPrincipal = (SecurityPrincipal) document.getNewMaintainableObject().getBusinessObject();
057
058                boolean newMaintenanceAction = getMaintenanceAction().equalsIgnoreCase(KRADConstants.MAINTENANCE_NEW_ACTION) || getMaintenanceAction().equalsIgnoreCase(KRADConstants.MAINTENANCE_COPY_ACTION);
059
060                assignOrUpdatePrincipalMembershipToDefinitionRoles(oldSecurityPrincipal, newSecurityPrincipal, newMaintenanceAction);
061                assignOrUpdatePrincipalModelRoles(newSecurityPrincipal);
062            }
063            catch (WorkflowException e) {
064                LOG.error("caught exception while handling handleRouteStatusChange -> documentService.getByDocumentHeaderId(" + documentHeader.getDocumentNumber() + "). ", e);
065                throw new RuntimeException("caught exception while handling handleRouteStatusChange -> documentService.getByDocumentHeaderId(" + documentHeader.getDocumentNumber() + "). ", e);
066            }
067        }
068    }
069
070    /**
071     * Iterates through the principal definition list and assigns the principal to the definition role if necessary or updates the current member assignment
072     * 
073     * @param oldSecurityPrincipal SecurityPrincipal before updates
074     * @param newSecurityPrincipal SecurityPrincipal which contains the definition list and principal
075     * @param newMaintenanceAction boolean indicating whether this is a new record (old side will not contain data)
076     */
077    protected void assignOrUpdatePrincipalMembershipToDefinitionRoles(SecurityPrincipal oldSecurityPrincipal, SecurityPrincipal newSecurityPrincipal, boolean newMaintenanceAction) {
078        RoleService roleService = KimApiServiceLocator.getRoleService();
079
080        String principalId = newSecurityPrincipal.getPrincipalId();
081
082        for (SecurityPrincipalDefinition securityPrincipalDefinition : newSecurityPrincipal.getPrincipalDefinitions()) {
083            SecurityDefinition securityDefinition = securityPrincipalDefinition.getSecurityDefinition();
084
085            Role definitionRoleInfo = roleService.getRole(securityDefinition.getRoleId());
086            
087            RoleMember principalMembershipInfo = null;
088            if (!newMaintenanceAction) {
089                SecurityPrincipalDefinition oldPrincipalDefinition = null;
090                for (SecurityPrincipalDefinition principalDefinition : oldSecurityPrincipal.getPrincipalDefinitions()) {
091                   if ((principalDefinition.getPrincipalDefinitionId() != null) && principalDefinition.getPrincipalDefinitionId().equals(securityPrincipalDefinition.getPrincipalDefinitionId())) {
092                       oldPrincipalDefinition = principalDefinition;
093                   }
094                }
095                
096                if (oldPrincipalDefinition != null) {
097                    principalMembershipInfo = getRoleMembershipForMemberType(definitionRoleInfo.getId(), principalId, MemberType.PRINCIPAL.getCode(), getRoleQualifiersFromSecurityModelDefinition(oldPrincipalDefinition));
098                }
099            }
100
101            // only create membership if principal definition record is active
102            boolean membershipActive = securityPrincipalDefinition.isActive();
103
104            // if membership already exists, need to remove if the principal record is now inactive or the qualifications need updated
105            if (principalMembershipInfo != null) {
106                boolean qualificationsMatch = doMembershipQualificationsMatchValues(principalMembershipInfo.getAttributes(), securityPrincipalDefinition.getConstraintCode(), securityPrincipalDefinition.getOperatorCode(), securityPrincipalDefinition.getAttributeValue());
107                if (!membershipActive || !qualificationsMatch) {
108                    roleService.removePrincipalFromRole(principalMembershipInfo.getMemberId(), definitionRoleInfo.getNamespaceCode(), definitionRoleInfo.getName(), principalMembershipInfo.getAttributes());
109                }
110            }
111
112            // create of update role if membership should be active
113            if (membershipActive) {
114                if ( principalMembershipInfo == null ) {
115                    principalMembershipInfo = roleService.assignPrincipalToRole( principalId, definitionRoleInfo.getNamespaceCode(), definitionRoleInfo.getName(), getRoleQualifiersFromSecurityModelDefinition(securityPrincipalDefinition));
116                } else {
117                    RoleMember.Builder updatedRoleMember = RoleMember.Builder.create(principalMembershipInfo);
118                    updatedRoleMember.setAttributes(getRoleQualifiersFromSecurityModelDefinition(securityPrincipalDefinition));
119                    updatedRoleMember.setMemberId(principalId);
120                    roleService.updateRoleMember(updatedRoleMember.build());
121                }
122            }
123        }
124    }
125
126    /**
127     * Iterates through the principal model list and assigns the principal to the model role or updates the membership
128     * 
129     * @param securityPrincipal SecurityPrincipal which contains the model list and principal
130     */
131    protected void assignOrUpdatePrincipalModelRoles(SecurityPrincipal securityPrincipal) {
132        RoleService roleService = KimApiServiceLocator.getRoleService();
133        String principalId = securityPrincipal.getPrincipalId();
134
135        for (SecurityModelMember principalModel : securityPrincipal.getPrincipalModels()) {
136            Role modelRole = roleService.getRole(principalModel.getSecurityModel().getRoleId());
137            updateSecurityModelRoleMember(modelRole, principalModel, MemberType.PRINCIPAL.getCode(), principalId, new HashMap<String, String>(0));
138        }
139    }
140
141}