Example #1
0
async def get_nodes(request: Request) -> ORJSONResponse:
    try:
        limit = int(request.query_params.get("limit", 50))
    except ValueError as e:
        raise HTTPException("Invalid limit", 422) from e
    if limit > 100:
        raise HTTPException("Limit too large", 422)

    if "all" in request.query_params:
        cursor = await connection().fetch(
            "select * from nodes where user_id = $1 order by id limit $2;",
            request.user.id,
            limit,
        )
        return ORJSONResponse(list(cursor))

    # if parent is not in params, it is none, so it gets the nodes at the root
    id_ = request.query_params.get("parent")
    cursor = await connection().fetch(
        "select * from nodes where user_id = $1 and parent = $2 order by id limit $3;",
        request.user.id,
        id_,
        limit,
    )
    return ORJSONResponse(list(cursor))
Example #2
0
async def check_signup_availability(_request: Request) -> ORJSONResponse:
    return ORJSONResponse(
        {
            "enabled": settings.SIGNUP_ENABLED,
            "email_confirm_required": settings.SIGNUP_EMAIL_CONFIRM_REQUIRED,
        }
    )
Example #3
0
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()
Example #4
0
async def handle_http_exception(_request: Request,
                                exception: HTTPException) -> ORJSONResponse:
    """handles our custom HTTPException with headers"""
    return ORJSONResponse(
        {"error": exception.detail},
        headers=exception.headers,
        status_code=exception.status_code,
    )
Example #5
0
 async def __call__(self, scope: Scope, receive: Receive,
                    send: Send) -> None:
     # noinspection PyBroadException
     try:
         return await self.app(scope, receive, send)
     except Exception:
         response = ORJSONResponse({"error": "internal server error"},
                                   status_code=500)
         await response(scope, receive, send)
Example #6
0
async def cancel_two_factor_setup(request: Request) -> ORJSONResponse:
    if not request.user.two_factor_secret:
        raise HTTPException("Not enabled", 422)
    if request.user.two_factor_recovery:
        raise HTTPException("Already confirmed", 422)
    await connection().execute(
        "update users set two_factor_secret = null where id = $1",
        request.user.id)
    return ORJSONResponse()
Example #7
0
async def delete_account(request: Request) -> ORJSONResponse:
    conn = connection()
    async with conn.transaction():
        await conn.execute(
            "delete from api_keys where user_id = $1;"
            "delete from users where id = $1;",
            request.user.id,
        )
    return ORJSONResponse(None, 205)
Example #8
0
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()
Example #9
0
async def reset_two_factor_recovery_code(request: Request) -> ORJSONResponse:
    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)
Example #10
0
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)
Example #11
0
async def get_two_factor_status(request: Request) -> ORJSONResponse:
    if request.user.two_factor_recovery:
        two_factor_status = "setup_complete"
    elif request.user.two_factor_secret:
        two_factor_status = "setup_in_progress"
    else:
        two_factor_status = "disabled"
    return ORJSONResponse({
        "status": two_factor_status,
        "recovery": request.user.two_factor_recovery
    })
Example #12
0
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()
Example #13
0
async def disable_two_factor(request: Request) -> ORJSONResponse:
    await connection().execute(
        """
        update users set
            two_factor_secret = null,
            two_factor_recovery = null
        where id = $1
        """,
        request.user.id,
    )
    return ORJSONResponse()
Example #14
0
async def delete_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

    result = await connection().execute("""delete from nodes where id = $1""",
                                        id_)
    if result != "DELETE 1":
        raise HTTPException("not found", 404)

    return ORJSONResponse()
Example #15
0
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)
Example #16
0
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)
Example #17
0
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)
Example #18
0
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)
Example #19
0
async def begin_two_factor_setup(request: Request) -> ORJSONResponse:
    if request.user.two_factor_recovery:
        raise HTTPException("Already enabled", 422)
    elif request.user.two_factor_secret:
        raise HTTPException("Setup already started", 422)

    secret = pyotp.random_base32(32)

    await connection().execute(
        "update users set two_factor_secret = $1 where id = $2", secret,
        request.user.id)

    return ORJSONResponse({
        "secret":
        secret,
        "qr_code_url":
        make_totp_qr_code(secret, request.user.email)
    })
Example #20
0
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()
Example #21
0
async def get_api_key(request: Request) -> ORJSONResponse:
    if "id" in request.query_params:
        result = await connection().fetchrow(
            "select id, scope, expiry from api_keys where id = $1 and user_id = $2",
            request.query_params["id"],
            request.user.id,
        )
        if not result:
            raise HTTPException("not found", 404)
        else:
            result = dict(result)
    else:
        result = list(
            map(
                dict,
                await connection().fetch(
                    "select id, scope, expiry from api_keys where user_id = $1",
                    request.user.id,
                ),
            ))
    return ORJSONResponse(result)
Example #22
0
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)
Example #23
0
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)
Example #24
0
async def index(_request: Request) -> ORJSONResponse:
    return ORJSONResponse("hello, world!")
Example #25
0
async def get_account(request: Request) -> ORJSONResponse:
    return ORJSONResponse({
        "id": request.user.id,
        "email": request.user.email,
        "name": request.user.name,
    })