示例#1
0
async def delete_user_form(request: Request,
                           user: User = Depends(get_current_active_user)):
    vm = VMBase(request, user=user)
    if user.deletion_protection:
        return templates.TemplateResponse(
            "dashboard/account_deletion_protection_warning.html",
            vm.to_dict(),
            headers={"HX-Trigger-After-Settle": "openModal"},
        )
    return templates.TemplateResponse(
        "dashboard/account_delete.html",
        vm.to_dict(),
        headers={"HX-Trigger-After-Settle": "openModal"},
    )
示例#2
0
async def request_login(request: Request,
                        email: str = Form(...),
                        flow: str = Form(...)):
    """
    Request a login code or link. This will start the login process.

    :param request: request from the server
    :param email:  Email address entered by user
    :param flow: Either "otp" or "magic" to indicate either one time passcode or
    magic link authentication flow
    :return: redirect to the appropriate page to continue the login process
    """
    try:
        await user_crud.check_or_create_user_from_email(email)
        await auth_client.authenticate(email, flow)
    except pac.InvalidAuthFlow:
        raise HTTPException(status_code=400, detail="Invalid Auth Flow")
    except pac.AuthClientError:
        raise HTTPException(status_code=500,
                            detail="Internal Error, try again later")
    if flow == "otp":
        vm = ConfirmCodeVM(request, email)
        return templates.TemplateResponse("login/confirm_code.html",
                                          vm.to_dict())
    elif flow == "magic":
        return make_redirect_response(
            "/login/magic-message",
            status_code=status.HTTP_303_SEE_OTHER,
        )
    # Currently, the only valid auth flows are "otp" and "magic." If something else
    # is provided, it should raise "InvalidAuthFlow," therefore this code should be
    # unreachable.
    raise HTTPException(status_code=500, detail="Something has gone wrong")
示例#3
0
async def get_disable_deletion_protection_code(
    request: Request, user: User = Depends(get_current_active_user)):
    """
    Initiate disabling deletion protection for the app. This will send an email.

    :param request: the request from the server
    :param user: the owner of the app.
    :return: HTML for the confirmation form
    """
    vm = VMBase(request)
    await vm.check_for_user()
    code = deletion_protection.generate_dp_code(user, "account")
    try:
        await io_email.send(
            to=user.email,
            subject="Deletion Protection Code",
            text=f"Enter this code to disable deletion protection "
            f"for your user account: {code}. This code will expire in "
            f"{config.OTP_LIFETIME} minutes.",
            from_name="Purple Authentication",
            reply_to=config.WEBMASTER_EMAIL,
        )
    except io_email.EmailError:
        raise HTTPException(status_code=500, detail="Failed to send email")
    return templates.TemplateResponse(
        "dashboard/account_deletion_protection_confirm.html", vm.to_dict())
示例#4
0
async def update_me(
        request: Request,
        name: str = Form(...),
        current_user: User = Depends(get_current_active_user),
):
    """
    Update information about a user. User emails can't be changed programmatically
    right now.

    :param request: request from the server
    :param name: The user's new name
    :param current_user: User from the database
    :return: the user
    """
    updated_user = await user_crud.update_user(current_user, name)
    vm = VMBase(request=request, user=updated_user)
    res = templates.TemplateResponse("dashboard/user_account.html",
                                     vm.to_dict())
    res.headers["HX-Trigger"] = htmx.make_show_notification_header(
        res.headers,
        "Account Updated",
        "Your account has been updated successfully.",
        "success",
    )
    return res
示例#5
0
async def confirm_otp_login(
        request: Request,
        email: str = Form(...),
        otp: str = Form(...),
        stay_logged_in: bool = Form(False),
):
    """
    Process and confirm user login using one time passcode.

    :param request: request from the server
    :param email: Email address previously entered by the user
    :param otp: One time passcode entered by user
    :param stay_logged_in: Whether the user wants to stay logged in
    :return: redirect to the appropriate page to continue the login process
    """
    try:
        tokens = await auth_client.submit_code(email, otp)
        return make_authenticated_response(
            "/dashboard",
            tokens["id_token"],
            tokens.get("refresh_token"),
            stay_logged_in,
        )
    except pac.AuthClientError:
        vm = ConfirmCodeVM(request, email, error="Invalid code")
        return templates.TemplateResponse("login/confirm_code.html",
                                          vm.to_dict())
