/* * Copyright 2012 Nodeable Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.streamreduce.rest.resource.admin; import com.streamreduce.core.model.Account; import com.streamreduce.core.model.User; import com.streamreduce.core.service.UserService; import com.streamreduce.core.service.exception.AccountNotFoundException; import com.streamreduce.core.service.exception.UserNotFoundException; import com.streamreduce.util.SecurityUtil; import net.sf.json.JSONObject; import org.bson.types.ObjectId; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.ws.rs.DELETE; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; import javax.ws.rs.HEAD; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; /** * Methods in this class should not be publically available. They are for internal and super-user use only! */ @Component @Path("admin/user") public class AdminUserResource extends AbstractAdminResource { @Autowired private UserService userService; /** * Usernames are unique in the DB. This is a convenience method to determine if a username already exists in the system. * * @param username - username to check * @return - http status code * @response.representation.200.doc Returned if the username is available * @response.representation.400.doc if the username is null or empty * @response.representation.409.doc Returned if the username is NOT available */ @HEAD @Path("available/{username}") public Response checkUsernameAvailability(@PathParam("username") String username) { if (isEmpty(username)) { return error("Username path param can not be empty.", Response.status(Response.Status.BAD_REQUEST)); } logger.debug("Check email as username availability for " + username); if (userService.isUsernameAvailable(username)) { return Response.ok().build(); } else { return error("Username not available", Response.status(Response.Status.CONFLICT)); } } /** * Get the User object given the username. ObjectId is not supported, only a valid username * * @param username - a valid nodeable username * @return - a single user DTO * @response.representation.200.doc Returned if the user is found based on the username * @response.representation.404.doc Returned if the username is not found * @response.representation.400.doc if the username is null or empty */ @GET @Path("{username}") public Response getUser(@PathParam("username") String username) { if (isEmpty(username)) { return error("Username path param can not be empty.", Response.status(Response.Status.BAD_REQUEST)); } logger.debug("Get user by username " + username); User user; try { user = userService.getUser(username); } catch (UserNotFoundException e) { return error(e.getMessage(), Response.status(Response.Status.NOT_FOUND)); } return Response.ok(toDTO(user)).build(); } /** * Creates a new user, represented by the JSON payload, for the given account ID. * * @response.representation.201.doc Returned if the username is successfully created * @response.representation.409.doc Returned if the username is NOT available * * @param accountId * @param json * @return the response object */ @POST @Path("{accountId}") public Response createUser(@PathParam("accountId") String accountId, JSONObject json) { String username = getJSON(json, "username"); if (isEmpty(username)) { return error("Username cannot be empty.", Response.status(Response.Status.BAD_REQUEST)); } Account account = null; try { account = userService.getAccount(new ObjectId(accountId)); } catch (AccountNotFoundException anfe) { return error("No account found with specified ID.", Response.status(Response.Status.BAD_REQUEST)); } if (!userService.isUsernameAvailable(username)) { return error("Username '" + username + "' is currently in use.", Response.status(Response.Status.BAD_REQUEST)); } boolean adminRole = (json.containsKey("role") && json.getString("role").equals("admin")); User user = new User.Builder() .fullname(getJSON(json, "fullname")) .password(getJSON(json, "password")) .username(username) .roles(adminRole ? userService.getAdminRoles() : userService.getUserRoles()) .account(account) .build(); // create the base nodeable user -- no account yet userService.createUser(user); return Response.status(Response.Status.CREATED).entity(toDTO(user)).build(); } /** * Resend the new user request email if the user has not already been activated. * All this really does is refire the event to trigger the email service. * * @param username - the Nodeable username to resend the invite too * @return http status code * @response.representation.200.doc Returned if the email was resent successfully * @response.representation.400.doc Returned if the user has already been activated * @response.representation.404.doc if the username is not found */ @GET @Path("resend/{username}") public Response recreateUserRequest(@PathParam("username") String username) { if (isEmpty(username)) { return error("Username path param can not be empty.", Response.status(Response.Status.BAD_REQUEST)); } try { User user = userService.getUser(username); if (user.getUserStatus().equals(User.UserStatus.ACTIVATED)) { return error("User has already been activated.", Response.status(Response.Status.BAD_REQUEST)); } userService.recreateUserRequest(user); } catch (UserNotFoundException e) { return error("User not found.", Response.status(Response.Status.NOT_FOUND)); } return Response.ok().build(); } /** * Send the password reset email for the specified username if the user has been activated already. * * @param username - the email/username to send the email too * @param mobile - if it's a mobile device request, this should be set to true * @return - http status code * @response.representation.200.doc Returned if the email was resent successfully * @response.representation.404.doc Returned if the user is not found * @response.representation.400.doc Returned if the user has not been activated yet */ @GET @Path("email/password/{username}") public Response sendPasswordResetEmail(@PathParam("username") String username, @DefaultValue("false") @QueryParam("mobile") boolean mobile) { if (isEmpty(username)) { return error("Username path param can not be empty.", Response.status(Response.Status.BAD_REQUEST)); } User user; try { user = userService.getUser(username); // this can only be done for for active users if (!user.getUserStatus().equals(User.UserStatus.ACTIVATED)) { return error("User has not been activated, perhaps you need to resend the activation email?", Response.status(Response.Status.BAD_REQUEST)); } // TODO: SOBA-421 check the activity log to see how many times this has been done. // if it's > N in the last X hours, return an error // create a secret key for the email // it will allow us to confirm and identify the user String secretKey = (SecurityUtil.generateRandomString()); user.setSecretKey(secretKey); // email will be sent on event userService.resetUserPassword(user, mobile); } catch (UserNotFoundException e) { return error(e.getMessage(), Response.status(Response.Status.NOT_FOUND)); } return Response.ok().build(); } /** * Delete a user and all the resources they own. This operation can not be undone. * * @param userId - the ObjectId of the user to remove * @return - http status code * @response.representation.200.doc Returned if the user was deleted successfully * @response.representation.404.doc Returned if the user is not found */ @DELETE @Path("{id}") public Response removeUser(@PathParam("id") ObjectId userId) { try { User user = userService.getUserById(userId); if (user.getUserStatus().equals(User.UserStatus.PENDING)) { // lightweight delete since user hasn't been totally setup userService.deletePendingUser(user); } else { userService.deleteUser(user); } } catch (UserNotFoundException e) { return error("User not found.", Response.status(Response.Status.NOT_FOUND)); } return Response.ok().build(); } }