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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
async def accounts(request: Request): context = make_context(request, "Accounts") return render_template(request, "account/search.html", context)
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)
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)