示例#6
0
async def dashboard_form_create_app(request: Request):
    vm = DashboardVM(request)
    if not await vm.check_for_user():
        return refresh_or_redirect_to_login("/dashboard")
    res = templates.TemplateResponse("dashboard/create_app_form.html",
                                     vm.to_dict())
    res.headers["HX-Trigger-After-Swap"] = "openModal"
    return res
示例#7
0
async def enable_deletion_protection(
    request: Request,
    app_id: str,
    user: User = Depends(get_current_active_user)):
    vm = SingleAppVM(request)
    await vm.check_for_user()
    vm.app = await clientapp_crud.enable_deletion_protection(app_id, user)
    res = templates.TemplateResponse(
        "dashboard/deletion_protection_display.html", vm.to_dict())
    res.headers["HX-Trigger-After-Settle"] = ujson.dumps(
        {"flashAppSection": "deletion_protection"})
    return res
示例#8
0
async def delete_app_form(request: Request,
                          app_id: str,
                          _user: User = Depends(get_current_active_user)):
    """
    Get HTML to either confirm deletion of the app or begin disabling deletion
    protection.
    :param request: the request from the server
    :param app_id: the unique id for the app to delete
    :param _user: the user who owns the app, this route requires authentication
    :return: HTML for the appropriate form
    """
    vm = SingleAppVM(request)
    await vm.get_app(app_id)
    if vm.app.deletion_protection:
        return templates.TemplateResponse(
            "dashboard/app_deletion_protection_warning.html", vm.to_dict())
    return templates.TemplateResponse(
        "dashboard/app_delete.html",
        vm.to_dict(),
        headers={"HX-Trigger-After-Settle": "openModal"},
    )
示例#9
0
async def update_app(
        request: Request,
        app_id: str,
        app_name: str = Form(...),
        redirect_url: str = Form(...),
        failure_redirect_url: str = Form(...),
        refresh_enabled: bool = Form(...),
        refresh_token_expire_hours: int = Form(24),
        low_quota_threshold: int = Form(10),
        user: User = Depends(get_current_active_user),
):
    """
    Update an app.

    :param request: the request from the server
    :param app_id: the unique id for the app to updated
    :param app_name: the new name for the app
    :param redirect_url: updated redirect url (see create_app)
    :param failure_redirect_url:  updated failure redirect url (see create_app)
    :param refresh_enabled: whether to enable refresh on the app
    :param refresh_token_expire_hours: how long refresh tokens are valid for.
    :param low_quota_threshold: how many authentications should be left before the
    app owner is notified that they are running out
    :param user: the owner of the app. This route requires authentication.
    :return: HTML to replace the app in the list and an event to display the app
    """
    updated_app = await clientapp_crud.update_client_app(
        app_id,
        user,
        app_name,
        redirect_url,
        refresh_enabled,
        refresh_token_expire_hours,
        failure_redirect_url,
        low_quota_threshold,
    )
    vm = SingleAppVM(request, updated_app)
    res = templates.TemplateResponse(
        "dashboard/single_app.html",
        vm.to_dict(),
    )
    res.headers["HX-Trigger"] = htmx.make_event_header(res.headers,
                                                       {"closeModal": {}})
    res.headers["HX-Trigger"] = htmx.make_show_notification_header(
        res.headers,
        "App Updated",
        f"Your app {updated_app.name} has been updated!",
        "success",
    )
    res.headers["HX-Trigger-After-Settle"] = ujson.dumps(
        {"showApp": updated_app.app_id})
    return res
示例#10
0
async def create_app(
        request: Request,
        app_name: str = Form(...),
        redirect_url: str = Form(...),
        failure_redirect_url: str = Form(...),
        refresh: bool = Form(False),
        refresh_token_expire_hours: int = Form(24),
        user: User = Depends(get_current_active_user),
):
    """
    Create a new app to authenticate against.

    :param request: request from the server
    :param app_name: The name of the app
    :param redirect_url: The url to redirect to after a successful magic link
    authentication.
    :param failure_redirect_url: The url to redirect to after a failed magic link
    authentication attempt.
    :param refresh: Whether to enable refresh on the app
    :param refresh_token_expire_hours: How long refresh tokens are valid for.
    :param user: The user creating the app. This route requires authentication.
    :return: HTML for the app in the list and events to display the full app.
    """
    new_app = await clientapp_crud.create_client_app(
        app_name=app_name,
        owner=user.email,
        redirect_url=redirect_url,
        refresh=refresh,
        refresh_token_expire_hours=refresh_token_expire_hours,
        failure_redirect_url=failure_redirect_url,
    )
    vm = SingleAppVM(request, new_app)
    res = templates.TemplateResponse(
        "dashboard/single_app.html",
        vm.to_dict(),
        headers={"HX-Trigger": '{"closeModal": "{}"}'},
        status_code=201,
    )
    res.headers["HX-Trigger"] = htmx.make_show_notification_header(
        res.headers,
        "App Created",
        f"Your app {new_app.name} has been created!",
        "success",
    )
    res.headers["HX-Trigger"] = htmx.make_event_header(res.headers,
                                                       {"appCreated": "{}"})
    res.headers["HX-Trigger-After-Settle"] = ujson.dumps(
        {"showApp": new_app.app_id})
    return res
