示例#1
0
async def packages_post(request: Request,
                        IDs: List[int] = Form(default=[]),
                        action: str = Form(default=str()),
                        confirm: bool = Form(default=False)):

    # If an invalid action is specified, just render GET /packages
    # with an BAD_REQUEST status_code.
    if action not in PACKAGE_ACTIONS:
        context = make_context(request, "Packages")
        return await packages_get(request, context, HTTPStatus.BAD_REQUEST)

    context = make_context(request, "Packages")

    # We deal with `IDs`, `merge_into` and `confirm` arguments
    # within action callbacks.
    callback = PACKAGE_ACTIONS.get(action)
    retval = await callback(request, package_ids=IDs, confirm=confirm)
    if retval:  # If *anything* was returned:
        success, messages = retval
        if not success:
            # If the first element was False:
            context["errors"] = messages
            return await packages_get(request, context, HTTPStatus.BAD_REQUEST)
        else:
            # Otherwise:
            context["success"] = messages

    return await packages_get(request, context)
示例#2
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)
示例#3
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)
示例#4
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)
示例#5
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)
示例#6
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)
示例#7
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)
示例#8
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)
示例#9
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)
示例#10
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)
示例#11
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)
示例#12
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)
示例#13
0
async def index(request: Request):
    """ Homepage route. """
    context = make_context(request, "Home")
    context['ssh_fingerprints'] = util.get_ssh_fingerprints()

    bases = db.query(models.PackageBase)

    redis = aurweb.redis.redis_connection()
    cache_expire = 300  # Five minutes.

    # Package statistics.
    query = bases.filter(models.PackageBase.PackagerUID.isnot(None))
    context["package_count"] = await db_count_cache(redis,
                                                    "package_count",
                                                    query,
                                                    expire=cache_expire)

    query = bases.filter(
        and_(models.PackageBase.MaintainerUID.is_(None),
             models.PackageBase.PackagerUID.isnot(None)))
    context["orphan_count"] = await db_count_cache(redis,
                                                   "orphan_count",
                                                   query,
                                                   expire=cache_expire)

    query = db.query(models.User)
    context["user_count"] = await db_count_cache(redis,
                                                 "user_count",
                                                 query,
                                                 expire=cache_expire)

    query = query.filter(
        or_(models.User.AccountTypeID == TRUSTED_USER_ID,
            models.User.AccountTypeID == TRUSTED_USER_AND_DEV_ID))
    context["trusted_user_count"] = await db_count_cache(redis,
                                                         "trusted_user_count",
                                                         query,
                                                         expire=cache_expire)

    # Current timestamp.
    now = time.utcnow()

    seven_days = 86400 * 7  # Seven days worth of seconds.
    seven_days_ago = now - seven_days

    one_hour = 3600
    updated = bases.filter(
        and_(
            models.PackageBase.ModifiedTS - models.PackageBase.SubmittedTS >=
            one_hour, models.PackageBase.PackagerUID.isnot(None)))

    query = bases.filter(
        and_(models.PackageBase.SubmittedTS >= seven_days_ago,
             models.PackageBase.PackagerUID.isnot(None)))
    context["seven_days_old_added"] = await db_count_cache(
        redis, "seven_days_old_added", query, expire=cache_expire)

    query = updated.filter(models.PackageBase.ModifiedTS >= seven_days_ago)
    context["seven_days_old_updated"] = await db_count_cache(
        redis, "seven_days_old_updated", query, expire=cache_expire)

    year = seven_days * 52  # Fifty two weeks worth: one year.
    year_ago = now - year
    query = updated.filter(models.PackageBase.ModifiedTS >= year_ago)
    context["year_old_updated"] = await db_count_cache(redis,
                                                       "year_old_updated",
                                                       query,
                                                       expire=cache_expire)

    query = bases.filter(
        models.PackageBase.ModifiedTS - models.PackageBase.SubmittedTS < 3600)
    context["never_updated"] = await db_count_cache(redis,
                                                    "never_updated",
                                                    query,
                                                    expire=cache_expire)

    # Get the 15 most recently updated packages.
    context["package_updates"] = updated_packages(15, cache_expire)

    if request.user.is_authenticated():
        # Authenticated users get a few extra pieces of data for
        # the dashboard display.
        packages = db.query(models.Package).join(models.PackageBase)

        maintained = packages.join(
            models.PackageComaintainer,
            models.PackageComaintainer.PackageBaseID == models.PackageBase.ID,
            isouter=True).join(
                models.User,
                or_(models.PackageBase.MaintainerUID == models.User.ID,
                    models.PackageComaintainer.UsersID ==
                    models.User.ID)).filter(models.User.ID == request.user.ID)

        # Packages maintained by the user that have been flagged.
        context["flagged_packages"] = maintained.filter(
            models.PackageBase.OutOfDateTS.isnot(None)).order_by(
                models.PackageBase.ModifiedTS.desc(),
                models.Package.Name.asc()).limit(50).all()

        # Flagged packages that request.user has voted for.
        context["flagged_packages_voted"] = query_voted(
            context.get("flagged_packages"), request.user)

        # Flagged packages that request.user is being notified about.
        context["flagged_packages_notified"] = query_notified(
            context.get("flagged_packages"), request.user)

        archive_time = aurweb.config.getint('options', 'request_archive_time')
        start = now - archive_time

        # Package requests created by request.user.
        context["package_requests"] = request.user.package_requests.filter(
            models.PackageRequest.RequestTS >= start
        ).order_by(
            # Order primarily by the Status column being PENDING_ID,
            # and secondarily by RequestTS; both in descending order.
            case([(models.PackageRequest.Status == PENDING_ID, 1)],
                 else_=0).desc(),
            models.PackageRequest.RequestTS.desc()).limit(50).all()

        # Packages that the request user maintains or comaintains.
        context["packages"] = maintained.filter(
            models.User.ID == models.PackageBase.MaintainerUID).order_by(
                models.PackageBase.ModifiedTS.desc(),
                models.Package.Name.desc()).limit(50).all()

        # Packages that request.user has voted for.
        context["packages_voted"] = query_voted(context.get("packages"),
                                                request.user)

        # Packages that request.user is being notified about.
        context["packages_notified"] = query_notified(context.get("packages"),
                                                      request.user)

        # Any packages that the request user comaintains.
        context["comaintained"] = packages.join(
            models.PackageComaintainer).filter(
                models.PackageComaintainer.UsersID
                == request.user.ID).order_by(
                    models.PackageBase.ModifiedTS.desc(),
                    models.Package.Name.desc()).limit(50).all()

        # Comaintained packages that request.user has voted for.
        context["comaintained_voted"] = query_voted(
            context.get("comaintained"), request.user)

        # Comaintained packages that request.user is being notified about.
        context["comaintained_notified"] = query_notified(
            context.get("comaintained"), request.user)

    return render_template(request, "index.html", context)