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.ClientException;
044import org.nuxeo.ecm.core.api.DocumentModel;
045import org.nuxeo.ecm.core.api.DocumentModelList;
046import org.nuxeo.ecm.directory.DirectoryException;
047import org.nuxeo.ecm.directory.Session;
048import org.nuxeo.ecm.directory.api.DirectoryService;
049import org.nuxeo.ecm.webengine.WebException;
050import org.nuxeo.ecm.webengine.model.WebObject;
051import org.nuxeo.runtime.api.Framework;
052import org.nuxeo.scim.server.jaxrs.marshalling.UserResponse;
053
054import com.unboundid.scim.data.UserResource;
055import com.unboundid.scim.sdk.Resources;
056
057/**
058 * Simple Resource class used to expose the SCIM API on Users endpoint
059 *
060 * @author tiry
061 * @since 7.4
062 */
063@WebObject(type = "users")
064@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
065public class SCIMUserWebObject extends BaseUMObject {
066
067    @Override
068    protected String getPrefix() {
069        return "/Users";
070    }
071
072    protected UserResource resolveUserRessource(String uid) {
073
074        try {
075            DocumentModel userModel = um.getUserModel(uid);
076            if (userModel != null) {
077                return mapper.getUserResourceFromNuxeoUser(userModel);
078            }
079        } catch (Exception e) {
080            log.error("Error while resolving User", e);
081        }
082
083        return null;
084    }
085
086    @GET
087    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML + "; qs=0.9" })
088    public Resources<UserResource> getUsers(@Context UriInfo uriInfo) {
089
090        Map<String, List<String>> params = uriInfo.getQueryParameters();
091
092        // filter
093        Map<String, Serializable> filter = new HashMap<>();
094        List<String> filters = params.get("filter");
095        if (filters != null && filters.size() > 0) {
096            String[] filterParts = filters.get(0).split(" ");
097            if (filterParts[1].equals("eq")) {
098                String key = filterParts[0];
099                if (key.equals("userName")) {
100                    key = "username";
101                }
102                String value = filterParts[2];
103                if (value.startsWith("\"")) {
104                    value = value.substring(1, value.length() - 2);
105                }
106                filter.put(key, value);
107            }
108        }
109
110        // sort
111        List<String> sortCol = params.get("sortBy");
112        List<String> sortType = params.get("sortOrder");
113        // XXX mapping
114        Map<String, String> orderBy = new HashMap<>();
115        if (sortCol != null && sortCol.size() > 0) {
116            String order = "asc";
117            if (sortType != null && sortType.size() > 0) {
118                if (sortType.get(0).equalsIgnoreCase("descending")) {
119                    order = "desc";
120                }
121                orderBy.put(sortCol.get(0), order);
122            }
123        }
124        int startIndex = 1;
125        if (params.get("startIndex") != null) {
126            startIndex = Integer.parseInt(params.get("startIndex").get(0));
127        }
128        int count = 10;
129        if (params.get("count") != null) {
130            count = Integer.parseInt(params.get("count").get(0));
131        }
132
133        try {
134            String directoryName = um.getUserDirectoryName();
135
136            DirectoryService ds = Framework.getLocalService(DirectoryService.class);
137
138            Session dSession = null;
139            DocumentModelList userModels = null;
140            try {
141                dSession = ds.open(directoryName);
142                userModels = dSession.query(filter, null, orderBy, true, count, startIndex - 1);
143            } finally {
144                dSession.close();
145            }
146
147            List<UserResource> userResources = new ArrayList<>();
148            for (DocumentModel userModel : userModels) {
149                userResources.add(mapper.getUserResourceFromNuxeoUser(userModel));
150            }
151            return new Resources<>(userResources, userResources.size(), startIndex);
152        } catch (Exception e) {
153            log.error("Error while getting Users", e);
154        }
155        return null;
156    }
157
158    @Path("{uid}")
159    @GET
160    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
161    public UserResource getUserResource(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
162        return resolveUserRessource(uid);
163
164    }
165
166    @Path("{uid}.xml")
167    @GET
168    @Produces(MediaType.APPLICATION_XML)
169    public UserResource getUserResourceAsXml(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
170        return getUserResource(uriInfo, uid);
171    }
172
173    @Path("{uid}.json")
174    @GET
175    @Produces(MediaType.APPLICATION_JSON)
176    public UserResource getUserResourceAsJSON(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
177        return getUserResource(uriInfo, uid);
178    }
179
180    @POST
181    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
182    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
183    public Response createUser(UserResource user) {
184        return doCreateUserResponse(user, fixeMediaType);
185    }
186
187    protected Response doCreateUserResponse(UserResource user, MediaType mt) {
188        try {
189            checkUpdateGuardPreconditions();
190            return UserResponse.created(doCreateUser(user), mt);
191        } catch (ClientException e) {
192            throw WebException.wrap(e);
193        }
194
195    }
196
197    protected UserResource doCreateUser(UserResource user) {
198
199        try {
200            DocumentModel newUser = mapper.createNuxeoUserFromUserResource(user);
201            UserResource resource = mapper.getUserResourceFromNuxeoUser(newUser);
202            return resource;
203        } catch (Exception e) {
204            log.error("Unable to create User", e);
205        }
206        return null;
207    }
208
209    @PUT
210    @Path("{uid}")
211    @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
212    @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
213    public Response updateUser(@Context UriInfo uriInfo, @PathParam("uid") String uid, UserResource user) {
214        try {
215            checkUpdateGuardPreconditions();
216            return doUpdateUser(uid, user, fixeMediaType);
217        } catch (ClientException e) {
218            throw WebException.wrap(e);
219        }
220    }
221
222    protected Response doUpdateUser(String uid, UserResource user, MediaType mt) {
223
224        try {
225            DocumentModel userModel = mapper.updateNuxeoUserFromUserResource(uid, user);
226            if (userModel != null) {
227                UserResource userResource = mapper.getUserResourceFromNuxeoUser(userModel);
228                return UserResponse.updated(userResource, mt);
229            }
230        } catch (Exception e) {
231            log.error("Unable to create User", e);
232        }
233        return null;
234    }
235
236    @Path("{uid}")
237    @DELETE
238    public Response deleteUserResource(@Context UriInfo uriInfo, @PathParam("uid") String uid) {
239        try {
240            um.deleteUser(uid);
241            return Response.ok().build();
242        } catch (DirectoryException e) {
243            return Response.status(Status.NOT_FOUND).build();
244        }
245    }
246
247}