示例#11
0
async def get_app_display(request: Request,
                          app_id: str,
                          _user: User = Depends(get_current_active_user)):
    """
    Get the HTML for the app display.

    :param request: the request from the server
    :param app_id: the unique id for the app
    :param _user: the user who owns the app. This route requires authentication.
    :return: HTML for the app display modal with an event to open the modal.
    """
    vm = SingleAppVM(request)
    await vm.get_app(app_id)
    return templates.TemplateResponse(
        "dashboard/app_display.html",
        vm.to_dict(),
        headers={"HX-Trigger-After-Settle": "openModal"},
    )
示例#12
0
async def rotate_app_keys_form(request: Request,
                               app_id: str,
                               _user: User = Depends(get_current_active_user)):
    """
    Get the form to rotate app encryption keys.

    :param request: request from the server
    :param app_id: the unique id of the app to rotate keys for
    :param _user: the user who owns the app. this route requires authentication

    :return: HTML of the appropriate form
    """
    vm = SingleAppVM(request)
    await vm.get_app(app_id)
    return templates.TemplateResponse(
        "dashboard/app_change_keys.html",
        vm.to_dict(),
    )
示例#13
0
async def how_it_works(request: Request):
    vm = HowItWorksVM(request)
    await vm.check_for_user()
    return templates.TemplateResponse("how-it-works.html", vm.to_dict())
示例#14
0
@portal_router.get("/login/magic-message")
async def magic_message(request: Request):
    vm = LoginVM(request)
    if await vm.check_for_user():
        return make_redirect_response("/dashboard")
    return templates.TemplateResponse("login/magic_message.html", vm.to_dict())


@portal_router.get("/login/magic-failed")
async def magic_failed_message(request: Request):
    vm = LoginVM(request)
    if await vm.check_for_user():
        return make_redirect_response("/dashboard")
    return templates.TemplateResponse("login/magic_failed.html", vm.to_dict())


@portal_router.get("/login")
async def start_login(
        request: Request,
        next_url: str = Query("/dashboard", alias="next"),
):
    vm = LoginVM(request)
    if await vm.check_for_user():
        return make_redirect_response("/dashboard")
    if id_token := await try_refresh(request):
        logging.debug("successfully refreshed")
        return make_authenticated_response(f"{next_url}?sessionRefreshed=true",
                                           id_token)
    return templates.TemplateResponse("login/login.html", vm.to_dict())
示例#15
0
async def index(request: Request):
    vm = IndexVM(request)
    await vm.check_for_user()
    return templates.TemplateResponse("index.html", vm.to_dict())
示例#16
0
async def magic_failed_message(request: Request):
    vm = LoginVM(request)
    if await vm.check_for_user():
        return make_redirect_response("/dashboard")
    return templates.TemplateResponse("login/magic_failed.html", vm.to_dict())
示例#17
0
async def confirm_login(request: Request, email: str = Query(None)):
    vm = LoginVM(request, user_email=email)
    if await vm.check_for_user():
        return make_redirect_response("/dashboard")
    return templates.TemplateResponse("login/confirm_code.html", vm.to_dict())
示例#18
0
async def tech_docs(request: Request):
    vm = TechDocsVM(request)
    return templates.TemplateResponse("tech-docs.html", vm.to_dict())
示例#19
0
async def dashboard(request: Request):
    vm = DashboardVM(request)
    if not await vm.check_for_user():
        return refresh_or_redirect_to_login("/dashboard")
    await vm.get_user_apps()
    return templates.TemplateResponse("dashboard/dashboard.html", vm.to_dict())