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}