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