001/* 002 * (C) Copyright 2013-2018 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0 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 * Contributors: 017 * <a href="mailto:grenard@nuxeo.com">Guillaume</a> 018 */ 019package org.nuxeo.ecm.automation.core.operations.users; 020 021import java.io.IOException; 022import java.io.Serializable; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.LinkedHashMap; 026import java.util.List; 027import java.util.Locale; 028import java.util.Map; 029 030import org.apache.commons.lang3.StringUtils; 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.nuxeo.common.utils.i18n.I18NUtils; 034import org.nuxeo.ecm.automation.OperationContext; 035import org.nuxeo.ecm.automation.core.Constants; 036import org.nuxeo.ecm.automation.core.annotations.Context; 037import org.nuxeo.ecm.automation.core.annotations.Operation; 038import org.nuxeo.ecm.automation.core.annotations.OperationMethod; 039import org.nuxeo.ecm.automation.core.annotations.Param; 040import org.nuxeo.ecm.automation.features.SuggestConstants; 041import org.nuxeo.ecm.core.api.Blob; 042import org.nuxeo.ecm.core.api.Blobs; 043import org.nuxeo.ecm.core.api.DocumentModel; 044import org.nuxeo.ecm.core.api.DocumentModelList; 045import org.nuxeo.ecm.core.api.NuxeoGroup; 046import org.nuxeo.ecm.core.api.NuxeoPrincipal; 047import org.nuxeo.ecm.core.schema.SchemaManager; 048import org.nuxeo.ecm.core.schema.types.Field; 049import org.nuxeo.ecm.core.schema.types.QName; 050import org.nuxeo.ecm.core.schema.types.Schema; 051import org.nuxeo.ecm.directory.Directory; 052import org.nuxeo.ecm.directory.SizeLimitExceededException; 053import org.nuxeo.ecm.directory.api.DirectoryService; 054import org.nuxeo.ecm.platform.usermanager.UserAdapter; 055import org.nuxeo.ecm.platform.usermanager.UserManager; 056 057/** 058 * SuggestUser Operation. 059 * 060 * @since 5.7.3 061 */ 062@Operation(id = SuggestUserEntries.ID, category = Constants.CAT_SERVICES, label = "Get user/group suggestion", description = "Get the user/group list of the running instance. This is returning a blob containing a serialized JSON array..", addToStudio = false) 063public class SuggestUserEntries { 064 065 @SuppressWarnings("unused") 066 private static final Log log = LogFactory.getLog(SuggestUserEntries.class); 067 068 public static final String ID = "UserGroup.Suggestion"; 069 070 public static final String POWERUSERS = "powerusers"; 071 072 @Context 073 protected OperationContext ctx; 074 075 @Context 076 protected SchemaManager schemaManager; 077 078 @Param(name = "searchTerm", alias = "prefix", required = false) 079 protected String prefix; 080 081 @Param(name = "searchType", required = false) 082 protected String searchType; 083 084 @Param(name = "groupRestriction", required = false, description = "Enter the id of a group to suggest only user from this group.") 085 protected String groupRestriction; 086 087 /** 088 * @since 7.10 089 */ 090 @Param(name = "hideAdminGroups", required = false, description = "If set, remove all administrator groups from the suggestions") 091 protected boolean hideAdminGroups; 092 093 /** 094 * @since 8.3 095 */ 096 @Param(name = "hidePowerUsersGroup", required = false, description = "If set, remove power users group from the suggestions") 097 protected boolean hidePowerUsersGroup; 098 099 @Param(name = "userSuggestionMaxSearchResults", required = false) 100 protected Integer userSuggestionMaxSearchResults; 101 102 @Param(name = "firstLabelField", required = false) 103 protected String firstLabelField; 104 105 @Param(name = "secondLabelField", required = false) 106 protected String secondLabelField; 107 108 @Param(name = "thirdLabelField", required = false) 109 protected String thirdLabelField; 110 111 @Param(name = "hideFirstLabel", required = false) 112 protected boolean hideFirstLabel = false; 113 114 @Param(name = "hideSecondLabel", required = false) 115 protected boolean hideSecondLabel = false; 116 117 @Param(name = "hideThirdLabel", required = false) 118 protected boolean hideThirdLabel; 119 120 @Param(name = "displayEmailInSuggestion", required = false) 121 protected boolean displayEmailInSuggestion; 122 123 @Param(name = "hideIcon", required = false) 124 protected boolean hideIcon; 125 126 @Context 127 protected UserManager userManager; 128 129 @Context 130 protected DirectoryService directoryService; 131 132 @Param(name = "lang", required = false) 133 protected String lang; 134 135 @OperationMethod 136 public Blob run() throws IOException { 137 List<Map<String, Object>> result = new ArrayList<>(); 138 boolean isGroupRestriction = !StringUtils.isBlank(groupRestriction); 139 boolean groupOnly = false; 140 boolean userOnly = isGroupRestriction; 141 142 if (!isGroupRestriction && searchType != null && !searchType.isEmpty()) { 143 if (searchType.equals(SuggestConstants.USER_TYPE)) { 144 userOnly = true; 145 } else if (searchType.equals(SuggestConstants.GROUP_TYPE)) { 146 groupOnly = true; 147 } 148 } 149 try { 150 DocumentModelList userList = null; 151 DocumentModelList groupList = null; 152 if (!groupOnly) { 153 Schema schema = schemaManager.getSchema(userManager.getUserSchemaName()); 154 userList = userManager.searchUsers(prefix); 155 Directory userDir = directoryService.getDirectory(userManager.getUserDirectoryName()); 156 for (DocumentModel user : userList) { 157 Map<String, Object> obj = new LinkedHashMap<>(); 158 for (Field field : schema.getFields()) { 159 QName fieldName = field.getName(); 160 String key = fieldName.getLocalName(); 161 Serializable value = user.getPropertyValue(fieldName.getPrefixedName()); 162 if (key.equals(userDir.getPasswordField())) { 163 continue; 164 } 165 obj.put(key, value); 166 } 167 String userId = user.getId(); 168 obj.put(SuggestConstants.ID, userId); 169 obj.put(SuggestConstants.TYPE_KEY_NAME, SuggestConstants.USER_TYPE); 170 obj.put(SuggestConstants.PREFIXED_ID_KEY_NAME, NuxeoPrincipal.PREFIX + userId); 171 SuggestConstants.computeUserLabel(obj, firstLabelField, secondLabelField, thirdLabelField, 172 hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, userId); 173 SuggestConstants.computeUserGroupIcon(obj, hideIcon); 174 if (isGroupRestriction) { 175 // We need to load all data about the user particularly 176 // its 177 // groups. 178 user = userManager.getUserModel(userId); 179 UserAdapter userAdapter = user.getAdapter(UserAdapter.class); 180 List<String> groups = userAdapter.getGroups(); 181 if (groups != null && groups.contains(groupRestriction)) { 182 result.add(obj); 183 } 184 } else { 185 result.add(obj); 186 } 187 } 188 } 189 if (!userOnly) { 190 Schema schema = schemaManager.getSchema(userManager.getGroupSchemaName()); 191 groupList = userManager.searchGroups(prefix); 192 List<String> admins = new ArrayList<>(); 193 if (hideAdminGroups) { 194 admins = userManager.getAdministratorsGroups(); 195 } 196 groupLoop: for (DocumentModel group : groupList) { 197 if (hideAdminGroups) { 198 for (String adminGroupName : admins) { 199 if (adminGroupName.equals(group.getId())) { 200 break groupLoop; 201 } 202 } 203 } 204 if (hidePowerUsersGroup) { 205 if (POWERUSERS.equals(group.getId())) { 206 break groupLoop; 207 } 208 } 209 Map<String, Object> obj = new LinkedHashMap<>(); 210 for (Field field : schema.getFields()) { 211 QName fieldName = field.getName(); 212 String key = fieldName.getLocalName(); 213 Serializable value = group.getPropertyValue(fieldName.getPrefixedName()); 214 obj.put(key, value); 215 } 216 String groupId = group.getId(); 217 obj.put(SuggestConstants.ID, groupId); 218 // If the group hasn't an label, let's put the groupid 219 SuggestConstants.computeGroupLabel(obj, groupId, userManager.getGroupLabelField(), hideFirstLabel); 220 obj.put(SuggestConstants.TYPE_KEY_NAME, SuggestConstants.GROUP_TYPE); 221 obj.put(SuggestConstants.PREFIXED_ID_KEY_NAME, NuxeoGroup.PREFIX + groupId); 222 SuggestConstants.computeUserGroupIcon(obj, hideIcon); 223 result.add(obj); 224 } 225 } 226 227 // Limit size results. 228 int userSize = userList != null ? userList.size() : 0; 229 int groupSize = groupList != null ? groupList.size() : 0; 230 int totalSize = userSize + groupSize; 231 if (userSuggestionMaxSearchResults != null && userSuggestionMaxSearchResults > 0) { 232 if (userSize > userSuggestionMaxSearchResults || groupSize > userSuggestionMaxSearchResults 233 || totalSize > userSuggestionMaxSearchResults) { 234 throw new SizeLimitExceededException(); 235 } 236 } 237 238 } catch (SizeLimitExceededException e) { 239 return searchOverflowMessage(); 240 } 241 242 return Blobs.createJSONBlobFromValue(result); 243 } 244 245 /** 246 * @return searchOverflowMessage 247 * @since 5.7.3 248 */ 249 private Blob searchOverflowMessage() throws IOException { 250 String label = I18NUtils.getMessageString("messages", "label.security.searchOverFlow", new Object[0], 251 getLocale()); 252 Map<String, Object> obj = Collections.singletonMap(SuggestConstants.LABEL, label); 253 return Blobs.createJSONBlobFromValue(Collections.singletonList(obj)); 254 } 255 256 protected String getLang() { 257 if (lang == null) { 258 lang = (String) ctx.get("lang"); 259 if (lang == null) { 260 lang = SuggestConstants.DEFAULT_LANG; 261 } 262 } 263 return lang; 264 } 265 266 protected Locale getLocale() { 267 return new Locale(getLang()); 268 } 269 270}