Beispiel #1
0
async def pkgbase_merge_post(request: Request,
                             name: str,
                             into: str = Form(default=str()),
                             comments: str = Form(default=str()),
                             confirm: bool = Form(default=False),
                             next: str = Form(default=str())):

    pkgbase = get_pkg_or_base(name, PackageBase)
    context = await make_variable_context(request, "Package Merging")
    context["pkgbase"] = pkgbase

    # TODO: Lookup errors from credential instead of hardcoding them.
    if not request.user.has_credential(creds.PKGBASE_MERGE):
        context["errors"] = [
            "Only Trusted Users and Developers can merge packages."
        ]
        return render_template(request,
                               "pkgbase/merge.html",
                               context,
                               status_code=HTTPStatus.UNAUTHORIZED)

    if not confirm:
        context["errors"] = [
            "The selected packages have not been deleted, "
            "check the confirmation checkbox."
        ]
        return render_template(request,
                               "pkgbase/merge.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    try:
        target = get_pkg_or_base(into, PackageBase)
    except HTTPException:
        context["errors"] = [
            "Cannot find package to merge votes and comments into."
        ]
        return render_template(request,
                               "pkgbase/merge.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    if pkgbase == target:
        context["errors"] = ["Cannot merge a package base with itself."]
        return render_template(request,
                               "pkgbase/merge.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    with db.begin():
        update_closure_comment(pkgbase, MERGE_ID, comments, target=target)

    # Merge pkgbase into target.
    actions.pkgbase_merge_instance(request, pkgbase, target, comments=comments)

    if not next:
        next = f"/pkgbase/{target.Name}"

    # Redirect to the newly merged into package.
    return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
Beispiel #2
0
async def account(request: Request, username: str):
    _ = l10n.get_translator_for_request(request)
    context = await make_variable_context(
        request, _("Account") + " " + username)
    if not request.user.is_authenticated():
        return render_template(request, "account/show.html", context,
                               status_code=HTTPStatus.UNAUTHORIZED)

    # Get related User record, if possible.
    user = get_user_by_name(username)
    context["user"] = user

    # Format PGPKey for display with a space between each 4 characters.
    k = user.PGPKey or str()
    context["pgp_key"] = " ".join([k[i:i + 4] for i in range(0, len(k), 4)])

    login_ts = None
    session = db.query(models.Session).filter(
        models.Session.UsersID == user.ID).first()
    if session:
        login_ts = user.session.LastUpdateTS
    context["login_ts"] = login_ts

    # Render the template.
    return render_template(request, "account/show.html", context)
Beispiel #3
0
async def http_exception_handler(request: Request, exc: HTTPException) \
        -> Response:
    """ Handle an HTTPException thrown in a route. """
    phrase = http.HTTPStatus(exc.status_code).phrase
    context = make_context(request, phrase)
    context["exc"] = exc
    context["phrase"] = phrase

    # Additional context for some exceptions.
    if exc.status_code == http.HTTPStatus.NOT_FOUND:
        tokens = request.url.path.split("/")
        matches = re.match("^([a-z0-9][a-z0-9.+_-]*?)(\\.git)?$", tokens[1])
        if matches:
            try:
                pkgbase = get_pkg_or_base(matches.group(1))
                context = pkgbaseutil.make_context(request, pkgbase)
            except HTTPException:
                pass

    try:
        return render_template(request, f"errors/{exc.status_code}.html",
                               context, exc.status_code)
    except TemplateNotFound:
        return render_template(request, "errors/detail.html", context,
                               exc.status_code)
Beispiel #4
0
def render_proposal(request: Request,
                    context: dict,
                    proposal: int,
                    voteinfo: models.TUVoteInfo,
                    voters: typing.Iterable[models.User],
                    vote: models.TUVote,
                    status_code: HTTPStatus = HTTPStatus.OK):
    """ Render a single TU proposal. """
    context["proposal"] = proposal
    context["voteinfo"] = voteinfo
    context["voters"] = voters.all()

    total = voteinfo.total_votes()
    participation = (total / voteinfo.ActiveTUs) if voteinfo.ActiveTUs else 0
    context["participation"] = participation

    accepted = (voteinfo.Yes > voteinfo.ActiveTUs / 2) or \
        (participation > voteinfo.Quorum and voteinfo.Yes > voteinfo.No)
    context["accepted"] = accepted

    can_vote = voters.filter(
        models.TUVote.User == request.user).first() is None
    context["can_vote"] = can_vote

    if not voteinfo.is_running():
        context["error"] = "Voting is closed for this proposal."

    context["vote"] = vote
    context["has_voted"] = vote is not None

    return render_template(request,
                           "tu/show.html",
                           context,
                           status_code=status_code)
Beispiel #5
0
async def account_comments(request: Request, username: str):
    user = get_user_by_name(username)
    context = make_context(request, "Accounts")
    context["username"] = username
    context["comments"] = user.package_comments.order_by(
        models.PackageComment.CommentTS.desc())
    return render_template(request, "account/comments.html", context)
Beispiel #6
0
async def pkgbase_delete_post(request: Request,
                              name: str,
                              confirm: bool = Form(default=False),
                              comments: str = Form(default=str()),
                              next: str = Form(default="/packages")):
    pkgbase = get_pkg_or_base(name, PackageBase)

    if not request.user.has_credential(creds.PKGBASE_DELETE):
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    if not confirm:
        context = templates.make_context(request, "Package Deletion")
        context["pkgbase"] = pkgbase
        context["errors"] = [("The selected packages have not been deleted, "
                              "check the confirmation checkbox.")]
        return render_template(request,
                               "pkgbase/delete.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    if comments:
        # Update any existing deletion requests' ClosureComment.
        with db.begin():
            requests = pkgbase.requests.filter(
                and_(PackageRequest.Status == PENDING_ID,
                     PackageRequest.ReqTypeID == DELETION_ID))
            for pkgreq in requests:
                pkgreq.ClosureComment = comments

    notifs = actions.pkgbase_delete_instance(request,
                                             pkgbase,
                                             comments=comments)
    util.apply_all(notifs, lambda n: n.send())
    return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
Beispiel #7
0
def render_terms_of_service(request: Request,
                            context: dict,
                            terms: typing.Iterable):
    if not terms:
        return RedirectResponse("/", status_code=HTTPStatus.SEE_OTHER)
    context["unaccepted_terms"] = terms
    return render_template(request, "tos/index.html", context)
Beispiel #8
0
async def pkgbase_flag_post(request: Request,
                            name: str,
                            comments: str = Form(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    if not comments:
        context = templates.make_context(request, "Flag Package Out-Of-Date")
        context["pkgbase"] = pkgbase
        context["errors"] = [
            "The selected packages have not been flagged, "
            "please enter a comment."
        ]
        return render_template(request,
                               "pkgbase/flag.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    has_cred = request.user.has_credential(creds.PKGBASE_FLAG)
    if has_cred and not pkgbase.OutOfDateTS:
        now = time.utcnow()
        with db.begin():
            pkgbase.OutOfDateTS = now
            pkgbase.Flagger = request.user
            pkgbase.FlaggerComment = comments

        notify.FlagNotification(request.user.ID, pkgbase.ID).send()

    return RedirectResponse(f"/pkgbase/{name}",
                            status_code=HTTPStatus.SEE_OTHER)
Beispiel #9
0
async def requests(request: Request,
                   O: int = Query(default=defaults.O),
                   PP: int = Query(default=defaults.PP)):
    context = make_context(request, "Requests")

    context["q"] = dict(request.query_params)

    O, PP = util.sanitize_params(O, PP)
    context["O"] = O
    context["PP"] = PP

    # A PackageRequest query, with left inner joined User and RequestType.
    query = db.query(PackageRequest).join(User,
                                          User.ID == PackageRequest.UsersID)

    # If the request user is not elevated (TU or Dev), then
    # filter PackageRequests which are owned by the request user.
    if not request.user.is_elevated():
        query = query.filter(PackageRequest.UsersID == request.user.ID)

    context["total"] = query.count()
    context["results"] = query.order_by(
        # Order primarily by the Status column being PENDING_ID,
        # and secondarily by RequestTS; both in descending order.
        case([(PackageRequest.Status == PENDING_ID, 1)], else_=0).desc(),
        PackageRequest.RequestTS.desc()).limit(PP).offset(O).all()

    return render_template(request, "requests.html", context)
Beispiel #10
0
async def pkgbase_request(request: Request,
                          name: str,
                          next: str = Query(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)
    context = await make_variable_context(request, "Submit Request")
    context["pkgbase"] = pkgbase
    context["next"] = next or f"/pkgbase/{name}"
    return render_template(request, "pkgbase/request.html", context)
Beispiel #11
0
async def pkgbase_flag_comment(request: Request, name: str):
    pkgbase = get_pkg_or_base(name, PackageBase)

    if pkgbase.OutOfDateTS is None:
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Flag Comment")
    context["pkgbase"] = pkgbase
    return render_template(request, "pkgbase/flag-comment.html", context)
Beispiel #12
0
async def request_close(request: Request, id: int):

    pkgreq = get_pkgreq_by_id(id)
    if not request.user.is_elevated() and request.user != pkgreq.User:
        # Request user doesn't have permission here: redirect to '/'.
        return RedirectResponse("/", status_code=HTTPStatus.SEE_OTHER)

    context = make_context(request, "Close Request")
    context["pkgreq"] = pkgreq
    return render_template(request, "requests/close.html", context)
Beispiel #13
0
async def pkgbase_delete_get(request: Request,
                             name: str,
                             next: str = Query(default=str())):
    if not request.user.has_credential(creds.PKGBASE_DELETE):
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Package Deletion")
    context["pkgbase"] = get_pkg_or_base(name, PackageBase)
    context["next"] = next or "/packages"
    return render_template(request, "pkgbase/delete.html", context)
Beispiel #14
0
async def pkgbase_flag_get(request: Request, name: str):
    pkgbase = get_pkg_or_base(name, PackageBase)

    has_cred = request.user.has_credential(creds.PKGBASE_FLAG)
    if not has_cred or pkgbase.OutOfDateTS is not None:
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Flag Package Out-Of-Date")
    context["pkgbase"] = pkgbase
    return render_template(request, "pkgbase/flag.html", context)
Beispiel #15
0
async def account_edit(request: Request, username: str):
    user = db.query(models.User, models.User.Username == username).first()

    response = cannot_edit(request, user)
    if response:
        return response

    context = await make_variable_context(request, "Accounts")
    context["user"] = db.refresh(user)

    context = make_account_form_context(context, request, user, dict())
    return render_template(request, "account/edit.html", context)
Beispiel #16
0
async def pkgbase_disown_post(request: Request,
                              name: str,
                              comments: str = Form(default=str()),
                              confirm: bool = Form(default=False),
                              next: str = Form(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    has_cred = request.user.has_credential(creds.PKGBASE_DISOWN,
                                           approved=[pkgbase.Maintainer])
    if not has_cred:
        return RedirectResponse(f"/pkgbase/{name}", HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Disown Package")
    context["pkgbase"] = pkgbase
    if not confirm:
        context["errors"] = [("The selected packages have not been disowned, "
                              "check the confirmation checkbox.")]
        return render_template(request,
                               "pkgbase/disown.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    with db.begin():
        update_closure_comment(pkgbase, ORPHAN_ID, comments)

    try:
        actions.pkgbase_disown_instance(request, pkgbase)
    except InvariantError as exc:
        context["errors"] = [str(exc)]
        return render_template(request,
                               "pkgbase/disown.html",
                               context,
                               status_code=HTTPStatus.BAD_REQUEST)

    if not next:
        next = f"/pkgbase/{name}"

    return RedirectResponse(next, status_code=HTTPStatus.SEE_OTHER)
Beispiel #17
0
async def pkgbase_disown_get(request: Request,
                             name: str,
                             next: str = Query(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    has_cred = request.user.has_credential(creds.PKGBASE_DISOWN,
                                           approved=[pkgbase.Maintainer])
    if not has_cred:
        return RedirectResponse(f"/pkgbase/{name}", HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Disown Package")
    context["pkgbase"] = pkgbase
    context["next"] = next or "/pkgbase/{name}"
    return render_template(request, "pkgbase/disown.html", context)
Beispiel #18
0
async def trusted_user_addvote(request: Request,
                               user: str = str(),
                               type: str = "add_tu",
                               agenda: str = str()):
    if not request.user.has_credential(creds.TU_ADD_VOTE):
        return RedirectResponse("/tu", status_code=HTTPStatus.SEE_OTHER)

    context = await make_variable_context(request, "Add Proposal")

    if type not in ADDVOTE_SPECIFICS:
        context["error"] = "Invalid type."
        type = "add_tu"  # Default it.

    context["user"] = user
    context["type"] = type
    context["agenda"] = agenda

    return render_template(request, "addvote.html", context)
Beispiel #19
0
async def pkgbase_voters(request: Request, name: str) -> Response:
    """
    View of package base voters.

    Requires `request.user` has creds.PKGBASE_LIST_VOTERS credential.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :return: HTMLResponse
    """
    # Get the PackageBase.
    pkgbase = get_pkg_or_base(name, PackageBase)

    if not request.user.has_credential(creds.PKGBASE_LIST_VOTERS):
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    context = templates.make_context(request, "Voters")
    context["pkgbase"] = pkgbase
    return render_template(request, "pkgbase/voters.html", context)
Beispiel #20
0
async def pkgbase_comaintainers(request: Request, name: str) -> Response:
    # Get the PackageBase.
    pkgbase = get_pkg_or_base(name, PackageBase)

    # Unauthorized users (Non-TU/Dev and not the pkgbase maintainer)
    # get redirected to the package base's page.
    has_creds = request.user.has_credential(creds.PKGBASE_EDIT_COMAINTAINERS,
                                            approved=[pkgbase.Maintainer])
    if not has_creds:
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    # Add our base information.
    context = templates.make_context(request, "Manage Co-maintainers")
    context.update({
        "pkgbase":
        pkgbase,
        "comaintainers": [c.User.Username for c in pkgbase.comaintainers]
    })

    return render_template(request, "pkgbase/comaintainers.html", context)
Beispiel #21
0
async def pkgbase_comment_edit(request: Request,
                               name: str,
                               id: int,
                               next: str = Form(default=None)):
    """
    Render the non-javascript edit form.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :param id: PackageComment.ID
    :param next: Optional `next` parameter used in the POST request
    :return: HTMLResponse
    """
    pkgbase = get_pkg_or_base(name, PackageBase)
    comment = get_pkgbase_comment(pkgbase, id)

    if not next:
        next = f"/pkgbase/{name}"

    context = await make_variable_context(request, "Edit comment", next=next)
    context["comment"] = comment
    return render_template(request, "pkgbase/comments/edit.html", context)
Beispiel #22
0
async def account_register(request: Request,
                           U: str = Form(default=str()),    # Username
                           E: str = Form(default=str()),    # Email
                           H: str = Form(default=False),    # Hide Email
                           BE: str = Form(default=None),    # Backup Email
                           R: str = Form(default=None),     # Real Name
                           HP: str = Form(default=None),    # Homepage
                           I: str = Form(default=None),     # IRC Nick
                           K: str = Form(default=None),     # PGP Key FP
                           L: str = Form(default=aurweb.config.get(
                               "options", "default_lang")),
                           TZ: str = Form(default=aurweb.config.get(
                               "options", "default_timezone")),
                           PK: str = Form(default=None),
                           CN: bool = Form(default=False),  # Comment Notify
                           CU: bool = Form(default=False),  # Update Notify
                           CO: bool = Form(default=False),  # Owner Notify
                           captcha: str = Form(default=str())):
    context = await make_variable_context(request, "Register")
    context["captcha_salt"] = get_captcha_salts()[0]
    context = make_account_form_context(context, request, None, dict())
    return render_template(request, "register.html", context)
Beispiel #23
0
async def pkgbase_comaintainers_post(request: Request, name: str,
                                     users: str = Form(default=str())) \
        -> Response:
    # Get the PackageBase.
    pkgbase = get_pkg_or_base(name, PackageBase)

    # Unauthorized users (Non-TU/Dev and not the pkgbase maintainer)
    # get redirected to the package base's page.
    has_creds = request.user.has_credential(creds.PKGBASE_EDIT_COMAINTAINERS,
                                            approved=[pkgbase.Maintainer])
    if not has_creds:
        return RedirectResponse(f"/pkgbase/{name}",
                                status_code=HTTPStatus.SEE_OTHER)

    users = {e.strip() for e in users.split("\n") if bool(e.strip())}
    records = {c.User.Username for c in pkgbase.comaintainers}

    users_to_rm = records.difference(users)
    pkgbaseutil.remove_comaintainers(pkgbase, users_to_rm)
    logger.debug(f"{request.user} removed comaintainers from "
                 f"{pkgbase.Name}: {users_to_rm}")

    users_to_add = users.difference(records)
    error = pkgbaseutil.add_comaintainers(request, pkgbase, users_to_add)
    if error:
        context = templates.make_context(request, "Manage Co-maintainers")
        context["pkgbase"] = pkgbase
        context["comaintainers"] = [
            c.User.Username for c in pkgbase.comaintainers
        ]
        context["errors"] = [error]
        return render_template(request, "pkgbase/comaintainers.html", context)

    logger.debug(f"{request.user} added comaintainers to "
                 f"{pkgbase.Name}: {users_to_add}")

    return RedirectResponse(f"/pkgbase/{pkgbase.Name}",
                            status_code=HTTPStatus.SEE_OTHER)
Beispiel #24
0
async def pkgbase_merge_get(request: Request,
                            name: str,
                            into: str = Query(default=str()),
                            next: str = Query(default=str())):
    pkgbase = get_pkg_or_base(name, PackageBase)

    context = templates.make_context(request, "Package Merging")
    context.update({"pkgbase": pkgbase, "into": into, "next": next})

    status_code = HTTPStatus.OK
    # TODO: Lookup errors from credential instead of hardcoding them.
    # Idea: Something like credential_errors(creds.PKGBASE_MERGE).
    # Perhaps additionally: bad_credential_status_code(creds.PKGBASE_MERGE).
    # Don't take these examples verbatim. We should find good naming.
    if not request.user.has_credential(creds.PKGBASE_MERGE):
        context["errors"] = [
            "Only Trusted Users and Developers can merge packages."
        ]
        status_code = HTTPStatus.UNAUTHORIZED

    return render_template(request,
                           "pkgbase/merge.html",
                           context,
                           status_code=status_code)
Beispiel #25
0
async def pkgbase(request: Request, name: str) -> Response:
    """
    Single package base view.

    :param request: FastAPI Request
    :param name: PackageBase.Name
    :return: HTMLResponse
    """
    # Get the PackageBase.
    pkgbase = get_pkg_or_base(name, PackageBase)

    # Redirect to /packages if there's only one related Package
    # and its name matches its PackageBase.
    packages = pkgbase.packages.all()
    pkg = packages[0]
    if len(packages) == 1 and pkg.Name == pkgbase.Name:
        return RedirectResponse(f"/packages/{pkg.Name}",
                                status_code=int(HTTPStatus.SEE_OTHER))

    # Add our base information.
    context = pkgbaseutil.make_context(request, pkgbase)
    context["packages"] = packages

    return render_template(request, "pkgbase/index.html", context)
Beispiel #26
0
async def internal_server_error(request: Request, exc: Exception) -> Response:
    """
    Catch all uncaught Exceptions thrown in a route.

    :param request: FastAPI Request
    :return: Rendered 500.html template with status_code 500
    """
    repo = aurweb.config.get("notifications", "gitlab-instance")
    project = aurweb.config.get("notifications", "error-project")
    token = aurweb.config.get("notifications", "error-token")

    context = make_context(request, "Internal Server Error")

    # Print out the exception via `traceback` and store the value
    # into the `traceback` context variable.
    tb_io = io.StringIO()
    traceback.print_exc(file=tb_io)
    tb = tb_io.getvalue()
    context["traceback"] = tb

    # Produce a SHA1 hash of the traceback string.
    tb_hash = hashlib.sha1(tb.encode()).hexdigest()
    tb_id = tb_hash[:7]

    redis = redis_connection()
    key = f"tb:{tb_hash}"
    retval = redis.get(key)
    if not retval:
        # Expire in one hour; this is just done to make sure we
        # don't infinitely store these values, but reduce the number
        # of automated reports (notification below). At this time of
        # writing, unexpected exceptions are not common, thus this
        # will not produce a large memory footprint in redis.
        pipe = redis.pipeline()
        pipe.set(key, tb)
        pipe.expire(key, 86400)  # One day.
        pipe.execute()

        # Send out notification about it.
        if "set-me" not in (project, token):
            proj = quote_plus(project)
            endp = f"{repo}/api/v4/projects/{proj}/issues"

            base = f"{request.url.scheme}://{request.url.netloc}"
            title = f"Traceback [{tb_id}]: {base}{request.url.path}"
            desc = [
                "DISCLAIMER",
                "----------",
                "**This issue is confidential** and should be sanitized "
                "before sharing with users or developers. Please ensure "
                "you've completed the following tasks:",
                "- [ ] I have removed any sensitive data and "
                "the description history.",
                "",
                "Exception Details",
                "-----------------",
                f"- Route: `{request.url.path}`",
                f"- User: `{request.user.Username}`",
                f"- Email: `{request.user.Email}`",
            ]

            # Add method-specific information to the description.
            if request.method.lower() == "get":
                # get
                if request.url.query:
                    desc = desc + [f"- Query: `{request.url.query}`"]
                desc += ["", f"```{tb}```"]
            else:
                # post
                form_data = str(dict(request.state.form_data))
                desc = desc + [f"- Data: `{form_data}`"] + ["", f"```{tb}```"]

            headers = {"Authorization": f"Bearer {token}"}
            data = {
                "title": title,
                "description": "\n".join(desc),
                "labels": ["triage"],
                "confidential": True,
            }
            logger.info(endp)
            resp = requests.post(endp, json=data, headers=headers)
            if resp.status_code != http.HTTPStatus.CREATED:
                logger.error(
                    f"Unable to report exception to {repo}: {resp.text}")
        else:
            logger.warning("Unable to report an exception found due to "
                           "unset notifications.error-{{project,token}}")

        # Log details about the exception traceback.
        logger.error(f"FATAL[{tb_id}]: An unexpected exception has occurred.")
        logger.error(tb)
    else:
        retval = retval.decode()

    return render_template(request,
                           "errors/500.html",
                           context,
                           status_code=http.HTTPStatus.INTERNAL_SERVER_ERROR)
Beispiel #27
0
async def accounts_post(request: Request,
                        O: int = Form(default=0),  # Offset
                        SB: str = Form(default=str()),  # Sort By
                        U: str = Form(default=str()),  # Username
                        T: str = Form(default=str()),  # Account Type
                        S: bool = Form(default=False),  # Suspended
                        E: str = Form(default=str()),  # Email
                        R: str = Form(default=str()),  # Real Name
                        I: str = Form(default=str()),  # IRC Nick
                        K: str = Form(default=str())):  # PGP Key
    context = await make_variable_context(request, "Accounts")
    context["pp"] = pp = 50  # Hits per page.

    offset = max(O, 0)  # Minimize offset at 0.
    context["offset"] = offset  # Offset.

    context["params"] = dict(await request.form())
    if "O" in context["params"]:
        context["params"].pop("O")

    # Setup order by criteria based on SB.
    order_by_columns = {
        "t": (models.AccountType.ID.asc(), models.User.Username.asc()),
        "r": (models.User.RealName.asc(), models.AccountType.ID.asc()),
        "i": (models.User.IRCNick.asc(), models.AccountType.ID.asc()),
    }
    default_order = (models.User.Username.asc(), models.AccountType.ID.asc())
    order_by = order_by_columns.get(SB, default_order)

    # Convert parameter T to an AccountType ID.
    account_types = {
        "u": at.USER_ID,
        "t": at.TRUSTED_USER_ID,
        "d": at.DEVELOPER_ID,
        "td": at.TRUSTED_USER_AND_DEV_ID
    }
    account_type_id = account_types.get(T, None)

    # Get a query handle to users, populate the total user
    # count into a jinja2 context variable.
    query = db.query(models.User).join(models.AccountType)

    # Populate this list with any additional statements to
    # be ANDed together.
    statements = [
        v for k, v in [
            (account_type_id is not None, models.AccountType.ID == account_type_id),
            (bool(U), models.User.Username.like(f"%{U}%")),
            (bool(S), models.User.Suspended == S),
            (bool(E), models.User.Email.like(f"%{E}%")),
            (bool(R), models.User.RealName.like(f"%{R}%")),
            (bool(I), models.User.IRCNick.like(f"%{I}%")),
            (bool(K), models.User.PGPKey.like(f"%{K}%")),
        ] if k
    ]

    # Filter the query by coe-mbining all statements added above into
    # an AND statement, unless there's just one statement, which
    # we pass on to filter() as args.
    if statements:
        query = query.filter(and_(*statements))

    context["total_users"] = query.count()

    # Finally, order and truncate our users for the current page.
    users = query.order_by(*order_by).limit(pp).offset(offset).all()
    context["users"] = util.apply_all(users, db.refresh)

    return render_template(request, "account/index.html", context)
Beispiel #28
0
async def accounts(request: Request):
    context = make_context(request, "Accounts")
    return render_template(request, "account/search.html", context)
Beispiel #29
0
async def passreset_post(request: Request,
                         user: str = Form(...),
                         resetkey: str = Form(default=None),
                         password: str = Form(default=None),
                         confirm: str = Form(default=None)):
    context = await make_variable_context(request, "Password Reset")

    # The user parameter being required, we can match against
    criteria = or_(models.User.Username == user, models.User.Email == user)
    db_user = db.query(models.User,
                       and_(criteria, models.User.Suspended == 0)).first()
    if db_user is None:
        context["errors"] = ["Invalid e-mail."]
        return render_template(request, "passreset.html", context,
                               status_code=HTTPStatus.NOT_FOUND)

    db.refresh(db_user)
    if resetkey:
        context["resetkey"] = resetkey

        if not db_user.ResetKey or resetkey != db_user.ResetKey:
            context["errors"] = ["Invalid e-mail."]
            return render_template(request, "passreset.html", context,
                                   status_code=HTTPStatus.NOT_FOUND)

        if not user or not password:
            context["errors"] = ["Missing a required field."]
            return render_template(request, "passreset.html", context,
                                   status_code=HTTPStatus.BAD_REQUEST)

        if password != confirm:
            # If the provided password does not match the provided confirm.
            context["errors"] = ["Password fields do not match."]
            return render_template(request, "passreset.html", context,
                                   status_code=HTTPStatus.BAD_REQUEST)

        if len(password) < models.User.minimum_passwd_length():
            # Translate the error here, which simplifies error output
            # in the jinja2 template.
            _ = get_translator_for_request(request)
            context["errors"] = [_(
                "Your password must be at least %s characters.") % (
                str(models.User.minimum_passwd_length()))]
            return render_template(request, "passreset.html", context,
                                   status_code=HTTPStatus.BAD_REQUEST)

        # We got to this point; everything matched up. Update the password
        # and remove the ResetKey.
        with db.begin():
            db_user.ResetKey = str()
            if db_user.session:
                db.delete(db_user.session)
            db_user.update_password(password)

        # Render ?step=complete.
        return RedirectResponse(url="/passreset?step=complete",
                                status_code=HTTPStatus.SEE_OTHER)

    # If we got here, we continue with issuing a resetkey for the user.
    resetkey = generate_resetkey()
    with db.begin():
        db_user.ResetKey = resetkey

    ResetKeyNotification(db_user.ID).send()

    # Render ?step=confirm.
    return RedirectResponse(url="/passreset?step=confirm",
                            status_code=HTTPStatus.SEE_OTHER)
Beispiel #30
0
async def account_edit_post(request: Request,
                            username: str,
                            U: str = Form(default=str()),  # Username
                            J: bool = Form(default=False),
                            E: str = Form(default=str()),  # Email
                            H: str = Form(default=False),    # Hide Email
                            BE: str = Form(default=None),    # Backup Email
                            R: str = Form(default=None),     # Real Name
                            HP: str = Form(default=None),    # Homepage
                            I: str = Form(default=None),     # IRC Nick
                            K: str = Form(default=None),     # PGP Key
                            L: str = Form(aurweb.config.get(
                                "options", "default_lang")),
                            TZ: str = Form(aurweb.config.get(
                                "options", "default_timezone")),
                            P: str = Form(default=str()),    # New Password
                            C: str = Form(default=None),     # Password Confirm
                            PK: str = Form(default=None),    # PubKey
                            CN: bool = Form(default=False),  # Comment Notify
                            UN: bool = Form(default=False),  # Update Notify
                            ON: bool = Form(default=False),  # Owner Notify
                            T: int = Form(default=None),
                            passwd: str = Form(default=str())):
    user = db.query(models.User).filter(
        models.User.Username == username).first()
    response = cannot_edit(request, user)
    if response:
        return response

    context = await make_variable_context(request, "Accounts")
    context["user"] = db.refresh(user)

    args = dict(await request.form())
    args["K"] = args.get("K", str()).replace(" ", "")

    context = make_account_form_context(context, request, user, args)
    ok, errors = process_account_form(request, user, args)

    if PK:
        context["ssh_pks"] = [PK]

    if not passwd:
        context["errors"] = ["Invalid password."]
        return render_template(request, "account/edit.html", context,
                               status_code=HTTPStatus.BAD_REQUEST)

    if not ok:
        context["errors"] = errors
        return render_template(request, "account/edit.html", context,
                               status_code=HTTPStatus.BAD_REQUEST)

    updates = [
        update.simple,
        update.language,
        update.timezone,
        update.ssh_pubkey,
        update.account_type,
        update.password
    ]

    for f in updates:
        f(**args, request=request, user=user, context=context)

    if not errors:
        context["complete"] = True

    # Update cookies with requests, in case they were changed.
    response = render_template(request, "account/edit.html", context)
    return cookies.update_response_cookies(request, response,
                                           aurtz=TZ, aurlang=L)