async def packages_unnotify(request: Request, package_ids: List[int] = [], **kwargs): if not package_ids: # TODO: This error does not yet have a translation. return (False, ["You did not select any packages for notification removal."]) # TODO: This error does not yet have a translation. error_tuple = (False, [ "A package you selected does not have notifications enabled." ]) bases = set() package_ids = set(package_ids) packages = db.query(models.Package).filter( models.Package.ID.in_(package_ids)).all() for pkg in packages: if pkg.PackageBase not in bases: bases.update({pkg.PackageBase}) # Perform some checks on what the user selected for notify. for pkgbase in bases: notif = db.query( pkgbase.notifications.filter(models.PackageNotification.UserID == request.user.ID).exists()).scalar() if not notif: return error_tuple for pkgbase in bases: pkgbase_actions.pkgbase_unnotify_instance(request, pkgbase) # TODO: This message does not yet have a translation. return (True, ["The selected packages' notifications have been removed."])
def __init__(self, uid, reqid, reqtype, pkgbase_id, merge_into=None): self._user = db.query( User.Username).filter(User.ID == uid).first().Username self._pkgbase = db.query( PackageBase.Name).filter(PackageBase.ID == pkgbase_id).first().Name self._to = aurweb.config.get('options', 'aur_request_ml') query = db.query(PackageRequest).join(PackageBase).join( PackageComaintainer, PackageComaintainer.PackageBaseID == PackageRequest.PackageBaseID, isouter=True).join( User, or_(User.ID == PackageRequest.UsersID, User.ID == PackageBase.MaintainerUID, User.ID == PackageComaintainer.UsersID)).filter( and_(PackageRequest.ID == reqid, User.Suspended == 0)).with_entities( User.Email).distinct() self._cc = [u.Email for u in query] pkgreq = db.query(PackageRequest.Comments).filter( PackageRequest.ID == reqid).first() self._text = pkgreq.Comments self._reqid = int(reqid) self._reqtype = reqtype self._merge_into = merge_into
def test_aurblup(alpm_db: AlpmDatabase): # Test that we can add a package. alpm_db.add("pkg", "1.0", "x86_64", provides=["pkg2", "pkg3"]) alpm_db.add("pkg2", "2.0", "x86_64") aurblup.main() # Test that the package got added to the database. for name in ("pkg", "pkg2"): pkg = db.query(OfficialProvider).filter( OfficialProvider.Name == name).first() assert pkg is not None # Test that we can remove the package. alpm_db.remove("pkg") # Run aurblup again with forced repository update. aurblup.main(True) # Expect that the database got updated accordingly. pkg = db.query(OfficialProvider).filter( OfficialProvider.Name == "pkg").first() assert pkg is None pkg2 = db.query(OfficialProvider).filter( OfficialProvider.Name == "pkg2").first() assert pkg2 is not None
async def authenticate(self, conn: HTTPConnection): unauthenticated = (None, AnonymousUser()) sid = conn.cookies.get("AURSID") if not sid: return unauthenticated timeout = aurweb.config.getint("options", "login_timeout") remembered = ("AURREMEMBER" in conn.cookies and bool(conn.cookies.get("AURREMEMBER"))) if remembered: timeout = aurweb.config.getint("options", "persistent_cookie_timeout") # If no session with sid and a LastUpdateTS now or later exists. now_ts = time.utcnow() record = db.query(Session).filter(Session.SessionID == sid).first() if not record: return unauthenticated elif record.LastUpdateTS < (now_ts - timeout): with db.begin(): db.delete_all([record]) return unauthenticated # At this point, we cannot have an invalid user if the record # exists, due to ForeignKey constraints in the schema upheld # by mysqlclient. with db.begin(): user = db.query(User).filter(User.ID == record.UsersID).first() user.nonce = util.make_nonce() user.authenticated = True return (AuthCredentials(["authenticated"]), user)
def __init__(self, uid, reqid, reason): user = db.query(User.Username).filter(User.ID == uid).first() self._user = user.Username if user else None self._to = aurweb.config.get('options', 'aur_request_ml') query = db.query(PackageRequest).join(PackageBase).join( PackageComaintainer, PackageComaintainer.PackageBaseID == PackageRequest.PackageBaseID, isouter=True).join( User, or_(User.ID == PackageRequest.UsersID, User.ID == PackageBase.MaintainerUID, User.ID == PackageComaintainer.UsersID)).filter( and_(PackageRequest.ID == reqid, User.Suspended == 0)).with_entities( User.Email).distinct() self._cc = [u.Email for u in query] pkgreq = db.query(PackageRequest).join(RequestType).filter( PackageRequest.ID == reqid).with_entities( PackageRequest.ClosureComment, RequestType.Name, PackageRequest.PackageBaseName).first() self._text = pkgreq.ClosureComment self._reqtype = pkgreq.Name self._pkgbase = pkgreq.PackageBaseName self._reqid = int(reqid) self._reason = reason
async def trusted_user_proposal(request: Request, proposal: int): if not request.user.has_credential(creds.TU_LIST_VOTES): return RedirectResponse("/tu", status_code=HTTPStatus.SEE_OTHER) context = await make_variable_context(request, "Trusted User") proposal = int(proposal) voteinfo = db.query( models.TUVoteInfo).filter(models.TUVoteInfo.ID == proposal).first() if not voteinfo: raise HTTPException(status_code=HTTPStatus.NOT_FOUND) voters = db.query(models.User).join( models.TUVote).filter(models.TUVote.VoteID == voteinfo.ID) vote = db.query(models.TUVote).filter( and_(models.TUVote.UserID == request.user.ID, models.TUVote.VoteID == voteinfo.ID)).first() if not request.user.has_credential(creds.TU_VOTE): context["error"] = "Only Trusted Users are allowed to vote." if voteinfo.User == request.user.Username: context["error"] = "You cannot vote in an proposal about you." elif vote is not None: context["error"] = "You've already voted for this proposal." context["vote"] = vote return render_proposal(request, context, proposal, voteinfo, voters, vote)
def is_banned(request: Request = None, **kwargs) -> None: host = request.client.host exists = db.query(models.Ban, models.Ban.IPAddress == host).exists() if db.query(exists).scalar(): raise ValidationError([ "Account registration has been disabled for your " "IP address, probably due to sustained spam attacks. " "Sorry for the inconvenience." ])
def test_request_type_name_display(): deletion = db.query(RequestType, RequestType.ID == DELETION_ID).first() assert deletion.name_display() == "Deletion" orphan = db.query(RequestType, RequestType.ID == ORPHAN_ID).first() assert orphan.name_display() == "Orphan" merge = db.query(RequestType, RequestType.ID == MERGE_ID).first() assert merge.name_display() == "Merge"
def pkgname_link(pkgname: str) -> str: record = db.query(Package).filter(Package.Name == pkgname).exists() if db.query(record).scalar(): return f"/packages/{pkgname}" official = db.query(OfficialProvider).filter( OfficialProvider.Name == pkgname).exists() if db.query(official).scalar(): base = "/".join([OFFICIAL_BASE, "packages"]) return f"{base}/?q={pkgname}"
def __init__(self, vote_id): self._vote_id = int(vote_id) subquery = db.query(TUVote.UserID).filter(TUVote.VoteID == vote_id) query = db.query(User).filter( and_(User.AccountTypeID.in_((2, 4)), ~User.ID.in_(subquery), User.Suspended == 0)).with_entities(User.Email, User.LangPreference) self._recipients = [(u.Email, u.LangPreference) for u in query] super().__init__()
def email_in_use(E: str = str(), user: models.User = None, _: l10n.Translator = None, **kwargs) -> None: exists = db.query(models.User).filter( and_(models.User.ID != user.ID, models.User.Email == E)).exists() if db.query(exists).scalar(): # If the email already exists... raise ValidationError([ _("The address, %s%s%s, is already in use.") % ("<strong>", E, "</strong>") ])
def username_in_use(U: str = str(), user: models.User = None, _: l10n.Translator = None, **kwargs) -> None: exists = db.query(models.User).filter( and_(models.User.ID != user.ID, models.User.Username == U)).exists() if db.query(exists).scalar(): # If the username already exists... raise ValidationError([ _("The username, %s%s%s, is already in use.") % ("<strong>", U, "</strong>") ])
def __init__(self, uid, pkgbase_id): self._pkgbase = db.query( PackageBase.Name).filter(PackageBase.ID == pkgbase_id).first().Name user = db.query(User).filter(User.ID == uid).with_entities( User.Email, User.LangPreference).first() self._to = user.Email self._lang = user.LangPreference super().__init__()
def test_create_delete(): with db.begin(): account_type = db.create(AccountType, AccountType="test") record = db.query(AccountType, AccountType.AccountType == "test").first() assert record is not None with db.begin(): db.delete(account_type) record = db.query(AccountType, AccountType.AccountType == "test").first() assert record is None
async def trusted_user_proposal_post(request: Request, proposal: int, decision: str = Form(...)): if not request.user.has_credential(creds.TU_LIST_VOTES): return RedirectResponse("/tu", status_code=HTTPStatus.SEE_OTHER) context = await make_variable_context(request, "Trusted User") proposal = int(proposal) # Make sure it's an int. voteinfo = db.query( models.TUVoteInfo).filter(models.TUVoteInfo.ID == proposal).first() if not voteinfo: raise HTTPException(status_code=HTTPStatus.NOT_FOUND) voters = db.query(models.User).join( models.TUVote).filter(models.TUVote.VoteID == voteinfo.ID) vote = db.query(models.TUVote).filter( and_(models.TUVote.UserID == request.user.ID, models.TUVote.VoteID == voteinfo.ID)).first() status_code = HTTPStatus.OK if not request.user.has_credential(creds.TU_VOTE): context["error"] = "Only Trusted Users are allowed to vote." status_code = HTTPStatus.UNAUTHORIZED elif voteinfo.User == request.user.Username: context["error"] = "You cannot vote in an proposal about you." status_code = HTTPStatus.BAD_REQUEST elif vote is not None: context["error"] = "You've already voted for this proposal." status_code = HTTPStatus.BAD_REQUEST if status_code != HTTPStatus.OK: return render_proposal(request, context, proposal, voteinfo, voters, vote, status_code=status_code) if decision in {"Yes", "No", "Abstain"}: # Increment whichever decision was given to us. setattr(voteinfo, decision, getattr(voteinfo, decision) + 1) else: return Response("Invalid 'decision' value.", status_code=HTTPStatus.BAD_REQUEST) with db.begin(): vote = db.create(models.TUVote, User=request.user, VoteInfo=voteinfo) context["error"] = "You've already voted for this proposal." return render_proposal(request, context, proposal, voteinfo, voters, vote)
def test_relation_types(): conflicts = db.query(RelationType, RelationType.Name == "conflicts").first() assert conflicts is not None assert conflicts.Name == "conflicts" provides = db.query(RelationType, RelationType.Name == "provides").first() assert provides is not None assert provides.Name == "provides" replaces = db.query(RelationType, RelationType.Name == "replaces").first() assert replaces is not None assert replaces.Name == "replaces"
def get_extended_fields(): subqueries = [ # PackageDependency db.query(models.PackageDependency ).join(models.DependencyType).with_entities( models.PackageDependency.PackageID.label("ID"), models.DependencyType.Name.label("Type"), models.PackageDependency.DepName.label("Name"), models.PackageDependency.DepCondition.label("Cond") ).distinct().order_by("Name"), # PackageRelation db.query(models.PackageRelation ).join(models.RelationType).with_entities( models.PackageRelation.PackageID.label("ID"), models.RelationType.Name.label("Type"), models.PackageRelation.RelName.label("Name"), models.PackageRelation.RelCondition.label("Cond") ).distinct().order_by("Name"), # Groups db.query(models.PackageGroup).join( models.Group, models.PackageGroup.GroupID == models.Group.ID).with_entities( models.PackageGroup.PackageID.label("ID"), literal("Groups").label("Type"), models.Group.Name.label("Name"), literal(str()).label("Cond")).distinct().order_by("Name"), # Licenses db.query(models.PackageLicense).join( models.License, models.PackageLicense.LicenseID == models.License.ID).with_entities( models.PackageLicense.PackageID.label("ID"), literal("License").label("Type"), models.License.Name.label("Name"), literal(str()).label("Cond")).distinct().order_by("Name"), # Keywords db.query(models.PackageKeyword).join( models.Package, Package.PackageBaseID == models.PackageKeyword.PackageBaseID).with_entities( models.Package.ID.label("ID"), literal("Keywords").label("Type"), models.PackageKeyword.Keyword.label("Name"), literal(str()).label("Cond")).distinct().order_by("Name") ] query = subqueries[0].union_all(*subqueries[1:]) return get_extended_dict(query)
async def check_terms_of_service(request: Request, call_next: typing.Callable): """ This middleware function redirects authenticated users if they have any outstanding Terms to agree to. """ if request.user.is_authenticated() and request.url.path != "/tos": unaccepted = query(Term).join(AcceptedTerm).filter( or_( AcceptedTerm.UsersID != request.user.ID, and_(AcceptedTerm.UsersID == request.user.ID, AcceptedTerm.TermsID == Term.ID, AcceptedTerm.Revision < Term.Revision))) if query(Term).count() > unaccepted.count(): return RedirectResponse("/tos", status_code=int(http.HTTPStatus.SEE_OTHER)) return await util.error_or_result(call_next, request)
def __init__(self, uid, pkgbase_id): self._user = db.query( User.Username).filter(User.ID == uid).first().Username self._pkgbase = db.query( PackageBase.Name).filter(PackageBase.ID == pkgbase_id).first().Name query = db.query(User).join(PackageNotification).filter( and_(User.UpdateNotify == 1, PackageNotification.UserID != uid, PackageNotification.PackageBaseID == pkgbase_id, User.Suspended == 0)).with_entities( User.Email, User.LangPreference).distinct() self._recipients = [(u.Email, u.LangPreference) for u in query] super().__init__()
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)
def get_captcha_salts(): """ Produce salts based on the current user count. """ count = query(User).count() salts = [] for i in range(0, 6): salts.append(f"aurweb-{count - i}") return salts
def add_comaintainers(request: Request, pkgbase: PackageBase, usernames: List[str]) -> None: """ Add comaintainers to `pkgbase`. :param request: FastAPI request :param pkgbase: PackageBase instance :param usernames: Iterable of username strings :return: Error string on failure else None """ # For each username in usernames, perform validation of the username # and append the User record to `users` if no errors occur. users = [] for username in usernames: user = db.query(User).filter(User.Username == username).first() if not user: _ = l10n.get_translator_for_request(request) return _("Invalid user name: %s") % username users.append(user) notifications = [] def add_comaint(user: User): nonlocal notifications # Populate `notifications` with add_comaintainer's return value, # which is a ComaintainerAddNotification. notifications.append(add_comaintainer(pkgbase, user)) # Move along: add all `users` as new `pkgbase` comaintainers. util.apply_all(users, add_comaint) # Send out notifications. util.apply_all(notifications, lambda n: n.send())
def main(): args = parse_args() db.get_engine() type = db.query(AccountType, AccountType.AccountType == args.type).first() with db.begin(): user = db.create(User, Username=args.username, Email=args.email, Passwd=args.password, RealName=args.realname, IRCNick=args.ircnick, PGPKey=args.pgp_key, AccountType=type) if args.ssh_pubkey: pubkey = args.ssh_pubkey.strip() # Remove host from the pubkey if it's there. pubkey = ' '.join(pubkey.split(' ')[:2]) with db.begin(): db.create(SSHPubKey, User=user, PubKey=pubkey, Fingerprint=get_fingerprint(pubkey)) print(user.json()) return 0
def test_request_post_deletion_as_maintainer(client: TestClient, auser: User, pkgbase: PackageBase): """ Test the POST route for creating a deletion request as maint works. """ endpoint = f"/pkgbase/{pkgbase.Name}/request" data = {"comments": "Test request.", "type": "deletion"} with client as request: resp = request.post(endpoint, data=data, cookies=auser.cookies) assert resp.status_code == int(HTTPStatus.SEE_OTHER) # Check the pkgreq record got created and accepted. pkgreq = db.query(PackageRequest).first() assert pkgreq is not None assert pkgreq.ReqTypeID == DELETION_ID assert pkgreq.Status == ACCEPTED_ID # Should've gotten two emails. assert Email.count() == 2 # A RequestOpenNotification should've been sent out. email = Email(1) expr = r"^\[PRQ#%d\] Deletion Request for [^ ]+$" % pkgreq.ID assert re.match(expr, email.headers.get("Subject")) # Check the content of the close notification. email = Email(2) expr = r"^\[PRQ#%d\] Deletion Request for [^ ]+ Accepted$" % pkgreq.ID assert re.match(expr, email.headers.get("Subject"))
async def packages_unflag(request: Request, package_ids: List[int] = [], **kwargs): if not package_ids: return (False, ["You did not select any packages to unflag."]) # Holds the set of package bases we're looking to unflag. # Constructed below via looping through the packages query. bases = set() package_ids = set(package_ids) # Convert this to a set for O(1). packages = db.query(models.Package).filter( models.Package.ID.in_(package_ids)).all() for pkg in packages: has_cred = request.user.has_credential( creds.PKGBASE_UNFLAG, approved=[pkg.PackageBase.Flagger]) if not has_cred: return (False, ["You did not select any packages to unflag."]) if pkg.PackageBase not in bases: bases.update({pkg.PackageBase}) for pkgbase in bases: pkgbase_actions.pkgbase_unflag_instance(request, pkgbase) return (True, ["The selected packages have been unflagged."])
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)
def test_tu_proposal_vote(client, proposal): tu_user, user, voteinfo = proposal # Store the current related values. yes = voteinfo.Yes cookies = {"AURSID": tu_user.login(Request(), "testPassword")} with client as request: data = {"decision": "Yes"} response = request.post(f"/tu/{voteinfo.ID}", cookies=cookies, data=data) assert response.status_code == int(HTTPStatus.OK) # Check that the proposal record got updated. assert voteinfo.Yes == yes + 1 # Check that the new TUVote exists. vote = db.query(TUVote, TUVote.VoteInfo == voteinfo, TUVote.User == tu_user).first() assert vote is not None root = parse_root(response.text) # Check that we're told we've voted. status = root.xpath('//span[contains(@class, "status")]/text()')[0] assert status == "You've already voted for this proposal."
def _search_by_comaintainer(self, keywords: str) -> orm.Query: self._join_user() self._join_comaint() user = db.query(User).filter(User.Username == keywords).first() uid = 0 if not user else user.ID self.query = self.query.filter(PackageComaintainer.UsersID == uid) return self
def ssh_pubkey(PK: str = str(), user: models.User = None, **kwargs) -> None: if not PK: # If no pubkey is provided, wipe out any pubkeys the user # has and return out early. with db.begin(): db.delete_all(user.ssh_pub_keys) return # Otherwise, parse ssh keys and their fprints out of PK. keys = util.parse_ssh_keys(PK.strip()) fprints = [get_fingerprint(" ".join(k)) for k in keys] with db.begin(): # Delete any existing keys we can't find. to_remove = user.ssh_pub_keys.filter( ~SSHPubKey.Fingerprint.in_(fprints)) db.delete_all(to_remove) # For each key, if it does not yet exist, create it. for i, full_key in enumerate(keys): prefix, key = full_key exists = user.ssh_pub_keys.filter( SSHPubKey.Fingerprint == fprints[i]).exists() if not db.query(exists).scalar(): # No public key exists, create one. db.create(models.SSHPubKey, UserID=user.ID, PubKey=" ".join([prefix, key]), Fingerprint=fprints[i])
def test_user_login_logout(user: User): """ Test creating a user and reading its columns. """ # Assert that make_user created a valid user. assert bool(user.ID) # Test authentication. assert user.valid_password("testPassword") assert not user.valid_password("badPassword") # Make a raw request. request = Request() assert not user.login(request, "badPassword") assert not user.is_authenticated() sid = user.login(request, "testPassword") assert sid is not None assert user.is_authenticated() # Expect that User session relationships work right. user_session = db.query(Session, Session.UsersID == user.ID).first() assert user_session == user.session assert user.session.SessionID == sid assert user.session.User == user # Search for the user via query API. result = db.query(User, User.ID == user.ID).first() # Compare the result and our original user. assert result == user assert result.ID == user.ID assert result.AccountType.ID == user.AccountType.ID assert result.Username == user.Username assert result.Email == user.Email # Test result authenticate methods to ensure they work the same. assert not result.valid_password("badPassword") assert result.valid_password("testPassword") assert result.is_authenticated() # Test out user string functions. assert repr(user) == f"<User(ID='{user.ID}', " + \ "AccountType='User', Username='******')>" # Test logout. user.logout(request) assert not user.is_authenticated()