def test_login_post_success(self): from consoleme.lib.dynamo import UserDynamoHandler ddb = UserDynamoHandler() ddb.create_user("testuser2", "correctpassword", ["group1", "*****@*****.**"]) body = json.dumps({ "username": "******", "password": "******", "after_redirect_uri": "/", }) response = self.fetch("/api/v2/login", method="POST", body=body) self.assertEqual(response.code, 200) self.assertEqual( json.loads(response.body), { "status": "redirect", "reason": "authenticated_redirect", "redirect_url": "/", "status_code": 200, "message": "User has successfully authenticated. Redirecting to their intended destination.", "errors": None, "data": None, }, )
def test_login_post_invalid_password(self): from consoleme.lib.dynamo import UserDynamoHandler ddb = UserDynamoHandler() ddb.create_user("testuser", "correctpassword", ["group1", "*****@*****.**"]) body = json.dumps({ "username": "******", "password": "******", "after_redirect_uri": "/" }) response = self.fetch("/api/v2/login", method="POST", body=body) self.assertEqual(response.code, 403) self.assertEqual( json.loads(response.body), { "status": "error", "reason": "authentication_failure", "redirect_url": None, "status_code": 403, "message": None, "errors": [ "User doesn't exist, or password is incorrect. ", "Your next authentication failure will result in a 1 second wait. This wait time will expire after 60 seconds of no authentication failures.", ], "data": None, }, )
class UserRegistrationHandler(tornado.web.RequestHandler): """ Allows user registration if it is configured. """ def initialize(self): self.ddb = UserDynamoHandler() async def post(self): # TODO: Send verification e-mail to proposed user log_data = { "function": f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}", "message": "Attempting to register user", "user-agent": self.request.headers.get("User-Agent"), } generic_error_message: str = "User registration failed" # Fail if getting users by password is not enabled if not config.get("auth.get_user_by_password"): errors = [ "Expected configuration `auth.get_user_by_password`, but it is not enabled." ] await handle_generic_error_response(self, generic_error_message, errors, 403, "not_configured", log_data) return # Fail if user registration not allowed if not config.get("auth.allow_user_registration"): errors = [ "Expected configuration `auth.allow_user_registration`, but it is not enabled." ] await handle_generic_error_response(self, generic_error_message, errors, 403, "not_configured", log_data) return registration_attempt = RegistrationAttemptModel.parse_raw( self.request.body) log_data["username"] = registration_attempt.username # Fail if username not valid email address try: if not validate_email(registration_attempt.username): errors = ["Username must be a valid e-mail address."] await handle_generic_error_response( self, generic_error_message, errors, 403, "invalid_request", log_data, ) return except Exception as e: sentry_sdk.capture_exception() await handle_generic_error_response(self, generic_error_message, [str(e)], 403, "invalid_request", log_data) return # Fail if user already exists if await self.ddb.get_user(registration_attempt.username): errors = ["User already exists"] await handle_generic_error_response(self, generic_error_message, errors, 403, "invalid_request", log_data) return # Fails if password is not strong enough. password_strength_errors = await check_password_strength( registration_attempt.password) if password_strength_errors: await handle_generic_error_response( self, password_strength_errors["message"], password_strength_errors["errors"], 403, "weak_password", log_data, ) return self.ddb.create_user(registration_attempt.username, registration_attempt.password) res = WebResponse( status="success", status_code=200, message= f"Successfully created user {registration_attempt.username}.", ) self.write(res.json())
class UserManagementHandler(BaseAPIV2Handler): """ Handles creating and updating users. Only authorized users are allowed to access this endpoint. """ def initialize(self): self.ddb = UserDynamoHandler() async def post(self): log_data = { "function": f"{__name__}.{self.__class__.__name__}.{sys._getframe().f_code.co_name}", "user": self.user, "message": "Create/Update User", "user-agent": self.request.headers.get("User-Agent"), "request_id": self.request_uuid, "ip": self.ip, } generic_error_message = "Unable to create/update user" log.debug(log_data) # Checks authz levels of current user if not can_admin_all(self.user, self.groups): errors = ["User is not authorized to access this endpoint."] await handle_generic_error_response(self, generic_error_message, errors, 403, "unauthorized", log_data) return request = UserManagementModel.parse_raw(self.request.body) log_data["requested_user"] = request.username if request.user_management_action.value == "create": log.debug({ **log_data, "message": "Creating user", "requested_user": request.username, "requested_groups": request.groups, }) # Fails if password is not strong enough. password_strength_errors = await check_password_strength( request.password) if password_strength_errors: await handle_generic_error_response( self, password_strength_errors["message"], password_strength_errors["errors"], 403, "weak_password", log_data, ) return self.ddb.create_user( request.username, request.password, request.groups, ) res = WebResponse( status="success", status_code=200, message=f"Successfully created user {request.username}.", ) self.write(res.json()) return elif request.user_management_action.value == "update": log.debug({ **log_data, "message": "Updating user", "requested_user": request.username, "requested_groups": request.groups, }) if request.password: # Fails if password is not strong enough. password_strength_errors = await check_password_strength( request.password) if password_strength_errors: await handle_generic_error_response( self, password_strength_errors["message"], password_strength_errors["errors"], 403, "weak_password", log_data, ) return self.ddb.update_user( request.username, request.password, request.groups, ) res = WebResponse( status="success", status_code=200, message=f"Successfully updated user {request.username}.", ) self.write(res.json()) return elif request.user_management_action.value == "delete": log.debug({ **log_data, "message": "Deleting user", "requested_user": request.username, }) self.ddb.delete_user(request.username, ) res = WebResponse( status="success", status_code=200, message=f"Successfully deleted user {request.username}.", ) self.write(res.json()) return else: errors = ["Change type is not supported by this endpoint."] await handle_generic_error_response(self, generic_error_message, errors, 403, "invalid_request", log_data) return