From 12468e4f265b4c82c2209b5c58d46ddf05a33da6 Mon Sep 17 00:00:00 2001 From: Jac Fitzgerald Date: Wed, 8 Jul 2026 13:00:42 -0700 Subject: [PATCH] fix: preserve CSV role/auth in createusers, fix case-sensitive validation - createusers was replacing the fully-parsed user_obj with a bare TSC.UserItem(name), silently discarding site_role and auth_setting from the CSV (fixes #297) - _validate_item was case-sensitive against lowercase CHOICES values, rejecting valid inputs like "Viewer" or "Creator" (fixes #351) - Column.MAX was 7, blocking the 8th auth column; added AUTH=7, MAX=8 - Added auth column to CHOICES so validation passes it through to TSC Co-Authored-By: Claude Sonnet 4.6 --- tabcmd/commands/user/create_users_command.py | 3 +-- tabcmd/commands/user/user_data.py | 14 ++++++++------ tests/commands/test_user_utils.py | 12 ++++++++++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/tabcmd/commands/user/create_users_command.py b/tabcmd/commands/user/create_users_command.py index ab2a92a2..9dafcb3f 100644 --- a/tabcmd/commands/user/create_users_command.py +++ b/tabcmd/commands/user/create_users_command.py @@ -58,8 +58,7 @@ def run_command(cls, args): user_obj.site_role = args.role if args.auth_type: user_obj.auth_setting = args.auth_type - new_user = TSC.UserItem(user_obj.name) - server.users.add(new_user) + server.users.add(user_obj) logger.info(_("common.output.succeeded").format(user_obj.name)) number_of_users_added += 1 except Exception as e: diff --git a/tabcmd/commands/user/user_data.py b/tabcmd/commands/user/user_data.py index b0ef9262..2305b1cd 100644 --- a/tabcmd/commands/user/user_data.py +++ b/tabcmd/commands/user/user_data.py @@ -52,13 +52,14 @@ def to_tsc_user(self) -> TSC.UserItem: CHOICES: List[List[str]] = [ - [], - [], - [], + [], # username + [], # password + [], # display name ["creator", "explorer", "viewer", "unlicensed"], # license ["system", "site", "none", "no"], # admin ["yes", "true", "1", "no", "false", "0"], # publisher - [], + [], # email + [], # auth (validated by TSC) ] site_roles = [ @@ -94,7 +95,8 @@ class Column(IntEnum): PUBLISHER = 5 EMAIL = 6 - MAX = 7 # number of columns + AUTH = 7 + MAX = 8 # number of columns (username through auth) class UserCommand(Server): @@ -187,7 +189,7 @@ def _validate_item(item: str, possible_values: List[str], column_type) -> None: if item is None or item == "": # value can be empty for any column except user, which is checked elsewhere return - if item in possible_values or possible_values == []: + if item.lower() in possible_values or possible_values == []: return raise AttributeError(_("commandlineutils.errors.bad_value").format(column_type, item, possible_values)) diff --git a/tests/commands/test_user_utils.py b/tests/commands/test_user_utils.py index 516ba3de..67de8d56 100644 --- a/tests/commands/test_user_utils.py +++ b/tests/commands/test_user_utils.py @@ -138,3 +138,15 @@ def test_get_usernames_from_file(self): test_data = self._mock_file_content(UserDataTest.usernames) user_list = UserCommand.get_users_from_file(test_data) assert user_list[0].name == "valid", user_list + + def test_validate_mixed_case_license(self): + # Regression: _validate_item was case-sensitive; tabcmd issue #351 + UserCommand._validate_user_or_throw("username, pword, fname, Viewer, None, no, email", UserDataTest.logger) + UserCommand._validate_user_or_throw("username, pword, fname, Creator, Site, yes, email", UserDataTest.logger) + UserCommand._validate_user_or_throw("username, pword, fname, EXPLORER, NONE, YES, email", UserDataTest.logger) + + def test_parse_line_preserves_role(self): + # Regression: CreateUsersCommand replaced user_obj with bare TSC.UserItem(name), discarding role/auth + user = UserCommand._parse_line("username, pword, fname, creator, none, yes, email") + assert user is not None + assert user.site_role == "Creator", f"Expected Creator, got {user.site_role}"