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.lookup;
017
018import org.apache.commons.beanutils.PropertyUtils;
019import org.apache.commons.lang.StringUtils;
020import org.kuali.rice.core.api.membership.MemberType;
021import org.kuali.rice.kim.api.KimConstants;
022import org.kuali.rice.kim.api.role.RoleService;
023import org.kuali.rice.kim.api.services.KimApiServiceLocator;
024import org.kuali.rice.kim.impl.permission.GenericPermissionBo;
025import org.kuali.rice.kim.impl.permission.PermissionBo;
026import org.kuali.rice.kim.impl.permission.UberPermissionBo;
027import org.kuali.rice.kim.impl.role.RoleBo;
028import org.kuali.rice.kim.impl.role.RolePermissionBo;
029import org.kuali.rice.kns.lookup.HtmlData;
030import org.kuali.rice.krad.bo.BusinessObject;
031import org.kuali.rice.krad.lookup.CollectionIncomplete;
032import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
033import org.kuali.rice.krad.service.LookupService;
034import org.kuali.rice.krad.util.KRADConstants;
035import org.kuali.rice.krad.util.UrlFactory;
036
037import java.lang.reflect.InvocationTargetException;
038import java.util.ArrayList;
039import java.util.HashMap;
040import java.util.List;
041import java.util.Map;
042import java.util.Properties;
043
044public class PermissionLookupableHelperServiceImpl extends RoleMemberLookupableHelperServiceImpl {
045
046        private static final long serialVersionUID = -3578448525862270477L;
047
048        private transient LookupService lookupService;
049        private transient RoleService roleService;
050        private volatile String genericPermissionDocumentTypeName;
051
052        @Override
053        public List<HtmlData> getCustomActionUrls(BusinessObject businessObject, List pkNames) {
054        List<HtmlData> htmlDataList = new ArrayList<HtmlData>();
055        // convert the PermissionBo class into an UberPermission object
056        businessObject = new GenericPermissionBo((UberPermissionBo)businessObject);
057        if (allowsMaintenanceEditAction(businessObject)) {
058                htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_EDIT_METHOD_TO_CALL, pkNames));
059        }
060        if (allowsMaintenanceNewOrCopyAction()) {
061                htmlDataList.add(getUrlData(businessObject, KRADConstants.MAINTENANCE_COPY_METHOD_TO_CALL, pkNames));
062        }
063        return htmlDataList;
064        }
065
066        protected String getActionUrlHref(BusinessObject businessObject, String methodToCall, List pkNames){
067        Properties parameters = new Properties();
068        parameters.put(KRADConstants.DISPATCH_REQUEST_PARAMETER, methodToCall);
069        parameters.put(KRADConstants.BUSINESS_OBJECT_CLASS_ATTRIBUTE, businessObject.getClass().getName());
070        parameters.put(KRADConstants.OVERRIDE_KEYS, KimConstants.PrimaryKeyConstants.PERMISSION_ID);
071        parameters.put(KRADConstants.COPY_KEYS, KimConstants.PrimaryKeyConstants.PERMISSION_ID);
072        if (StringUtils.isNotBlank(getReturnLocation())) {
073                parameters.put(KRADConstants.RETURN_LOCATION_PARAMETER, getReturnLocation());
074                }
075        parameters.putAll(getParametersFromPrimaryKey(businessObject, pkNames));
076        return UrlFactory.parameterizeUrl(KRADConstants.MAINTENANCE_ACTION, parameters);
077    }
078        
079        @Override
080        protected String getMaintenanceDocumentTypeName() {
081                //using DCL idiom to cache genericPermissionDocumentTypeName.
082        //see effective java 2nd ed. pg. 71
083        String g = genericPermissionDocumentTypeName;
084        if (g == null) {
085            synchronized (this) {
086                g = genericPermissionDocumentTypeName;
087                if (g == null) {
088                    genericPermissionDocumentTypeName = g = getMaintenanceDocumentDictionaryService().getDocumentTypeName(
089                            GenericPermissionBo.class);
090                }
091            }
092        }
093
094        return g;
095        }
096                
097        @Override
098        protected List<? extends BusinessObject> getMemberSearchResults(Map<String, String> searchCriteria, boolean unbounded) {
099                Map<String, String> permissionSearchCriteria = buildSearchCriteria(searchCriteria);
100                Map<String, String> roleSearchCriteria = buildRoleSearchCriteria(searchCriteria);
101                boolean permissionCriteriaEmpty = permissionSearchCriteria==null || permissionSearchCriteria.isEmpty();
102                boolean roleCriteriaEmpty = roleSearchCriteria==null || roleSearchCriteria.isEmpty();
103                
104                List<UberPermissionBo> permissionSearchResultsCopy = new CollectionIncomplete<UberPermissionBo>(new ArrayList<UberPermissionBo>(), new Long(0));
105                if(!permissionCriteriaEmpty && !roleCriteriaEmpty){
106                        permissionSearchResultsCopy = getCombinedSearchResults(permissionSearchCriteria, roleSearchCriteria, unbounded);
107                } else if(permissionCriteriaEmpty && !roleCriteriaEmpty){
108                        permissionSearchResultsCopy = getPermissionsWithRoleSearchCriteria(roleSearchCriteria, unbounded);
109                } else if(!permissionCriteriaEmpty && roleCriteriaEmpty){
110                        permissionSearchResultsCopy = getPermissionsWithPermissionSearchCriteria(permissionSearchCriteria, unbounded);
111                } else if(permissionCriteriaEmpty && roleCriteriaEmpty){
112                        return getAllPermissions(unbounded);
113                }
114                return permissionSearchResultsCopy;
115        }
116        
117        private List<UberPermissionBo> getAllPermissions(boolean unbounded){
118                List<UberPermissionBo> permissions = searchPermissions(new HashMap<String, String>(), unbounded);
119                for(UberPermissionBo permission: permissions) {
120                        populateAssignedToRoles(permission);
121        }
122                return permissions;
123        }
124        
125        private List<UberPermissionBo> getCombinedSearchResults(
126                        Map<String, String> permissionSearchCriteria, Map<String, String> roleSearchCriteria, boolean unbounded){
127                List<UberPermissionBo> permissionSearchResults = searchPermissions(permissionSearchCriteria, unbounded);
128                List<RoleBo> roleSearchResults = searchRoles(roleSearchCriteria, unbounded);
129                List<UberPermissionBo> permissionsForRoleSearchResults = getPermissionsForRoleSearchResults(roleSearchResults, unbounded);
130                List<UberPermissionBo> matchedPermissions = new CollectionIncomplete<UberPermissionBo>(
131                        new ArrayList<UberPermissionBo>(), getActualSizeIfTruncated(permissionsForRoleSearchResults));
132                if((permissionSearchResults!=null && !permissionSearchResults.isEmpty()) && 
133                                (permissionsForRoleSearchResults!=null && !permissionsForRoleSearchResults.isEmpty())){
134                        for(UberPermissionBo permission: permissionSearchResults){
135                                for(UberPermissionBo permissionFromRoleSearch: permissionsForRoleSearchResults){
136                                        if(permissionFromRoleSearch.getId().equals(permission.getId())) {
137                                                matchedPermissions.add(permissionFromRoleSearch);
138                    }
139                                }
140                        }
141                }
142                return matchedPermissions;
143        }
144        
145        private List<UberPermissionBo> searchPermissions(Map<String, String> permissionSearchCriteria, boolean unbounded){
146                return getPermissionsSearchResultsCopy(new ArrayList<PermissionBo>(getLookupService().findCollectionBySearchHelper(
147                                PermissionBo.class, permissionSearchCriteria, unbounded)));
148
149        }
150        
151        private List<UberPermissionBo> getPermissionsWithRoleSearchCriteria(Map<String, String> roleSearchCriteria, boolean unbounded){
152                return getPermissionsForRoleSearchResults(searchRoles(roleSearchCriteria, unbounded), unbounded);
153        }
154
155        private List<UberPermissionBo> getPermissionsForRoleSearchResults(List<RoleBo> roleSearchResults, boolean unbounded){
156                Long actualSizeIfTruncated = getActualSizeIfTruncated(roleSearchResults);
157                List<UberPermissionBo> permissions = new ArrayList<UberPermissionBo>();
158                List<UberPermissionBo> tempPermissions;
159                List<String> collectedPermissionIds = new ArrayList<String>();
160                Map<String, String> permissionCriteria;
161                
162                for(RoleBo roleImpl: roleSearchResults){
163                        permissionCriteria = new HashMap<String, String>();
164                        permissionCriteria.put("rolePermissions.roleId", roleImpl.getId());
165                        tempPermissions = searchPermissions(permissionCriteria, unbounded);
166                        actualSizeIfTruncated += getActualSizeIfTruncated(tempPermissions);
167                        for(UberPermissionBo permission: tempPermissions){
168                                if(!collectedPermissionIds.contains(permission.getId())){
169                                        populateAssignedToRoles(permission);
170                                        collectedPermissionIds.add(permission.getId());
171                                        permissions.add(permission);
172                                }
173                        }
174                        //need to find roles that current role is a member of and build search string
175                        List<String> parentRoleIds = KimApiServiceLocator.getRoleService().getMemberParentRoleIds(MemberType.ROLE.getCode(), roleImpl.getId());
176                        for (String parentRoleId : parentRoleIds) {
177                                Map<String, String> roleSearchCriteria = new HashMap<String, String>();
178                                roleSearchCriteria.put("roleId", parentRoleId);
179                                //get all parent role permissions and merge them with current permissions
180                                permissions = mergePermissionLists(permissions, getPermissionsWithRoleSearchCriteria(roleSearchCriteria, unbounded));
181                        }
182                }
183                
184                return new CollectionIncomplete<UberPermissionBo>(permissions, actualSizeIfTruncated);
185        }
186        
187
188        private void populateAssignedToRoles(UberPermissionBo permission){
189                Map<String, String> criteria;
190                for(RolePermissionBo rolePermission: permission.getRolePermissions()){
191                        if ( rolePermission.isActive() ) {
192                                criteria = new HashMap<String, String>();
193                                criteria.put("id", rolePermission.getRoleId());
194        //                      permission.getAssignedToRoles().add((RoleBo)getBusinessObjectService().findByPrimaryKey(RoleBo.class, criteria));
195                RoleBo roleBo = getBusinessObjectService().findByPrimaryKey(RoleBo.class, criteria);
196                permission.getAssignedToRoles().add(roleBo);
197
198                        }
199                }
200        }
201
202        private List<UberPermissionBo> getPermissionsWithPermissionSearchCriteria(
203                        Map<String, String> permissionSearchCriteria, boolean unbounded){
204                String detailCriteriaStr = permissionSearchCriteria.remove( DETAIL_CRITERIA );
205                Map<String, String> detailCriteria = parseDetailCriteria(detailCriteriaStr);
206
207                final List<UberPermissionBo> permissions = searchPermissions(permissionSearchCriteria, unbounded);
208                List<UberPermissionBo> filteredPermissions = new CollectionIncomplete<UberPermissionBo>(
209                                new ArrayList<UberPermissionBo>(), getActualSizeIfTruncated(permissions));
210                for(UberPermissionBo perm: permissions){
211                        if ( detailCriteria.isEmpty() ) {
212                                filteredPermissions.add(perm);
213                                populateAssignedToRoles(perm);
214                        } else {
215                                if ( isMapSubset( new HashMap<String, String>(perm.getDetails()), detailCriteria ) ) {
216                                        filteredPermissions.add(perm);
217                                        populateAssignedToRoles(perm);
218                                }
219                        }
220                }
221                return filteredPermissions;
222        }
223        
224        private List<UberPermissionBo> getPermissionsSearchResultsCopy(List<PermissionBo> permissionSearchResults){
225                List<UberPermissionBo> permissionSearchResultsCopy = new CollectionIncomplete<UberPermissionBo>(
226                                new ArrayList<UberPermissionBo>(), getActualSizeIfTruncated(permissionSearchResults));
227                for(PermissionBo permissionBo: permissionSearchResults){
228                        UberPermissionBo permissionCopy = new UberPermissionBo();
229
230            try {
231                PropertyUtils.copyProperties(permissionCopy, permissionBo);
232                //Hack for tomcat 7 KULRICE-5927
233                permissionCopy.setTemplate(permissionBo.getTemplate());
234            } catch (IllegalAccessException e) {
235                throw new RuntimeException("unable to copy properties");
236            } catch (InvocationTargetException e) {
237                throw new RuntimeException("unable to copy properties");
238            } catch (NoSuchMethodException e) {
239                throw new RuntimeException("unable to copy properties");
240            }
241
242            permissionSearchResultsCopy.add(permissionCopy);
243                }
244                return permissionSearchResultsCopy;
245        }
246
247        /**
248         * @return the lookupService
249         */
250        public synchronized LookupService getLookupService() {
251                if ( lookupService == null ) {
252                        lookupService = KRADServiceLocatorWeb.getLookupService();
253                }
254                return lookupService;
255        }
256
257        public synchronized RoleService getRoleService() {
258                if (roleService == null) {
259                        roleService = KimApiServiceLocator.getRoleService();
260                }
261                return roleService;
262        }
263
264        private List<UberPermissionBo> mergePermissionLists(List<UberPermissionBo> perm1, List<UberPermissionBo> perm2) {
265                List<UberPermissionBo> returnList = new ArrayList<UberPermissionBo>(perm1);
266                List<String> permissionIds = new ArrayList<String>(perm1.size());
267                for (UberPermissionBo perm : returnList) {
268                        permissionIds.add(perm.getId());
269                }
270                for (int i=0; i<perm2.size(); i++) {
271                    if (!permissionIds.contains(perm2.get(i).getId())) {
272                        returnList.add(perm2.get(i));
273                    }
274                }
275                return returnList;
276        }
277}