001/*
002 * (C) Copyright 2015 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Nuxeo - initial API and implementation
016 *
017 */
018
019package org.nuxeo.scim.server.jaxrs.usermanager;
020
021import java.io.Serializable;
022import java.util.ArrayList;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026
027import javax.ws.rs.Consumes;
028import javax.ws.rs.DELETE;
029import javax.ws.rs.GET;
030import javax.ws.rs.POST;
031import javax.ws.rs.PUT;
032import javax.ws.rs.Path;
033import javax.ws.rs.PathParam;
034import javax.ws.rs.Produces;
035import javax.ws.rs.core.Context;
036import javax.ws.rs.core.MediaType;
037import javax.ws.rs.core.Response;
038import javax.ws.rs.core.Response.Status;
039import javax.ws.rs.core.UriInfo;
040
041import org.nuxeo.ecm.core.api.ClientException;
042import org.nuxeo.ecm.core.api.DocumentModel;
043import org.nuxeo.ecm.core.api.DocumentModelList;
044import org.nuxeo.ecm.directory.DirectoryException;
045import org.nuxeo.ecm.directory.Session;
046import org.nuxeo.ecm.directory.api.DirectoryService;
047import org.nuxeo.ecm.webengine.WebException;
048import org.nuxeo.ecm.webengine.model.WebObject;
049import org.nuxeo.runtime.api.Framework;
050import org.nuxeo.scim.server.jaxrs.marshalling.UserResponse;
051
052import com.unboundid.scim.data.UserResource;
053import com.unboundid.scim.sdk.Resources;
054
055/**
056 * Simple Resource class used to expose the SCIM API on Users endpoint
057 *
058 * @author tiry
059 * @since 7.4
060 */
061@WebObject(type = "users")
062@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
063public class SCIMUserWebObject extends BaseUMObject {
064
065    @Override
066    protected String getPrefix() {
067        return "/Users";
068    }
069
070    protected UserResource resolveUserRessource(String uid) {
071
072        try {
073            DocumentModel userModel = um.getUserModel(uid);
074            if (userModel != null) {
075                return mapper.getUserResourceFromNuxeoUser(userModel);
076            }
077        } catch (Exception e) {
078            log.error("Error while resolving User", e);
079        }
080
081        return null;
082    }
083
084    @GET
085    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML + "; qs=0.9" })
086    public Resources<UserResource> getUsers(@Context UriInfo uriInfo) {
087
088        Map<String, List<String>> params = uriInfo.getQueryParameters();
089
090        // filter
091        Map<String, Serializable> filter = new HashMap<>();
092        List<String> filters = params.get("filter");
093        if (filters != null && filters.size() > 0) {
094            String[] filterParts = filters.get(0).split(" ");
095            if (filterParts[1].equals("eq")) {
096                String key = filterParts[0];
097                if (key.equals("userName")) {
098                    key = "username";
099                }
100                String value = filterParts[2];
101                if (value.startsWith("\"")) {
102                    value = value.substring(1, value.length() - 2);
103                }
104                filter.put(key, value);
105            }
106        }
107
108        // sort
109        List<String> sortCol = params.get("sortBy");
110        List<String> sortType = params.get("sortOrder");
111        // XXX mapping
112        Map<String, String> orderBy = new HashMap<>();
113        if (sortCol != null && sortCol.size() > 0) {
114            String order = "asc";
115            if (sortType != null && sortType.size() > 0) {
116                if (sortType.get(0).equalsIgnoreCase("descending")) {
117                    order = "desc";
118                }
119                orderBy.put(sortCol.get(0), order);
120            }
121        }
122        int startIndex = 1;
123        if (params.get("startIndex") != null) {
124            startIndex = Integer.parseInt(params.get("startIndex").get(0));
125        }
126        int count = 10;
127        if (params.get("count") != null) {
128            count = Integer.parseInt(params.get("count").get(0));
129        }
130
131        try {
132            String directoryName = um.getUserDirectoryName();
133
134            DirectoryService ds = Framework.getLocalService(DirectoryService.class);
135
136            Session dSession = null;
137            DocumentModelList userModels = null;
138            try {
139                dSession = ds.open(directoryName);
140                userModels = dSession.query(filter, null, orderBy, true, count, startIndex - 1);
141            } finally {
142                dSession.close();
143            }
144
145            List<UserResource> userResources = new ArrayList<>();
146            for (DocumentModel userModel : userModels) {
147                userResources.add(mapper.getUserResourceFromNuxeoUser(userModel));
148            }
149            return new Resources<>(userResources, userResources.size(), startIndex);
150        } catch (Exception e) {
151            log.error("Error while getting Users", e);
152        }
153        return null;
154    }
155
156    @Path("{uid}")
157    @GET
158    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
159    public UserResource getUserResource(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
160        return resolveUserRessource(uid);
161
162    }
163
164    @Path("{uid}.xml")
165    @GET
166    @Produces(MediaType.APPLICATION_XML)
167    public UserResource getUserResourceAsXml(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
168        return getUserResource(uriInfo, uid);
169    }
170
171    @Path("{uid}.json")
172    @GET
173    @Produces(MediaType.APPLICATION_JSON)
174    public UserResource getUserResourceAsJSON(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
175        return getUserResource(uriInfo, uid);
176    }
177
178    @POST
179    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
180    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
181    public Response createUser(UserResource user) {
182        return doCreateUserResponse(user, fixeMediaType);
183    }
184
185    protected Response doCreateUserResponse(UserResource user, MediaType mt) {
186        try {
187            checkUpdateGuardPreconditions();
188            return UserResponse.created(doCreateUser(user), mt);
189        } catch (ClientException e) {
190            throw WebException.wrap(e);
191        }
192
193    }
194
195    protected UserResource doCreateUser(UserResource user) {
196
197        try {
198            DocumentModel newUser = mapper.createNuxeoUserFromUserResource(user);
199            UserResource resource = mapper.getUserResourceFromNuxeoUser(newUser);
200            return resource;
201        } catch (Exception e) {
202            log.error("Unable to create User", e);
203        }
204        return null;
205    }
206
207    @PUT
208    @Path("{uid}")
209    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
210    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
211    public Response updateUser(@Context UriInfo uriInfo, @PathParam("uid") String uid, UserResource user) {
212        try {
213            checkUpdateGuardPreconditions();
214            return doUpdateUser(uid, user, fixeMediaType);
215        } catch (ClientException e) {
216            throw WebException.wrap(e);
217        }
218    }
219
220    protected Response doUpdateUser(String uid, UserResource user, MediaType mt) {
221
222        try {
223            DocumentModel userModel = mapper.updateNuxeoUserFromUserResource(uid, user);
224            if (userModel != null) {
225                UserResource userResource = mapper.getUserResourceFromNuxeoUser(userModel);
226                return UserResponse.updated(userResource, mt);
227            }
228        } catch (Exception e) {
229            log.error("Unable to create User", e);
230        }
231        return null;
232    }
233
234    @Path("{uid}")
235    @DELETE
236    public Response deleteUserResource(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
237        try {
238            um.deleteUser(uid);
239            return Response.ok().build();
240        } catch (DirectoryException e) {
241            return Response.status(Status.NOT_FOUND).build();
242        }
243    }
244
245}