001/*
002 * (C) Copyright 2016 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 *     Thomas Roger
018 *     Yannis JULIENNE
019 */
020package org.nuxeo.functionaltests;
021
022import static org.nuxeo.functionaltests.AbstractTest.NUXEO_URL;
023import static org.nuxeo.functionaltests.Constants.ADMINISTRATOR;
024
025import java.io.IOException;
026import java.util.ArrayList;
027import java.util.Arrays;
028import java.util.Calendar;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032
033import org.apache.commons.lang.StringUtils;
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036import org.codehaus.jackson.JsonNode;
037import org.codehaus.jackson.map.ObjectMapper;
038import org.nuxeo.client.api.NuxeoClient;
039import org.nuxeo.client.api.objects.Document;
040import org.nuxeo.client.api.objects.Documents;
041import org.nuxeo.client.api.objects.acl.ACE;
042import org.nuxeo.client.api.objects.user.Group;
043import org.nuxeo.client.internals.spi.NuxeoClientException;
044import org.nuxeo.common.utils.URIUtils;
045
046import okhttp3.Response;
047import okhttp3.ResponseBody;
048
049/**
050 * @since 8.3
051 */
052public class RestHelper {
053
054    private static final NuxeoClient CLIENT = new NuxeoClient(NUXEO_URL, ADMINISTRATOR, ADMINISTRATOR);
055
056    private static final String USER_WORKSPACE_PATH_FORMAT = "/default-domain/UserWorkspaces/%s";
057
058    private static final String DEFAULT_USER_EMAIL = "devnull@nuxeo.com";
059
060    private static final String DOCUMENT_QUERY_BY_PATH_BASE = "SELECT * FROM Document WHERE ecm:path = '%s'";
061
062    private static final List<String> documentIdsToDelete = new ArrayList<>();
063
064    private static final List<String> documentPathsToDelete = new ArrayList<>();
065
066    private static final List<String> usersToDelete = new ArrayList<>();
067
068    private static final List<String> groupsToDelete = new ArrayList<>();
069
070    private static final int NOT_FOUND_ERROR_STATUS = 404;
071
072    protected static final Log log = LogFactory.getLog(RestHelper.class);
073
074    // @yannis : temporary fix for setting user password before JAVACLIENT-91
075    private static final ObjectMapper MAPPER = new ObjectMapper();
076
077    private RestHelper() {
078        // helper class
079    }
080
081    public static void cleanup() {
082        cleanupDocuments();
083        cleanupUsers();
084        cleanupGroups();
085    }
086
087    public static void cleanupDocuments() {
088        documentIdsToDelete.forEach(RestHelper::deleteDocument);
089        documentIdsToDelete.clear();
090        documentPathsToDelete.clear();
091    }
092
093    public static void cleanupUsers() {
094        for (String user : usersToDelete) {
095            RestHelper.deleteDocument(String.format(USER_WORKSPACE_PATH_FORMAT, user));
096        }
097        usersToDelete.forEach(RestHelper::deleteUser);
098        usersToDelete.clear();
099    }
100
101    public static void cleanupGroups() {
102        groupsToDelete.forEach(RestHelper::deleteGroup);
103        groupsToDelete.clear();
104    }
105
106    public static String createUser(String username, String password) {
107        return createUser(username, password, null, null, null, null, null);
108    }
109
110    public static String createUser(String username, String password, String firstName, String lastName, String company,
111            String email, String group) {
112
113        String finalEmail = StringUtils.isBlank(email) ? DEFAULT_USER_EMAIL : email;
114
115        // @yannis : temporary fix for setting user password before JAVACLIENT-91
116        String json = buildUserJSON(username, password, firstName, lastName, company, finalEmail, group);
117
118        Response response = CLIENT.post(AbstractTest.NUXEO_URL + "/api/v1/user", json);
119        if (!response.isSuccessful()) {
120            throw new RuntimeException(String.format("Unable to create user '%s'", username));
121        }
122
123        try (ResponseBody responseBody = response.body()) {
124            JsonNode jsonNode = MAPPER.readTree(responseBody.charStream());
125            String id = jsonNode.get("id").getTextValue();
126            usersToDelete.add(id);
127            return id;
128        } catch (IOException e) {
129            throw new RuntimeException(e);
130        }
131    }
132
133    public static void addUserToDelete(String userName) {
134        usersToDelete.add(userName);
135    }
136
137    public static void removeUserToDelete(String userName) {
138        usersToDelete.remove(userName);
139    }
140
141    private static String buildUserJSON(String username, String password, String firstName, String lastName,
142            String company, String email, String group) {
143        StringBuilder sb = new StringBuilder();
144        sb.append("{");
145        sb.append("\"entity-type\": \"user\"").append(",\n");
146        sb.append("\"id\": \"").append(username).append("\",\n");
147        sb.append("\"properties\": {").append("\n");
148        if (firstName != null) {
149            sb.append("\"firstName\": \"").append(firstName).append("\",\n");
150        }
151        if (lastName != null) {
152            sb.append("\"lastName\": \"").append(lastName).append("\",\n");
153        }
154        if (email != null) {
155            sb.append("\"email\": \"").append(email).append("\",\n");
156        }
157        if (company != null) {
158            sb.append("\"company\": \"").append(company).append("\",\n");
159        }
160        if (group != null) {
161            sb.append("\"groups\": [\"").append(group).append("\"]").append(",\n");
162        }
163        sb.append("\"username\": \"").append(username).append("\",\n");
164        sb.append("\"password\": \"").append(password).append("\"\n");
165        sb.append("}").append("\n");
166        sb.append("}");
167        return sb.toString();
168    }
169
170    public static void deleteUser(String username) {
171        try {
172            CLIENT.getUserManager().deleteUser(username);
173        } catch (NuxeoClientException e) {
174            if (NOT_FOUND_ERROR_STATUS == e.getStatus()) {
175                log.warn(String.format("User %s not deleted because not found", username));
176            } else {
177                throw e;
178            }
179        }
180    }
181
182    public static void createGroup(String name, String label) {
183        createGroup(name, label, null, null);
184    }
185
186    public static void createGroup(String name, String label, String[] members, String[] subGroups) {
187        Group group = new Group();
188        group.setGroupName(name);
189        group.setGroupLabel(label);
190        if (members != null) {
191            group.setMemberUsers(Arrays.asList(members));
192        }
193        if (subGroups != null) {
194            group.setMemberGroups(Arrays.asList(subGroups));
195        }
196
197        CLIENT.getUserManager().createGroup(group);
198        groupsToDelete.add(name);
199    }
200
201    public static void deleteGroup(String name) {
202        try {
203            CLIENT.getUserManager().deleteGroup(name);
204        } catch (NuxeoClientException e) {
205            if (NOT_FOUND_ERROR_STATUS == e.getStatus()) {
206                log.warn(String.format("Group %s not deleted because not found", name));
207            } else {
208                throw e;
209            }
210        }
211    }
212
213    public static String createDocument(String idOrPath, String type, String title, String description) {
214        Document document = new Document(title, type);
215        Map<String, Object> properties = new HashMap<>();
216        properties.put("dc:title", title);
217        if (description != null) {
218            properties.put("dc:description", description);
219        }
220        document.setProperties(properties);
221
222        if (idOrPath.startsWith("/")) {
223            document = CLIENT.repository().createDocumentByPath(idOrPath, document);
224        } else {
225            document = CLIENT.repository().createDocumentById(idOrPath, document);
226        }
227
228        String docId = document.getId();
229        String docPath = document.getPath();
230        // do we already have to delete one parent?
231        if (documentPathsToDelete.stream().noneMatch(docPath::startsWith)) {
232            documentIdsToDelete.add(docId);
233            documentPathsToDelete.add(docPath);
234        }
235        return docId;
236    }
237
238    public static void deleteDocument(String idOrPath) {
239        // TODO change that by proper deleteDocument(String)
240        if (idOrPath.startsWith("/")) {
241            // @yannis : temporary way to avoid DocumentNotFoundException in server log before NXP-19658
242            Documents documents = CLIENT.repository().query(String.format(DOCUMENT_QUERY_BY_PATH_BASE, idOrPath));
243            if (documents.size() > 0) {
244                CLIENT.repository().deleteDocument(documents.getDocument(0));
245            }
246        } else {
247            CLIENT.repository().deleteDocument(CLIENT.repository().fetchDocumentById(idOrPath));
248        }
249    }
250
251    public static void addPermission(String idOrPath, String username, String permission) {
252        Document document;
253        if (idOrPath.startsWith("/")) {
254            document = CLIENT.repository().fetchDocumentByPath(idOrPath);
255        } else {
256            document = CLIENT.repository().fetchDocumentById(idOrPath);
257        }
258
259        ACE ace = new ACE();
260        ace.setUsername(username);
261        ace.setPermission(permission);
262
263        // @yannis : temporary fix for setting permission before JAVACLIENT-90 is done
264        Calendar beginDate = Calendar.getInstance();
265        ace.setBegin(beginDate);
266        Calendar endDate = Calendar.getInstance();
267        endDate.add(Calendar.YEAR, 1);
268        ace.setEnd(endDate);
269
270        document.addPermission(ace);
271    }
272
273    public static void removePermissions(String idOrPath, String username) {
274        Document document;
275        if (idOrPath.startsWith("/")) {
276            document = CLIENT.repository().fetchDocumentByPath(idOrPath);
277        } else {
278            document = CLIENT.repository().fetchDocumentById(idOrPath);
279        }
280
281        document.removePermission(username);
282    }
283
284    public static void logOnServer(String level, String message) {
285        CLIENT.get(String.format("%s/restAPI/systemLog?token=dolog&level=%s&message=%s", AbstractTest.NUXEO_URL, level,
286                URIUtils.quoteURIPathComponent(message, true)));
287    }
288
289}