Skip to content

Commit 4826880

Browse files
committed
feat: enable idp_configuration_id in bulk_add
1 parent 303b48d commit 4826880

File tree

3 files changed

+29
-6
lines changed

3 files changed

+29
-6
lines changed

tableauserverclient/server/endpoint/users_endpoint.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ def bulk_add(self, users: Iterable[UserItem]) -> JobItem:
403403
404404
Email is optional, but if provided, it must be a valid email address.
405405
406-
If auth_setting is not provided, the default is ServerDefault.
406+
If auth_setting is not provided, and idp_configuration_id is None, then
407+
default is ServerDefault.
407408
408409
If site_role is not provided, the default is Unlicensed.
409410
@@ -414,6 +415,10 @@ def bulk_add(self, users: Iterable[UserItem]) -> JobItem:
414415
Details about administrator level and publishing capability are
415416
inferred from the site_role.
416417
418+
If the user belongs to a different IDP configuration, the UserItem's
419+
idp_configuration_id attribute must be set to the IDP configuration ID
420+
that the user belongs to.
421+
417422
Parameters
418423
----------
419424
users: Iterable[UserItem]
@@ -733,7 +738,7 @@ def filter(self, *invalid, page_size: Optional[int] = None, **kwargs) -> QuerySe
733738
return super().filter(*invalid, page_size=page_size, **kwargs)
734739

735740

736-
def create_users_csv(users: Iterable[UserItem], identity_pool=None) -> bytes:
741+
def create_users_csv(users: Iterable[UserItem]) -> bytes:
737742
"""
738743
Create a CSV byte string from an Iterable of UserItem objects. The CSV will
739744
have the following columns, and no header row:
@@ -761,8 +766,6 @@ def create_users_csv(users: Iterable[UserItem], identity_pool=None) -> bytes:
761766
bytes
762767
A byte string containing the CSV data.
763768
"""
764-
if identity_pool is not None:
765-
raise NotImplementedError("Identity pool is not supported in this version")
766769
with io.StringIO() as output:
767770
writer = csv.writer(output, quoting=csv.QUOTE_MINIMAL)
768771
for user in users:

tableauserverclient/server/request_factory.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,12 @@ def import_from_csv_req(self, csv_content: bytes, users: Iterable[UserItem]):
943943
raise ValueError("User name must be populated.")
944944
user_element = ET.SubElement(xml_request, "user")
945945
user_element.attrib["name"] = user.name
946-
user_element.attrib["authSetting"] = user.auth_setting or "ServerDefault"
946+
if user.auth_setting is not None and user.idp_configuration_id is not None:
947+
raise ValueError("User cannot have both authSetting and idpConfigurationId.")
948+
elif user.idp_configuration_id is not None:
949+
user_element.attrib["idpConfigurationId"] = user.idp_configuration_id
950+
else:
951+
user_element.attrib["authSetting"] = user.auth_setting or "ServerDefault"
947952

948953
parts = {
949954
"tableau_user_import": ("tsc_users_file.csv", csv_content, "file"),

test/test_user.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def make_user(
3737
domain: str = "",
3838
fullname: str = "",
3939
email: str = "",
40+
idp_id: str = "",
4041
) -> TSC.UserItem:
4142
user = TSC.UserItem(name, site_role or None)
4243
if auth_setting:
@@ -47,6 +48,8 @@ def make_user(
4748
user.fullname = fullname
4849
if email:
4950
user.email = email
51+
if idp_id:
52+
user.idp_configuration_id = idp_id
5053
return user
5154

5255

@@ -415,6 +418,7 @@ def test_bulk_add(self):
415418
make_user("Frank", "SiteAdministratorExplorer", "TableauIDWithMFA", email="[email protected]"),
416419
make_user("Grace", "SiteAdministratorCreator", "SAML", "example.com", "Grace Example", "[email protected]"),
417420
make_user("Hank", "Unlicensed"),
421+
make_user("Ivy", "Unlicensed", idp_id="0123456789"),
418422
]
419423
with requests_mock.mock() as m:
420424
m.post(f"{self.server.users.baseurl}/import", text=BULK_ADD_XML.read_text())
@@ -446,7 +450,11 @@ def test_bulk_add(self):
446450

447451
for user, xml_user in zip(users, xml_users):
448452
assert user.name == xml_user.get("name")
449-
assert xml_user.get("authSetting") == (user.auth_setting or "ServerDefault")
453+
if user.idp_configuration_id is None:
454+
assert xml_user.get("authSetting") == (user.auth_setting or "ServerDefault")
455+
else:
456+
assert xml_user.get("idpConfigurationId") == user.idp_configuration_id
457+
assert xml_user.get("authSetting") is None
450458

451459
csv_data = create_users_csv(users).replace(b"\r\n", b"\n")
452460
assert csv_data.strip() == segments[0].split(b"\n\n")[1].strip()
@@ -505,3 +513,10 @@ def test_add_all(self) -> None:
505513
self.server.users.add_all(users)
506514

507515
assert mock_add.call_count == len(users)
516+
517+
def test_add_idp_and_auth_error(self) -> None:
518+
self.server.version = "3.24"
519+
users = [make_user("Alice", "Viewer", auth_setting="SAML", idp_id="01234")]
520+
521+
with pytest.raises(ValueError, match="User cannot have both authSetting and idpConfigurationId."):
522+
self.server.users.bulk_add(users)

0 commit comments

Comments
 (0)