001/*
002 * (C) Copyright 2015 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 *     Nuxeo - initial API and implementation
018 *
019 */
020
021package org.nuxeo.scim.server.jaxrs.usermanager;
022
023import java.io.Serializable;
024import java.util.ArrayList;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028
029import javax.ws.rs.Consumes;
030import javax.ws.rs.DELETE;
031import javax.ws.rs.GET;
032import javax.ws.rs.POST;
033import javax.ws.rs.PUT;
034import javax.ws.rs.Path;
035import javax.ws.rs.PathParam;
036import javax.ws.rs.Produces;
037import javax.ws.rs.core.Context;
038import javax.ws.rs.core.MediaType;
039import javax.ws.rs.core.Response;
040import javax.ws.rs.core.Response.Status;
041import javax.ws.rs.core.UriInfo;
042
043import org.nuxeo.ecm.core.api.DocumentModel;
044import org.nuxeo.ecm.core.api.DocumentModelList;
045import org.nuxeo.ecm.directory.DirectoryException;
046import org.nuxeo.ecm.directory.Session;
047import org.nuxeo.ecm.directory.api.DirectoryService;
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.getService(DirectoryService.class);
135
136            DocumentModelList userModels = null;
137            try (Session dSession = ds.open(directoryName)) {
138                userModels = dSession.query(filter, null, orderBy, true, count, startIndex - 1);
139            }
140
141            List<UserResource> userResources = new ArrayList<>();
142            for (DocumentModel userModel : userModels) {
143                userResources.add(mapper.getUserResourceFromNuxeoUser(userModel));
144            }
145            return new Resources<>(userResources, userResources.size(), startIndex);
146        } catch (Exception e) {
147            log.error("Error while getting Users", e);
148        }
149        return null;
150    }
151
152    @Path("{uid}")
153    @GET
154    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
155    public UserResource getUserResource(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
156        return resolveUserRessource(uid);
157
158    }
159
160    @Path("{uid}.xml")
161    @GET
162    @Produces(MediaType.APPLICATION_XML)
163    public UserResource getUserResourceAsXml(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
164        return getUserResource(uriInfo, uid);
165    }
166
167    @Path("{uid}.json")
168    @GET
169    @Produces(MediaType.APPLICATION_JSON)
170    public UserResource getUserResourceAsJSON(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
171        return getUserResource(uriInfo, uid);
172    }
173
174    @POST
175    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
176    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
177    public Response createUser(UserResource user) {
178        return doCreateUserResponse(user, fixeMediaType);
179    }
180
181    protected Response doCreateUserResponse(UserResource user, MediaType mt) {
182        checkUpdateGuardPreconditions();
183        return UserResponse.created(doCreateUser(user), mt);
184
185    }
186
187    protected UserResource doCreateUser(UserResource user) {
188
189        try {
190            DocumentModel newUser = mapper.createNuxeoUserFromUserResource(user);
191            UserResource resource = mapper.getUserResourceFromNuxeoUser(newUser);
192            return resource;
193        } catch (Exception e) {
194            log.error("Unable to create User", e);
195        }
196        return null;
197    }
198
199    @PUT
200    @Path("{uid}")
201    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
202    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
203    public Response updateUser(@Context UriInfo uriInfo, @PathParam("uid") String uid, UserResource user) {
204        checkUpdateGuardPreconditions();
205        return doUpdateUser(uid, user, fixeMediaType);
206    }
207
208    protected Response doUpdateUser(String uid, UserResource user, MediaType mt) {
209
210        try {
211            DocumentModel userModel = mapper.updateNuxeoUserFromUserResource(uid, user);
212            if (userModel != null) {
213                UserResource userResource = mapper.getUserResourceFromNuxeoUser(userModel);
214                return UserResponse.updated(userResource, mt);
215            }
216        } catch (Exception e) {
217            log.error("Unable to create User", e);
218        }
219        return null;
220    }
221
222    @Path("{uid}")
223    @DELETE
224    public Response deleteUserResource(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
225        try {
226            um.deleteUser(uid);
227            return Response.ok().build();
228        } catch (DirectoryException e) {
229            return Response.status(Status.NOT_FOUND).build();
230        }
231    }
232
233}