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}