async def patch_account_name(request: Request) -> ORJSONResponse: name = await get_json(request) validate_types_raising(name, str) if not name: raise HTTPException("invalid name", 400) await connection().execute("update users set name = $1 where id = $2", name, request.user.id) return ORJSONResponse()
async def patch_account_email(request: Request) -> ORJSONResponse: json = await get_json(request) validate_types_raising(json, str) try: email = email_validator.validate_email(json).email except email_validator.EmailNotValidError as e: raise HTTPException("invalid email address", 400) from e await connection().execute("update users set email = $1 where id = $2", email, request.user.id) return ORJSONResponse()
async def signup_no_confirm(request: Request) -> ORJSONResponse: json = await get_json(request) validate_types_raising(json, {"name": str, "password": str, "email": str}) try: email_validator.validate_email(json["email"]) except email_validator.EmailNotValidError as e: raise HTTPException("invalid email address", 422) from e user_id = await _create_account(**json) return ORJSONResponse(user_id, 201)
async def delete_api_key(request: Request) -> ORJSONResponse: query = dict(request.query_params) validate_types_raising(query, {"id": str}) result = await connection().execute( "delete from api_keys where id = $1 and user_id = $2", query["id"], request.user.id, ) if result.split(" ")[1] == "0": raise HTTPException("not found", 404) return ORJSONResponse()
async def signup_confirm(request: Request) -> ORJSONResponse: json = await get_json(request) validate_types_raising(json, {"name": str, "password": str, "token": str}) email = decode_jwt(json["token"])["sub"] json["email"] = email try: user_id = await _create_account(**json) except HTTPException as e: raise HTTPException("account has already been created", 409) from e return ORJSONResponse(user_id, 201)
async def signup(request: Request) -> ORJSONResponse: json = await get_json(request) validate_types_raising(json, str) try: email = email_validator.validate_email(json) except email_validator.EmailNotValidError as e: raise HTTPException("invalid email address", 422) from e if await connection().fetchval("""select id from users where email = $1""", email): raise HTTPException("email address in use", 422) await send_message(email, "Confirm Tome account", "signup_confirm") return ORJSONResponse(None, 202)
async def generate_api_key(request: Request) -> ORJSONResponse: json = await get_json(request) validate_types_raising(json, { "scope": List[str], "duration": Optional[int] }) if json["duration"] is not None: expiry = time.time() + json["duration"] else: expiry = None scope = json["scope"] key = await create_api_key(expiry, request.user.id, scope) return ORJSONResponse(key, 201)
async def confirm_two_factor_setup(request: Request) -> ORJSONResponse: json = await get_json(request) validate_types_raising(json, str) if request.user.two_factor_recovery: raise HTTPException("Already confirmed", 422) if not request.user.two_factor_secret: raise HTTPException("Not enabled", 422) if not pyotp.TOTP(request.user.two_factor_secret).verify(json): raise HTTPException("Incorrect code", 422) recovery = pyotp.random_base32(32) await connection().execute( "update users set two_factor_recovery = $1 where id = $2", recovery, request.user.id, ) return ORJSONResponse(recovery)
async def change_password(request: Request) -> ORJSONResponse: json = await get_json(request) validate_types_raising(json, {"new": str, "current": str}) # check new password validity if json["new"] == json["current"]: # same as current (even if current is incorrect, we needn't bother checking) raise HTTPException("Password not changed", 422) check_password_strength(json["new"]) # check current password is correct if not verify_password(request.user.password, json["current"]): raise HTTPException("Incorrect password", 401) # update password hashed_new = hash_password(json["new"]) await connection().execute("update users set password = $1 where id = $2", hashed_new, request.user.id) return ORJSONResponse()
async def modify_node(request: Request) -> ORJSONResponse: try: id_ = uuid.UUID(request.query_params.get("id")) except (ValueError, TypeError) as e: raise HTTPException("invalid ID", 422) from e json = await get_json(request) validate_types_raising(json, { "content": Optional[str], "parent": Optional[str] }) query = _modify_node_query(json, id_) try: result = await connection().fetchrow(*query) except (TypeError, ValueError) as e: raise HTTPException("Invalid ID", 422) from e except asyncpg.ForeignKeyViolationError as e: raise HTTPException("Parent does not exist", 404) from e return ORJSONResponse(result)
async def create_node(request: Request) -> ORJSONResponse: json = await get_json(request) validate_types_raising(json, { "parent": Optional[str], "content": Optional[str] }) parent = None if "parent" in json: try: parent = uuid.UUID(json["parent"]) except ValueError as e: raise HTTPException("invalid ID", 422) from e try: result = await connection().fetchval( "insert into nodes (user_id, parent) values ($1, $2) returning id", request.user.id, parent, ) except asyncpg.ForeignKeyViolationError as e: raise HTTPException("Parent node does not exist", 404) from e return ORJSONResponse(result)