Skip to content

Commit d69dac1

Browse files
authored
feat: add create local user support (#305)
Adds create user support for local authentication providers (e.g., 'ldap', 'oauth2', 'pam', 'password', 'proxy', or 'saml'). Closes #113 Closes #225
1 parent 0e9cf02 commit d69dac1

File tree

6 files changed

+292
-88
lines changed

6 files changed

+292
-88
lines changed
Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,79 @@
11
from posit import connect
22

33

4-
class TestAttributeContent:
4+
class TestUser:
5+
@classmethod
6+
def setup_class(cls):
7+
cls.client = client = connect.Client()
8+
cls.aron = client.users.create(
9+
username="aron", email="[email protected]", password="s3cur3p@ssword"
10+
)
11+
cls.bill = client.users.create(
12+
username="bill", email="[email protected]", password="s3cur3p@ssword"
13+
)
14+
cls.cole = client.users.create(
15+
username="cole", email="[email protected]", password="s3cur3p@ssword"
16+
)
17+
18+
def test_lock(self):
19+
for user in (self.aron, self.bill, self.cole):
20+
user.lock()
21+
assert len(self.client.users.find(account_status="locked")) == 1
22+
user.unlock()
23+
assert len(self.client.users.find(account_status="locked")) == 0
24+
25+
def test_count(self):
26+
# aron, bill, cole, and me
27+
assert self.client.users.count() == 4
28+
29+
def test_find(self):
30+
assert self.client.users.find(prefix="aron") == [self.aron]
31+
assert self.client.users.find(prefix="bill") == [self.bill]
32+
assert self.client.users.find(prefix="cole") == [self.cole]
33+
34+
def test_find_one(self):
35+
assert self.client.users.find_one(prefix="aron") == self.aron
36+
assert self.client.users.find_one(prefix="bill") == self.bill
37+
assert self.client.users.find_one(prefix="cole") == self.cole
38+
39+
def test_get(self):
40+
assert self.client.users.get(self.aron["guid"]) == self.aron
41+
assert self.client.users.get(self.bill["guid"]) == self.bill
42+
assert self.client.users.get(self.cole["guid"]) == self.cole
43+
44+
45+
class TestUserContent:
546
"""Checks behavior of the content attribute."""
647

748
@classmethod
849
def setup_class(cls):
9-
cls.client = connect.Client()
10-
cls.user = cls.client.me
11-
cls.user.content.create(
50+
cls.client = client = connect.Client()
51+
cls.me = me = client.me
52+
cls.content = me.content.create(
1253
name="Sample",
1354
description="Simple sample content for testing",
1455
access_type="acl",
1556
)
1657

1758
@classmethod
1859
def teardown_class(cls):
19-
assert cls.user.content.find_one().delete() is None
20-
assert cls.user.content.count() == 0
60+
assert cls.content.delete() is None
61+
assert cls.me.content.count() == 0
2162

2263
def test_count(self):
23-
assert self.user.content.count() == 1
64+
assert self.me.content.count() == 1
2465

2566
def test_find(self):
26-
assert self.user.content.find()
67+
assert self.me.content.find()
2768

2869
def test_find_one(self):
29-
assert self.user.content.find_one()
70+
assert self.me.content.find_one()
71+
72+
def test_multiple_users(self):
73+
user = self.client.users.create(
74+
username="example", email="[email protected]", password="s3cur3p@ssword"
75+
)
76+
# assert filtering limits to the provided user.
77+
assert self.me.content.find_one() == self.content
78+
assert user.content.find_one() is None
79+
user.lock()

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ classifiers = [
2121
dynamic = ["version"]
2222
dependencies = [
2323
"requests>=2.31.0,<3",
24-
"packaging"
24+
"packaging",
25+
"typing-extensions"
2526
]
2627

2728
[project.urls]

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
requests==2.32.2
22
packaging==24.1
3+
typing-extensions==4.12.2

src/posit/connect/content.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ def create(
367367
Content title. Default is None.
368368
description : str, optional
369369
Content description. Default is None.
370-
access_type : Literal['all', 'acl', 'logged_in', optional
370+
access_type : Literal['all', 'acl', 'logged_in'], optional
371371
How content manages viewers. Default is 'acl'. Options: 'all', 'logged_in', 'acl'.
372372
connection_timeout : int, optional
373373
Max seconds without data exchange. Default is None. Falls back to server setting 'Scheduler.ConnectionTimeout'.

src/posit/connect/paginator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class Paginator:
3838
url (str): The URL of the paginated API endpoint.
3939
"""
4040

41-
def __init__(self, session: requests.Session, url: str, params: dict = {}) -> None:
41+
def __init__(self, session: requests.Session, url: str, params = {}) -> None:
4242
self.session = session
4343
self.url = url
4444
self.params = params

0 commit comments

Comments
 (0)