package com.wesabe.grendel.resources; import java.security.SecureRandom; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Context; import javax.ws.rs.core.EntityTag; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import org.joda.time.DateTime; import com.google.inject.Inject; import com.google.inject.Provider; import com.wesabe.grendel.auth.Credentials; import com.wesabe.grendel.auth.Session; import com.wesabe.grendel.entities.Document; import com.wesabe.grendel.entities.User; import com.wesabe.grendel.entities.dao.UserDAO; import com.wesabe.grendel.openpgp.CryptographicException; import com.wesabe.grendel.openpgp.UnlockedKeySet; import com.wesabe.grendel.representations.UpdateUserRepresentation; import com.wesabe.grendel.representations.UserInfoRepresentation; import com.wideplay.warp.persist.Transactional; /** * A resource for managing individual {@link User}s. * * @author coda */ @Path("/users/{id}") public class UserResource { private final UserDAO userDAO; private final Provider<SecureRandom> randomProvider; @Inject public UserResource(UserDAO userDAO, Provider<SecureRandom> randomProvider) { this.userDAO = userDAO; this.randomProvider = randomProvider; } /** * Responds to a {@link GET} request with information about the specified * user. */ @GET @Produces(MediaType.APPLICATION_JSON) public Response show(@Context Request request, @Context UriInfo uriInfo, @PathParam("id") String id) { final User user = userDAO.findById(id); if (user == null) { throw new WebApplicationException(Status.NOT_FOUND); } checkPreconditions(request, user); return Response.ok(new UserInfoRepresentation(uriInfo, user)) .tag(user.getEtag()) .lastModified(user.getModifiedAt().toDate()) .build(); } /** * Responds to a {@link PUT} request by changing the user's password. * <p> * <strong>N.B.:</strong> Requires Basic authentication. * @throws CryptographicException */ @PUT @Transactional @Consumes(MediaType.APPLICATION_JSON) public Response update(@Context Request request,@Context Credentials credentials, @PathParam("id") String id, UpdateUserRepresentation entity) throws CryptographicException { entity.validate(); final Session session = credentials.buildSession(userDAO, id); checkPreconditions(request, session.getUser()); final User user = session.getUser(); final UnlockedKeySet keySet = session.getKeySet(); user.setKeySet( keySet.relock( credentials.getPassword().toCharArray(), entity.getPassword(), randomProvider.get() ) ); user.setModifiedAt(new DateTime()); userDAO.saveOrUpdate(user); return Response.noContent().build(); } /** * Responds to a {@link DELETE} request by deleting the user <strong>and * all their {@link Document}s.</strong> */ @DELETE @Transactional public Response delete(@Context Request request,@Context UriInfo uriInfo, @PathParam("id") String id) { final User user = userDAO.findById(id); checkPreconditions(request, user); userDAO.delete(user); return Response.noContent().build(); } /** * If the request has {@code If-Modified-Since} or {@code If-None-Match} * headers, and the resource has a matching {@link User#getModifiedAt()} * or {@link User#getEtag()}, returns a {@code 304 Unmodified}, * indicating the client has the most recent version of the resource. * * If the request has a {@code If-Unmodified-Since} or {@code If-Match} * headers, and the resource has a more recent * {@link User#getModifiedAt()} or {@link User#getEtag()}, returns * a {@code 412 Precondition Failed}, indicating the client should re-read * the resource before overwriting it. */ private void checkPreconditions(Request request, User user) { final EntityTag eTag = new EntityTag(user.getEtag()); final ResponseBuilder response = request.evaluatePreconditions(user.getModifiedAt().toDate(), eTag); if (response != null) { throw new WebApplicationException(response.build()); } } }