001/*
002 * (C) Copyright 2013 Nuxeo SA (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.Serializable;
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Locale;
025
026import org.apache.commons.lang.StringUtils;
027import org.apache.commons.logging.Log;
028import org.apache.commons.logging.LogFactory;
029import org.nuxeo.common.utils.i18n.I18NUtils;
030import org.nuxeo.ecm.automation.OperationContext;
031import org.nuxeo.ecm.automation.core.Constants;
032import org.nuxeo.ecm.automation.core.annotations.Context;
033import org.nuxeo.ecm.automation.core.annotations.Operation;
034import org.nuxeo.ecm.automation.core.annotations.OperationMethod;
035import org.nuxeo.ecm.automation.core.annotations.Param;
036import org.nuxeo.ecm.automation.features.SuggestConstants;
037import org.nuxeo.ecm.core.api.Blob;
038import org.nuxeo.ecm.core.api.Blobs;
039import org.nuxeo.ecm.core.api.DocumentModel;
040import org.nuxeo.ecm.core.api.DocumentModelList;
041import org.nuxeo.ecm.core.api.NuxeoGroup;
042import org.nuxeo.ecm.core.api.NuxeoPrincipal;
043import org.nuxeo.ecm.core.schema.SchemaManager;
044import org.nuxeo.ecm.core.schema.types.Field;
045import org.nuxeo.ecm.core.schema.types.QName;
046import org.nuxeo.ecm.core.schema.types.Schema;
047import org.nuxeo.ecm.directory.Directory;
048import org.nuxeo.ecm.directory.SizeLimitExceededException;
049import org.nuxeo.ecm.directory.api.DirectoryService;
050import org.nuxeo.ecm.platform.usermanager.UserAdapter;
051import org.nuxeo.ecm.platform.usermanager.UserManager;
052
053import net.sf.json.JSONArray;
054import net.sf.json.JSONObject;
055
056/**
057 * SuggestUser Operation.
058 *
059 * @since 5.7.3
060 */
061@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)
062public class SuggestUserEntries {
063
064    @SuppressWarnings("unused")
065    private static final Log log = LogFactory.getLog(SuggestUserEntries.class);
066
067    public static final String ID = "UserGroup.Suggestion";
068
069    public static final String POWERUSERS = "powerusers";
070
071    @Context
072    protected OperationContext ctx;
073
074    @Context
075    protected SchemaManager schemaManager;
076
077    @Param(name = "searchTerm", alias = "prefix", required = false)
078    protected String prefix;
079
080    @Param(name = "searchType", required = false)
081    protected String searchType;
082
083    @Param(name = "groupRestriction", required = false, description = "Enter the id of a group to suggest only user from this group.")
084    protected String groupRestriction;
085
086    /**
087     * @since 7.10
088     */
089    @Param(name = "hideAdminGroups", required = false, description = "If set, remove all administrator groups from the suggestions")
090    protected boolean hideAdminGroups;
091
092    /**
093     * @since 8.3
094     */
095    @Param(name = "hidePowerUsersGroup", required = false, description = "If set, remove power users group from the suggestions")
096    protected boolean hidePowerUsersGroup;
097
098    @Param(name = "userSuggestionMaxSearchResults", required = false)
099    protected Integer userSuggestionMaxSearchResults;
100
101    @Param(name = "firstLabelField", required = false)
102    protected String firstLabelField;
103
104    @Param(name = "secondLabelField", required = false)
105    protected String secondLabelField;
106
107    @Param(name = "thirdLabelField", required = false)
108    protected String thirdLabelField;
109
110    @Param(name = "hideFirstLabel", required = false)
111    protected boolean hideFirstLabel = false;
112
113    @Param(name = "hideSecondLabel", required = false)
114    protected boolean hideSecondLabel = false;
115
116    @Param(name = "hideThirdLabel", required = false)
117    protected boolean hideThirdLabel;
118
119    @Param(name = "displayEmailInSuggestion", required = false)
120    protected boolean displayEmailInSuggestion;
121
122    @Param(name = "hideIcon", required = false)
123    protected boolean hideIcon;
124
125    @Context
126    protected UserManager userManager;
127
128    @Context
129    protected DirectoryService directoryService;
130
131    @Param(name = "lang", required = false)
132    protected String lang;
133
134    @OperationMethod
135    public Blob run() {
136        JSONArray result = new JSONArray();
137        boolean isGroupRestriction = !StringUtils.isBlank(groupRestriction);
138        boolean groupOnly = false;
139        boolean userOnly = isGroupRestriction;
140
141        if (!isGroupRestriction && searchType != null && !searchType.isEmpty()) {
142            if (searchType.equals(SuggestConstants.USER_TYPE)) {
143                userOnly = true;
144            } else if (searchType.equals(SuggestConstants.GROUP_TYPE)) {
145                groupOnly = true;
146            }
147        }
148        try {
149            DocumentModelList userList = null;
150            DocumentModelList groupList = null;
151            if (!groupOnly) {
152                Schema schema = schemaManager.getSchema(userManager.getUserSchemaName());
153                userList = userManager.searchUsers(prefix);
154                Directory userDir = directoryService.getDirectory(userManager.getUserDirectoryName());
155                for (DocumentModel user : userList) {
156                    JSONObject obj = new JSONObject();
157                    for (Field field : schema.getFields()) {
158                        QName fieldName = field.getName();
159                        String key = fieldName.getLocalName();
160                        Serializable value = user.getPropertyValue(fieldName.getPrefixedName());
161                        if (key.equals(userDir.getPasswordField())) {
162                            continue;
163                        }
164                        obj.element(key, value);
165                    }
166                    String userId = user.getId();
167                    obj.put(SuggestConstants.ID, userId);
168                    obj.put(SuggestConstants.TYPE_KEY_NAME, SuggestConstants.USER_TYPE);
169                    obj.put(SuggestConstants.PREFIXED_ID_KEY_NAME, NuxeoPrincipal.PREFIX + userId);
170                    SuggestConstants.computeUserLabel(obj, firstLabelField, secondLabelField, thirdLabelField,
171                            hideFirstLabel, hideSecondLabel, hideThirdLabel, displayEmailInSuggestion, userId);
172                    SuggestConstants.computeUserGroupIcon(obj, hideIcon);
173                    if (isGroupRestriction) {
174                        // We need to load all data about the user particularly
175                        // its
176                        // groups.
177                        user = userManager.getUserModel(userId);
178                        UserAdapter userAdapter = user.getAdapter(UserAdapter.class);
179                        List<String> groups = userAdapter.getGroups();
180                        if (groups != null && groups.contains(groupRestriction)) {
181                            result.add(obj);
182                        }
183                    } else {
184                        result.add(obj);
185                    }
186                }
187            }
188            if (!userOnly) {
189                Schema schema = schemaManager.getSchema(userManager.getGroupSchemaName());
190                groupList = userManager.searchGroups(prefix);
191                List<String> admins = new ArrayList<>();
192                if (hideAdminGroups) {
193                    admins = userManager.getAdministratorsGroups();
194                }
195                groupLoop:
196                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                    JSONObject obj = new JSONObject();
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.element(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.createBlob(result.toString(), "application/json");
243    }
244
245    /**
246     * @return searchOverflowMessage
247     * @since 5.7.3
248     */
249    private Blob searchOverflowMessage() {
250        JSONArray result = new JSONArray();
251        JSONObject obj = new JSONObject();
252        obj.put(SuggestConstants.LABEL,
253                I18NUtils.getMessageString("messages", "label.security.searchOverFlow", new Object[0], getLocale()));
254        result.add(obj);
255        return Blobs.createBlob(result.toString(), "application/json");
256    }
257
258    protected String getLang() {
259        if (lang == null) {
260            lang = (String) ctx.get("lang");
261            if (lang == null) {
262                lang = SuggestConstants.DEFAULT_LANG;
263            }
264        }
265        return lang;
266    }
267
268    protected Locale getLocale() {
269        return new Locale(getLang());
270    }
271
272}