package org.multibit.mbm.resources.user; import com.google.common.base.Optional; import com.yammer.dropwizard.jersey.caching.CacheControl; import com.yammer.metrics.annotation.Timed; import org.multibit.mbm.api.hal.HalMediaType; import org.multibit.mbm.api.request.AdminDeleteEntityRequest; import org.multibit.mbm.api.request.user.AdminCreateUserRequest; import org.multibit.mbm.api.request.user.AdminUpdateUserRequest; import org.multibit.mbm.api.response.hal.user.AdminUserBridge; import org.multibit.mbm.api.response.hal.user.AdminUserCollectionBridge; import org.multibit.mbm.auth.Authority; import org.multibit.mbm.auth.annotation.RestrictedTo; import org.multibit.mbm.db.dao.UserDao; import org.multibit.mbm.core.model.User; import org.multibit.mbm.core.model.UserBuilder; import org.multibit.mbm.resources.BaseResource; import org.multibit.mbm.resources.ResourceAsserts; import org.springframework.stereotype.Component; import javax.annotation.Resource; import javax.ws.rs.*; import javax.ws.rs.core.Response; import java.net.URI; import java.util.List; import java.util.concurrent.TimeUnit; /** * <p>Resource to provide the following to application:</p> * <ul> * <li>Provision of REST endpoints to manage CRUD operations by an administrator against a collection of {@link User} entities</li> * </ul> * * @since 0.0.1 */ @Component @Path("/admin") @Produces({HalMediaType.APPLICATION_HAL_JSON, HalMediaType.APPLICATION_HAL_XML}) public class AdminUserResource extends BaseResource { @Resource(name = "hibernateUserDao") private UserDao userDao; /** * Create a new User from the given mandatory fields * * @param adminUser A user with administrator rights * * @return A response containing the minimum details of the created entity */ @POST @Timed @Path("/user") public Response create( @RestrictedTo({Authority.ROLE_ADMIN}) User adminUser, AdminCreateUserRequest createUserRequest) { // Build a new User from the given request information User user = UserBuilder.newInstance() .withUsername(createUserRequest.getUsername()) .withPassword(createUserRequest.getPasswordDigest()) .build(); // Perform basic verification Optional<User> verificationUser = userDao.getByCredentials(user.getUsername(), user.getPasswordDigest()); ResourceAsserts.assertNotConflicted(verificationUser, "user"); // Persist the user User persistentUser = userDao.saveOrUpdate(user); // Provide a representation to the client AdminUserBridge bridge = new AdminUserBridge(uriInfo, Optional.of(adminUser)); URI location = uriInfo.getAbsolutePathBuilder().path(persistentUser.getId().toString()).build(); return created(bridge, persistentUser, location); } /** * Provide a paged response of all users in the system * * @param adminUser A user with administrator rights * @param rawPageSize The unvalidated page size * @param rawPageNumber The unvalidated page number * * @return A response containing a paged list of all users */ @GET @Timed @Path("/user") @CacheControl(maxAge = 6, maxAgeUnit = TimeUnit.HOURS) public Response retrieveAllByPage( @RestrictedTo({Authority.ROLE_ADMIN}) User adminUser, @QueryParam("ps") Optional<String> rawPageSize, @QueryParam("pn") Optional<String> rawPageNumber) { // Validation int pageSize = Integer.valueOf(rawPageSize.get()); int pageNumber = Integer.valueOf(rawPageNumber.get()); List<User> users = userDao.getAllByPage(pageSize, pageNumber); // Provide a representation to the client AdminUserCollectionBridge bridge = new AdminUserCollectionBridge(uriInfo, Optional.of(adminUser)); return ok(bridge, users); } /** * Update an existing User with the populated fields * * @param adminUser A user with administrator rights * * @return A response containing the full details of the updated entity */ @PUT @Timed @Path("/user/{userId}") public Response update( @RestrictedTo({Authority.ROLE_ADMIN}) User adminUser, @PathParam("userId") Long userId, AdminUpdateUserRequest updateUserRequest) { // Retrieve the user Optional<User> user = userDao.getById(userId); ResourceAsserts.assertPresent(user, "user"); // Verify and apply any changes to the User User persistentUser = user.get(); // Apply the request to the entity apply(updateUserRequest, persistentUser); // Persist the updated user persistentUser = userDao.saveOrUpdate(user.get()); // Provide a representation to the client AdminUserBridge bridge = new AdminUserBridge(uriInfo, Optional.of(adminUser)); return ok(bridge, persistentUser); } /** * Delete an existing User (usually meaning set flag to deleted) * * @param adminUser A user with administrator rights * * @return A response containing the full details of the updated entity */ @DELETE @Timed @Path("/user/{userId}") public Response delete( @RestrictedTo({Authority.ROLE_ADMIN}) User adminUser, @PathParam("userId") Long userId, AdminDeleteEntityRequest deleteEntityRequest) { // Retrieve the user Optional<User> user = userDao.getById(userId); ResourceAsserts.assertPresent(user, "user"); // Verify and apply any changes to the User User persistentUser = user.get(); persistentUser.setDeleted(true); persistentUser.setReasonForDelete(deleteEntityRequest.getReason()); // Persist the updated user persistentUser = userDao.saveOrUpdate(user.get()); // Provide a representation to the client AdminUserBridge bridge = new AdminUserBridge(uriInfo, Optional.of(adminUser)); return ok(bridge, persistentUser); } /** * @param updateRequest The update request containing the changes * @param entity The entity to which these changes will be applied */ private void apply(AdminUpdateUserRequest updateRequest, User entity) { // TODO This will fail due to specialised password digest in UserBuilder // General approach here should be to use UserBuilder to create a new User // then bean copy into the persistent entity ignoring the ID field if (updateRequest.getPasswordDigest() != null) { entity.setPasswordDigest(updateRequest.getPasswordDigest()); } if (updateRequest.getUsername() != null) { entity.setUsername(updateRequest.getUsername()); } if (updateRequest.getApiKey() != null) { entity.setApiKey(updateRequest.getApiKey()); } if (updateRequest.getSecretKey() != null) { entity.setSecretKey(updateRequest.getSecretKey()); } // TODO Fill in the more advanced entries later (lots of primary/secondary fiddlin' about) // for (Map.Entry<String, String> userField: updateRequest.getUserFieldMap().entrySet()) { // UserFieldDetail userFieldDetail=new UserFieldDetail(); // entity.getUserFieldMap().put(userField.getKey(),userField.getValue()); // } } public void setUserDao(UserDao userDao) { this.userDao = userDao; } }