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.kim.impl.type;
017
018import org.apache.commons.lang.StringUtils;
019import org.apache.log4j.Logger;
020import org.kuali.rice.kew.engine.RouteContext;
021import org.kuali.rice.kim.api.KimConstants;
022import org.kuali.rice.kim.api.group.Group;
023import org.kuali.rice.kim.api.group.GroupService;
024import org.kuali.rice.kim.api.role.RoleMembership;
025import org.kuali.rice.kim.api.role.RoleService;
026import org.kuali.rice.kim.api.services.KimApiServiceLocator;
027import org.kuali.rice.kim.api.type.KimType;
028import org.kuali.rice.kim.api.type.KimTypeInfoService;
029import org.kuali.rice.kim.bo.ui.KimDocumentRoleMember;
030import org.kuali.rice.kim.bo.ui.PersonDocumentGroup;
031import org.kuali.rice.kim.bo.ui.PersonDocumentRole;
032import org.kuali.rice.kim.document.IdentityManagementGroupDocument;
033import org.kuali.rice.kim.document.IdentityManagementPersonDocument;
034import org.kuali.rice.kim.document.IdentityManagementRoleDocument;
035import org.kuali.rice.kim.framework.services.KimFrameworkServiceLocator;
036import org.kuali.rice.kim.framework.type.KimTypeService;
037import org.kuali.rice.kns.workflow.attribute.QualifierResolverBase;
038import org.kuali.rice.krad.document.Document;
039
040import java.util.ArrayList;
041import java.util.Collections;
042import java.util.HashMap;
043import java.util.List;
044import java.util.Map;
045
046/**
047 * This is a description of what this class does - kellerj don't forget to fill this in. 
048 * 
049 * @author Kuali Rice Team (rice.collab@kuali.org)
050 *
051 */
052public class KimTypeQualifierResolver extends QualifierResolverBase {
053        private static final Logger LOG = Logger.getLogger(KimTypeQualifierResolver.class);
054        
055        protected static final String GROUP_ROUTE_LEVEL = "GroupType";
056        protected static final String ROLE_ROUTE_LEVEL = "RoleType";
057
058        private static KimTypeInfoService kimTypeInfoService;
059        private static GroupService groupService;
060        private static RoleService roleService;
061        
062        protected static Map<String,KimTypeService> typeServices = new HashMap<String, KimTypeService>();
063        
064        /**
065         * This overridden method ...
066         * 
067         * @see org.kuali.rice.kew.role.QualifierResolver#resolve(org.kuali.rice.kew.engine.RouteContext)
068         */
069        public List<Map<String, String>> resolve(RouteContext context) {
070        String routeLevel = context.getNodeInstance().getName();
071        Document document = getDocument(context);
072        List<Map<String, String>> qualifiers = new ArrayList<Map<String, String>>();
073        String customDocTypeName = null;
074        
075        if ( document instanceof IdentityManagementGroupDocument ) {
076                customDocTypeName = handleGroupDocument(qualifiers, (IdentityManagementGroupDocument)document, routeLevel);
077        } else if ( document instanceof IdentityManagementRoleDocument ) {
078                customDocTypeName = handleRoleDocument(qualifiers, (IdentityManagementRoleDocument)document, routeLevel);
079        } else if ( document instanceof IdentityManagementPersonDocument ) {
080                customDocTypeName = handlePersonDocument(qualifiers, (IdentityManagementPersonDocument)document, routeLevel);
081        }
082        // add standard components
083        decorateWithCommonQualifiers(qualifiers, context, customDocTypeName);
084        // return the resulting list of Map<String, String>s
085                return qualifiers;
086        }
087
088        protected KimTypeService getTypeService( String typeId ) {
089        KimTypeService typeService = typeServices.get(typeId);
090        if ( typeService == null ) {                    
091                KimType typeInfo = getKimTypeInfoService().getKimType(typeId);
092                if ( typeInfo != null ) {
093                        typeService = KimFrameworkServiceLocator.getKimTypeService(typeInfo);
094                        typeServices.put(typeId, typeService);
095                } else {
096                        LOG.warn( "Unable to retrieve KIM Type Info object for id: " + typeId );
097                }
098        }
099        return typeService;
100        }
101
102    protected void putMatchingAttributesIntoQualifier( Map<String, String> qualifier, Map<String, String> itemAttributes, List<String> routingAttributes ) {
103                if ( routingAttributes != null && !routingAttributes.isEmpty() ) {
104                // pull the qualifiers off the document object (group or role member)
105                for ( String attribName : routingAttributes ) {
106                        qualifier.put( attribName, itemAttributes.get(attribName));
107                }
108                }
109        }
110        
111        protected String handleGroupDocument( List<Map<String, String>> qualifiers, IdentityManagementGroupDocument groupDoc, String routeLevel ) {
112        // get the appropriate type service for the group being edited
113        String typeId = groupDoc.getGroupTypeId();
114        qualifiers.add( getGroupQualifier(groupDoc.getGroupId(), typeId, groupDoc.getQualifiersAsAttributes(), routeLevel) );
115        String customDocTypeName = null;
116        KimTypeService typeService = getTypeService(typeId);
117        customDocTypeName = typeService.getWorkflowDocumentTypeName();
118        return customDocTypeName;
119        }
120
121        protected String handleRoleDocument( List<Map<String, String>> qualifiers, IdentityManagementRoleDocument roleDoc, String routeLevel ) {
122        String customDocTypeName = null;
123
124//        LOG.warn( "Role member data routing not implemented for the Role document yet!" );
125        // get the appropriate type service for the group being edited
126        String typeId = roleDoc.getRoleTypeId();
127        KimTypeService typeService = getTypeService(typeId);
128        if ( typeService != null ) {
129                // QUESTION: can roles be modified in a way which requires routing?
130                // get the existing role members
131                List<RoleMembership> currentRoleMembers = KimApiServiceLocator.getRoleService().getRoleMembers( Collections.singletonList( roleDoc.getRoleId() ),  Collections.<String, String>emptyMap() );
132                // loop over the role members on the document, check  if added or removed
133                for ( KimDocumentRoleMember rm : roleDoc.getMembers() ) {
134                        boolean foundMember = false;
135                        for ( RoleMembership rmi : currentRoleMembers ) {
136                                if ( rmi.getId().equals( rm.getRoleMemberId() ) ) {
137                                        foundMember = true;
138                                        if ( !rm.isActive() ) { // don't need to check the role member information 
139                                                                                        // - only active members are returned
140                                                // inactivated member, add a qualifier
141                                                qualifiers.add( getRoleQualifier(rm.getRoleId(), typeId, typeService, rm.getQualifierAsMap(), routeLevel) );
142                                        }
143                                        break;
144                                }
145                        }
146                        if ( !foundMember ) {
147                                qualifiers.add( getRoleQualifier(rm.getRoleId(), typeId, typeService, rm.getQualifierAsMap(), routeLevel) );
148                        }
149                }
150                
151                customDocTypeName = typeService.getWorkflowDocumentTypeName();
152        }               
153        return customDocTypeName;
154        }
155        
156        protected String handlePersonDocument( List<Map<String, String>> qualifiers, IdentityManagementPersonDocument personDoc, String routeLevel ) {
157        // check the route level - see if we are doing groups or roles at the moment
158        String principalId = personDoc.getPrincipalId();
159        if ( GROUP_ROUTE_LEVEL.equals(routeLevel) ) {
160                // if groups, find any groups to which the user was added or removed
161                // get the type and service for each group
162                // handle as per the group document, a qualifier for each group
163                List<String> currentGroups = getGroupService().getDirectGroupIdsByPrincipalId(principalId);
164                List<PersonDocumentGroup> groups = personDoc.getGroups();
165                for ( PersonDocumentGroup group : groups ) {
166                        // if they are being added to the group, add a qualifier set
167                        if ( group.isActive() && !currentGroups.contains( group.getGroupId() ) ) {
168                        // pull the group to get its attributes for adding to the qualifier 
169                        Group kimGroup = getGroupService().getGroup(group.getGroupId());
170                                qualifiers.add( getGroupQualifier( group.getGroupId(), kimGroup.getKimTypeId(), kimGroup.getAttributes(), routeLevel ) );
171                        }
172                }
173                // detect removed groups
174                // get the existing directly assigned groups for the person
175                for ( String groupId : currentGroups ) {
176                        for ( PersonDocumentGroup group : groups ) {
177                                if ( !group.isActive() ) {
178                                Group kimGroup = getGroupService().getGroup(groupId);
179                                qualifiers.add( getGroupQualifier( groupId, kimGroup.getKimTypeId(), kimGroup.getAttributes(), routeLevel ) );
180                                }
181                        }
182                }
183        } else if ( ROLE_ROUTE_LEVEL.equals(routeLevel) ) {
184//              getRoleService().get
185                for ( PersonDocumentRole pdr : personDoc.getRoles() ) {
186                KimTypeService typeService = getTypeService(pdr.getKimTypeId());
187                        for ( KimDocumentRoleMember rm : pdr.getRolePrncpls() ) {
188                                boolean foundMember = false;
189                        for ( RoleMembership rmi : getRoleService().getRoleMembers( Collections.singletonList( rm.getRoleId() ), Collections.<String, String>emptyMap() ) ) {
190                                if ( StringUtils.equals( rmi.getId(), rm.getRoleMemberId() ) ) {
191                                        foundMember = true;
192                                                if ( !rm.isActive() ) { // don't need to check the role member information 
193                                                                // - only active members are returned
194                                                        // inactivated member, add a qualifier
195                                                                qualifiers.add( getRoleQualifier(rm.getRoleId(), pdr.getKimRoleType().getId(), typeService, rm.getQualifierAsMap(), routeLevel) );
196                                                        }
197                                                        break;
198                                }
199                        }
200                                if ( !foundMember ) {
201                                        qualifiers.add( getRoleQualifier(rm.getRoleId(), pdr.getKimRoleType().getId(), typeService, rm.getQualifierAsMap(), routeLevel) );
202                                }
203                        }
204                }
205                // if roles, check the role member data for any roles added
206                // get the type and service for each role
207                // handle as for the role document, a qualifier for each role membership added
208//              LOG.warn( "Role-based data routing on the person document not implemented!" );
209        }
210        
211        return null;
212        }
213
214    protected Map<String, String> getGroupQualifier( String groupId, String kimTypeId, Map<String, String> groupAttributes, String routeLevel ) {
215                Map<String, String> qualifier = new HashMap<String, String>();
216                // pull the group to get its attributes for adding to the qualifier 
217        qualifier.put(KimConstants.PrimaryKeyConstants.KIM_TYPE_ID, kimTypeId);
218        qualifier.put(KimConstants.AttributeConstants.QUALIFIER_RESOLVER_PROVIDED_IDENTIFIER, kimTypeId);
219        qualifier.put(KimConstants.PrimaryKeyConstants.GROUP_ID, groupId);
220        KimTypeService typeService = getTypeService(kimTypeId);
221        if ( typeService != null ) {
222                // check for the custom document type for the group
223                String customDocTypeName = typeService.getWorkflowDocumentTypeName();
224                if ( StringUtils.isNotBlank(customDocTypeName)) {
225                        qualifier.put(KIM_ATTRIBUTE_DOCUMENT_TYPE_NAME, customDocTypeName );
226                }
227                putMatchingAttributesIntoQualifier(qualifier, groupAttributes, typeService.getWorkflowRoutingAttributes(routeLevel) );
228        }
229        return qualifier;
230    }
231    
232    protected Map<String, String> getRoleQualifier( String roleId, String kimTypeId, KimTypeService typeService, Map<String, String> roleAttributes, String routeLevel ) {
233                Map<String, String> qualifier = new HashMap<String, String>();
234                // pull the group to get its attributes for adding to the qualifier 
235        qualifier.put(KimConstants.PrimaryKeyConstants.KIM_TYPE_ID, kimTypeId);
236        qualifier.put(KimConstants.AttributeConstants.QUALIFIER_RESOLVER_PROVIDED_IDENTIFIER, kimTypeId);
237        qualifier.put("roleId", roleId);
238                // check for the custom document type for the group
239                String customDocTypeName = typeService.getWorkflowDocumentTypeName();
240                if ( StringUtils.isNotBlank(customDocTypeName)) {
241                        qualifier.put(KIM_ATTRIBUTE_DOCUMENT_TYPE_NAME, customDocTypeName );
242                }
243                putMatchingAttributesIntoQualifier(qualifier, roleAttributes, typeService.getWorkflowRoutingAttributes(routeLevel) );
244        return qualifier;
245    }
246        
247        public KimTypeInfoService getKimTypeInfoService() {
248                if ( kimTypeInfoService == null ) {
249                        kimTypeInfoService = KimApiServiceLocator.getKimTypeInfoService();
250                }
251                return kimTypeInfoService;
252        }
253
254        public static GroupService getGroupService() {
255                if ( groupService == null ) {
256                        groupService = KimApiServiceLocator.getGroupService();
257                }
258                return groupService;
259        }
260
261        public static RoleService getRoleService() {
262                if ( roleService == null ) {
263                        roleService = KimApiServiceLocator.getRoleService();
264                }
265                return roleService;
266        }
267}