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_usermaint(user: User): """ In this case, we first test that only the expired record gets updated, but the non-expired record remains untouched. After, we update the login time on the non-expired record and exercise its code path. """ now = time.utcnow() limit_to = now - 86400 * 7 with db.begin(): user.LastLoginIPAddress = "127.0.0.1" user.LastLogin = limit_to - 666 user.LastSSHLoginIPAddress = "127.0.0.1" user.LastSSHLogin = now - 10 usermaint.main() assert user.LastLoginIPAddress is None assert user.LastSSHLoginIPAddress == "127.0.0.1" with db.begin(): user.LastSSHLogin = limit_to - 666 usermaint.main() assert user.LastLoginIPAddress is None assert user.LastSSHLoginIPAddress is None
def test_dependency_type_creation(): with begin(): dependency_type = create(DependencyType, Name="Test Type") assert bool(dependency_type.ID) assert dependency_type.Name == "Test Type" with begin(): delete(dependency_type)
def test_user_credential_types(user: User): assert user.AccountTypeID in creds.user_developer_or_trusted_user assert user.AccountTypeID not in creds.trusted_user assert user.AccountTypeID not in creds.developer assert user.AccountTypeID not in creds.trusted_user_or_dev with db.begin(): user.AccountTypeID = at.TRUSTED_USER_ID assert user.AccountTypeID in creds.trusted_user assert user.AccountTypeID in creds.trusted_user_or_dev with db.begin(): user.AccountTypeID = at.DEVELOPER_ID assert user.AccountTypeID in creds.developer assert user.AccountTypeID in creds.trusted_user_or_dev with db.begin(): user.AccountTypeID = at.TRUSTED_USER_AND_DEV_ID assert user.AccountTypeID in creds.trusted_user assert user.AccountTypeID in creds.developer assert user.AccountTypeID in creds.trusted_user_or_dev # Some model authorization checks. assert user.is_elevated() assert user.is_trusted_user() assert user.is_developer()
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 pkgbase_disown_instance(request: Request, pkgbase: PackageBase) -> None: disowner = request.user notifs = [notify.DisownNotification(disowner.ID, pkgbase.ID)] is_maint = disowner == pkgbase.Maintainer if is_maint: with db.begin(): # Comaintainer with the lowest Priority value; next-in-line. prio_comaint = pkgbase.comaintainers.order_by( PackageComaintainer.Priority.asc() ).first() if prio_comaint: # If there is such a comaintainer, promote them to maint. pkgbase.Maintainer = prio_comaint.User notifs.append(pkgbaseutil.remove_comaintainer(prio_comaint)) else: # Otherwise, just orphan the package completely. pkgbase.Maintainer = None elif request.user.has_credential(creds.PKGBASE_DISOWN): # Otherwise, the request user performing this disownage is a # Trusted User and we treat it like a standard orphan request. notifs += handle_request(request, ORPHAN_ID, pkgbase) with db.begin(): pkgbase.Maintainer = None db.delete_all(pkgbase.comaintainers) util.apply_all(notifs, lambda n: n.send())
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 test_official_provider_cs(): """ Test case sensitivity of the database table. """ with db.begin(): oprovider = db.create(OfficialProvider, Name="some-name", Repo="some-repo", Provides="some-provides") assert bool(oprovider.ID) with db.begin(): oprovider_cs = db.create(OfficialProvider, Name="SOME-NAME", Repo="SOME-REPO", Provides="SOME-PROVIDES") assert bool(oprovider_cs.ID) assert oprovider.ID != oprovider_cs.ID assert oprovider.Name == "some-name" assert oprovider.Repo == "some-repo" assert oprovider.Provides == "some-provides" assert oprovider_cs.Name == "SOME-NAME" assert oprovider_cs.Repo == "SOME-REPO" assert oprovider_cs.Provides == "SOME-PROVIDES"
def account_type() -> AccountType: with db.begin(): account_type_ = db.create(AccountType, AccountType="TestUser") yield account_type_ with db.begin(): db.delete(account_type_)
def test_request_type_creation(): with db.begin(): request_type = db.create(RequestType, Name="Test Request") assert bool(request_type.ID) assert request_type.Name == "Test Request" with db.begin(): db.delete(request_type)
def test_relation_type_creation(): with db.begin(): relation_type = db.create(RelationType, Name="test-relation") assert bool(relation_type.ID) assert relation_type.Name == "test-relation" with db.begin(): db.delete(relation_type)
def test_user_is_developer(user: User): with db.begin(): user.AccountTypeID = at.DEVELOPER_ID assert user.is_developer() is True # Do it again with the combined role. with db.begin(): user.AccountTypeID = at.TRUSTED_USER_AND_DEV_ID assert user.is_developer() is True
def test_request_type_null_name_returns_empty_string(): with db.begin(): request_type = db.create(RequestType) assert bool(request_type.ID) assert request_type.Name == str() with db.begin(): db.delete(request_type)
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
def test_session_cs(): """ Test case sensitivity of the database table. """ with db.begin(): user2 = db.create(User, Username="******", Email="*****@*****.**", ResetKey="testReset2", Passwd="testPassword", AccountTypeID=USER_ID) with db.begin(): session_cs = db.create(Session, User=user2, SessionID="TESTSESSION", LastUpdateTS=time.utcnow()) assert session_cs.SessionID == "TESTSESSION" assert session_cs.SessionID != "testSession"
def test_merge_request(client: TestClient, user: User, tu_user: User, pkgbase: PackageBase, target: PackageBase, pkgreq: PackageRequest): """ Test merging a package with a pre - existing request. """ with db.begin(): pkgreq.ReqTypeID = MERGE_ID pkgreq.MergeBaseName = target.Name other_target = create_pkgbase(user, "other-target") other_request = create_request(MERGE_ID, user, pkgbase, "Other request.") other_target2 = create_pkgbase(user, "other-target2") other_request2 = create_request(MERGE_ID, user, pkgbase, "Other request2.") with db.begin(): other_request.MergeBaseName = other_target.Name other_request2.MergeBaseName = other_target2.Name # `pkgreq`.ReqTypeID is already DELETION_ID. endpoint = f"/pkgbase/{pkgbase.Name}/merge" comments = "Test merge closure." data = {"into": target.Name, "comments": comments, "confirm": True} with client as request: resp = request.post(endpoint, data=data, cookies=tu_user.cookies) assert resp.status_code == int(HTTPStatus.SEE_OTHER) assert resp.headers.get("location") == f"/pkgbase/{target.Name}" # Ensure that `pkgreq`.ClosureComment was left alone when specified. assert pkgreq.ClosureComment == comments # We should've gotten 3 emails: an accepting and two rejections. assert Email.count() == 3 # Assert specific IDs match up in the subjects. accepted = Email(1).parse() subj = r"^\[PRQ#%d\] Merge Request for [^ ]+ Accepted$" % pkgreq.ID assert re.match(subj, accepted.headers.get("Subject")) # In the accepted case, we already supplied a closure comment, # which stops one from being autogenerated by the algorithm. assert "[Autogenerated]" not in accepted.body # Test rejection emails, which do have autogenerated closures. rejected = Email(2).parse() subj = r"^\[PRQ#%d\] Merge Request for [^ ]+ Rejected$" % other_request.ID assert re.match(subj, rejected.headers.get("Subject")) assert "[Autogenerated]" in rejected.body rejected = Email(3).parse() subj = r"^\[PRQ#%d\] Merge Request for [^ ]+ Rejected$" % other_request2.ID assert re.match(subj, rejected.headers.get("Subject")) assert "[Autogenerated]" in rejected.body
def test_tu_voteinfo_is_running(user: User): ts = time.utcnow() with db.begin(): tu_voteinfo = create(TUVoteInfo, Agenda="Blah blah.", User=user.Username, Submitted=ts, End=ts + 1000, Quorum=0.5, Submitter=user) assert tu_voteinfo.is_running() is True with db.begin(): tu_voteinfo.End = ts - 5 assert tu_voteinfo.is_running() is False
def test_user_account_type_relationship(account_type): with db.begin(): user = db.create(User, Username="******", Email="*****@*****.**", RealName="Test User", Passwd="testPassword", AccountType=account_type) assert user.AccountType == account_type # This must be db.deleted here to avoid foreign key issues when # deleting the temporary AccountType in the fixture. with db.begin(): db.delete(user)
def relations(user: User, packages: List[Package]) -> List[PackageRelation]: output = [] with db.begin(): rel = db.create(PackageRelation, Package=packages[0], RelTypeID=rt.CONFLICTS_ID, RelName="chungus-conflicts") output.append(rel) rel = db.create(PackageRelation, Package=packages[1], RelTypeID=rt.CONFLICTS_ID, RelName="chungy-conflicts") output.append(rel) rel = db.create(PackageRelation, Package=packages[0], RelTypeID=rt.PROVIDES_ID, RelName="chungus-provides", RelCondition="<=200") output.append(rel) rel = db.create(PackageRelation, Package=packages[0], RelTypeID=rt.REPLACES_ID, RelName="chungus-replaces", RelCondition="<=200") output.append(rel) # Finally, yield the packages. yield output
def simple(U: str = str(), E: str = str(), H: bool = False, BE: str = str(), R: str = str(), HP: str = str(), I: str = str(), K: str = str(), J: bool = False, CN: bool = False, UN: bool = False, ON: bool = False, S: bool = False, user: models.User = None, **kwargs) -> None: now = time.utcnow() with db.begin(): user.Username = U or user.Username user.Email = E or user.Email user.HideEmail = strtobool(H) user.BackupEmail = user.BackupEmail if BE is None else BE user.RealName = user.RealName if R is None else R user.Homepage = user.Homepage if HP is None else HP user.IRCNick = user.IRCNick if I is None else I user.PGPKey = user.PGPKey if K is None else K user.Suspended = strtobool(S) user.InactivityTS = now * int(strtobool(J)) user.CommentNotify = strtobool(CN) user.UpdateNotify = strtobool(UN) user.OwnershipNotify = strtobool(ON)
def test_package_dependencies(user: User, package: Package): with db.begin(): pkgdep = db.create(PackageDependency, Package=package, DepTypeID=DEPENDS_ID, DepName="test-dep") assert pkgdep.DepName == "test-dep" assert pkgdep.Package == package assert pkgdep in package.package_dependencies assert not pkgdep.is_package() with db.begin(): base = db.create(PackageBase, Name=pkgdep.DepName, Maintainer=user) db.create(Package, PackageBase=base, Name=pkgdep.DepName) assert pkgdep.is_package()
def test_package_source(package: Package): with db.begin(): pkgsource = db.create(PackageSource, Package=package) assert pkgsource.Package == package # By default, PackageSources.Source assigns the string '/dev/null'. assert pkgsource.Source == "/dev/null" assert pkgsource.SourceArch is None
def test_homepage_dashboard(redis, packages, user): # Create Comaintainer records for all of the packages. with db.begin(): for pkg in packages: db.create(PackageComaintainer, PackageBase=pkg.PackageBase, User=user, Priority=1) cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: response = request.get("/", cookies=cookies) assert response.status_code == int(HTTPStatus.OK) root = parse_root(response.text) # Assert some expectations that we end up getting all fifty # packages in the "My Packages" table. expectations = [f"pkg_{i}" for i in range(50 - 1, 0, -1)] my_packages = root.xpath('//table[@id="my-packages"]/tbody/tr') for i, expected in enumerate(expectations): name, version, votes, pop, voted, notify, desc, maint \ = my_packages[i].xpath('./td') assert name.xpath('./a').pop(0).text.strip() == expected # Do the same for the Comaintained Packages table. my_packages = root.xpath('//table[@id="comaintained-packages"]/tbody/tr') for i, expected in enumerate(expectations): name, version, votes, pop, voted, notify, desc, maint \ = my_packages[i].xpath('./td') assert name.xpath('./a').pop(0).text.strip() == expected
def test_tu_proposal_vote_already_voted(client, proposal): tu_user, user, voteinfo = proposal with db.begin(): db.create(TUVote, VoteInfo=voteinfo, User=tu_user) voteinfo.Yes += 1 voteinfo.ActiveTUs += 1 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, allow_redirects=False) assert response.status_code == int(HTTPStatus.BAD_REQUEST) root = parse_root(response.text) status = root.xpath('//span[contains(@class, "status")]/text()')[0] assert status == "You've already voted for this proposal." with client as request: data = {"decision": "Yes"} response = request.get(f"/tu/{voteinfo.ID}", cookies=cookies, data=data, allow_redirects=False) assert response.status_code == int(HTTPStatus.OK) root = parse_root(response.text) status = root.xpath('//span[contains(@class, "status")]/text()')[0] assert status == "You've already voted for this proposal."
def test_tu_proposal_vote_cant_self_vote(client, proposal): tu_user, user, voteinfo = proposal # Update voteinfo.User. with db.begin(): voteinfo.User = tu_user.Username 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, allow_redirects=False) assert response.status_code == int(HTTPStatus.BAD_REQUEST) root = parse_root(response.text) status = root.xpath('//span[contains(@class, "status")]/text()')[0] assert status == "You cannot vote in an proposal about you." with client as request: data = {"decision": "Yes"} response = request.get(f"/tu/{voteinfo.ID}", cookies=cookies, data=data, allow_redirects=False) assert response.status_code == int(HTTPStatus.OK) root = parse_root(response.text) status = root.xpath('//span[contains(@class, "status")]/text()')[0] assert status == "You cannot vote in an proposal about you."
def test_tu_proposal_vote_unauthorized(client: TestClient, proposal: Tuple[User, User, TUVoteInfo]): tu_user, user, voteinfo = proposal with db.begin(): tu_user.AccountTypeID = DEVELOPER_ID 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, allow_redirects=False) assert response.status_code == int(HTTPStatus.UNAUTHORIZED) root = parse_root(response.text) status = root.xpath('//span[contains(@class, "status")]/text()')[0] assert status == "Only Trusted Users are allowed to vote." with client as request: data = {"decision": "Yes"} response = request.get(f"/tu/{voteinfo.ID}", cookies=cookies, data=data, allow_redirects=False) assert response.status_code == int(HTTPStatus.OK) root = parse_root(response.text) status = root.xpath('//span[contains(@class, "status")]/text()')[0] assert status == "Only Trusted Users are allowed to vote."
def test_rpc_msearch(client: TestClient, user: User, packages: List[Package]): params = {"v": 5, "type": "msearch", "arg": user.Username} with client as request: response = request.get("/rpc", params=params) data = response.json() # user1 maintains 4 packages; assert that we got them all. assert data.get("resultcount") == 4 names = list(sorted(r.get("Name") for r in data.get("results"))) expected_results = [ "big-chungus", "chungy-chungus", "gluggly-chungus", "other-pkg" ] assert names == expected_results # Search for a non-existent maintainer, giving us zero packages. params["arg"] = "blah-blah" response = request.get("/rpc", params=params) data = response.json() assert data.get("resultcount") == 0 with db.begin(): packages[0].PackageBase.Maintainer = None # A missing arg still succeeds, but it returns all orphans. # Just verify that we receive no error and the orphaned result. params.pop("arg") response = request.get("/rpc", params=params) data = response.json() assert data.get("resultcount") == 1 result = data.get("results")[0] assert result.get("Name") == "big-chungus"
def test_rpc_too_many_info_results(client: TestClient, packages: List[Package]): # Make many of these packages depend and rely on each other. # This way, we can test to see that the exceeded limit stays true # regardless of the number of related records. with db.begin(): for i in range(len(packages) - 1): db.create(PackageDependency, DepTypeID=DEPENDS_ID, Package=packages[i], DepName=packages[i + 1].Name) db.create(PackageRelation, RelTypeID=PROVIDES_ID, Package=packages[i], RelName=packages[i + 1].Name) config_getint = config.getint def mock_config(section: str, key: str): if key == "max_rpc_results": return 1 return config_getint(section, key) params = {"v": 5, "type": "info", "arg[]": [p.Name for p in packages]} with mock.patch("aurweb.config.getint", side_effect=mock_config): with client as request: resp = request.get("/rpc", params=params) assert resp.json().get("error") == "Too many package results."
def test_homepage_dashboard_flagged(user: User, user2: User, package: Package): pkgbase = package.PackageBase now = time.utcnow() with db.begin(): db.create(PackageComaintainer, User=user2, PackageBase=pkgbase, Priority=1) pkgbase.OutOfDateTS = now - 5 pkgbase.Flagger = user # Test that a comaintainer viewing the dashboard shows them their # flagged co-maintained packages. comaint_cookies = {"AURSID": user2.login(Request(), "testPassword")} with client as request: resp = request.get("/", cookies=comaint_cookies) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) flagged = root.xpath('//table[@id="flagged-packages"]//tr/td/a')[0] assert flagged.text.strip() == package.Name # Test that a maintainer viewing the dashboard shows them their # flagged maintained packages. cookies = {"AURSID": user.login(Request(), "testPassword")} with client as request: resp = request.get("/", cookies=cookies) assert resp.status_code == int(HTTPStatus.OK) root = parse_root(resp.text) flagged = root.xpath('//table[@id="flagged-packages"]//tr/td/a')[0] assert flagged.text.strip() == package.Name
async def request_close_post(request: Request, id: int, comments: str = Form(default=str())): pkgreq = get_pkgreq_by_id(id) # `pkgreq`.User can close their own request. approved = [pkgreq.User] if not request.user.has_credential(creds.PKGREQ_CLOSE, approved=approved): # 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 now = time.utcnow() with db.begin(): pkgreq.Closer = request.user pkgreq.ClosureComment = comments pkgreq.ClosedTS = now pkgreq.Status = REJECTED_ID notify_ = notify.RequestCloseNotification(request.user.ID, pkgreq.ID, pkgreq.status_display()) notify_.send() return RedirectResponse("/requests", status_code=HTTPStatus.SEE_OTHER)