diff --git a/dodo.py b/dodo.py index 9d193c9b..82ac894c 100644 --- a/dodo.py +++ b/dodo.py @@ -455,7 +455,7 @@ def uniquify_file(filename): ) ) else: - print("Saved {} sorted unique lines to {}".format(len(uniques), filename)) + print("Saved {} sorted unique lines to {}".format(len(unique_lines), filename)) def task_clean_all(): diff --git a/tabcmd/commands/user/create_site_users.py b/tabcmd/commands/user/create_site_users.py index c63436b0..7338c623 100644 --- a/tabcmd/commands/user/create_site_users.py +++ b/tabcmd/commands/user/create_site_users.py @@ -49,7 +49,7 @@ def run_command(cls, args): if args.role: user_obj.site_role = args.role # tsc is case sensitive if args.auth_type: - user_obj.auth_setting = args.auth_type + user_obj.auth_setting = UserCommand.normalize_auth_setting(args.auth_type, user_obj.name) number_of_users_listed += 1 result = server.users.add(user_obj) logger.info(_("common.output.succeeded").format(user_obj.name)) diff --git a/tabcmd/commands/user/create_users_command.py b/tabcmd/commands/user/create_users_command.py index 9dafcb3f..7790f4ec 100644 --- a/tabcmd/commands/user/create_users_command.py +++ b/tabcmd/commands/user/create_users_command.py @@ -57,7 +57,7 @@ def run_command(cls, args): if args.role: user_obj.site_role = args.role if args.auth_type: - user_obj.auth_setting = args.auth_type + user_obj.auth_setting = UserCommand.normalize_auth_setting(args.auth_type, user_obj.name) server.users.add(user_obj) logger.info(_("common.output.succeeded").format(user_obj.name)) number_of_users_added += 1 diff --git a/tabcmd/commands/user/user_data.py b/tabcmd/commands/user/user_data.py index 272a5375..cd7ec38e 100644 --- a/tabcmd/commands/user/user_data.py +++ b/tabcmd/commands/user/user_data.py @@ -45,7 +45,8 @@ def to_tsc_user(self) -> TSC.UserItem: site_role = UserCommand.evaluate_site_role(self.license_level, self.admin_level, self.publisher) if not site_role: raise AttributeError(_("tabcmd.user.error.site_role_required")) - user = TSC.UserItem(self.name, site_role, self.auth) + auth = UserCommand.normalize_auth_setting(self.auth, self.name) + user = TSC.UserItem(self.name, site_role, auth) user.email = self.email user.fullname = self.fullname return user @@ -104,6 +105,20 @@ class UserCommand(Server): This class acts as a base class for user related group of commands """ + # tabcmd Classic accepts "Local" as an auth type; TSC's UserItem.Auth enum + # has no Local value and rejects it. Map Classic's "Local" to ServerDefault + # (both for CSV values and for the CLI --auth-type override) so inputs + # authored for Classic import without crashing, and log a warning so the + # operator sees the remap happened. + @staticmethod + def normalize_auth_setting(auth, username: Optional[str] = None): + if isinstance(auth, str) and auth.lower() == "local": + logging.getLogger(__name__).warning( + _("tabcmd.user.warning.local_auth_remapped").format(username) + ) + return TSC.UserItem.Auth.ServerDefault + return auth + @staticmethod def set_role_arg(parser): parser.add_argument( diff --git a/tabcmd/locales/de/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/de/LC_MESSAGES/tabcmd.mo index cac49e59..bb3aab6b 100644 Binary files a/tabcmd/locales/de/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/de/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/en/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/en/LC_MESSAGES/tabcmd.mo index a067f324..0bede2ba 100644 Binary files a/tabcmd/locales/en/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/en/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/en/tabcmd_messages_en.properties b/tabcmd/locales/en/tabcmd_messages_en.properties index 7eda4bff..605e229f 100644 --- a/tabcmd/locales/en/tabcmd_messages_en.properties +++ b/tabcmd/locales/en/tabcmd_messages_en.properties @@ -214,6 +214,7 @@ tabcmd.status.job_completed=Job completed tabcmd.status.waiting_for_refresh_job=Waiting for refresh job to begin tabcmd.user.error.site_role_required=Site role is required tabcmd.user.help.auth_type=Assigns the authentication type for all users in the CSV file. Possible values: +tabcmd.user.warning.local_auth_remapped=User '{0}': auth_setting 'Local' is not a Tableau Server auth type; mapped to ServerDefault. The site's default auth method will be used. tabcmd.user.help.site_role=Specifies a site role for all users in the .csv file. Possible roles: tabcmd.warning.calculations_not_supported=Adding or removing Calculations tasks are not supported tabcmdparser.global.behaviors=Global behaviors: diff --git a/tabcmd/locales/es/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/es/LC_MESSAGES/tabcmd.mo index db1cd9b8..971f2d80 100644 Binary files a/tabcmd/locales/es/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/es/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/fr/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/fr/LC_MESSAGES/tabcmd.mo index 70813c35..7fb39edf 100644 Binary files a/tabcmd/locales/fr/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/fr/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/ga/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/ga/LC_MESSAGES/tabcmd.mo index ecc45de2..be01907c 100644 Binary files a/tabcmd/locales/ga/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/ga/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/it/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/it/LC_MESSAGES/tabcmd.mo index 50a2e9f7..81bf7744 100644 Binary files a/tabcmd/locales/it/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/it/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/ja/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/ja/LC_MESSAGES/tabcmd.mo index ce6b79fa..85511455 100644 Binary files a/tabcmd/locales/ja/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/ja/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/ko/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/ko/LC_MESSAGES/tabcmd.mo index 0b1ea7d1..0d307a70 100644 Binary files a/tabcmd/locales/ko/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/ko/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/pt/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/pt/LC_MESSAGES/tabcmd.mo index fa709d8a..6bfc41ef 100644 Binary files a/tabcmd/locales/pt/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/pt/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/sv/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/sv/LC_MESSAGES/tabcmd.mo index e329a6a8..0fa1d20a 100644 Binary files a/tabcmd/locales/sv/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/sv/LC_MESSAGES/tabcmd.mo differ diff --git a/tabcmd/locales/zh/LC_MESSAGES/tabcmd.mo b/tabcmd/locales/zh/LC_MESSAGES/tabcmd.mo index 3c252067..04786bd3 100644 Binary files a/tabcmd/locales/zh/LC_MESSAGES/tabcmd.mo and b/tabcmd/locales/zh/LC_MESSAGES/tabcmd.mo differ diff --git a/tests/commands/test_user_utils.py b/tests/commands/test_user_utils.py index 67de8d56..d765dc03 100644 --- a/tests/commands/test_user_utils.py +++ b/tests/commands/test_user_utils.py @@ -150,3 +150,32 @@ def test_parse_line_preserves_role(self): 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}" + + def test_local_auth_maps_to_server_default(self): + # tabcmd Classic accepts "Local" as an auth type; TSC's Auth enum has no + # Local value, so passing it through raises ValueError on server.users.add. + # to_tsc_user should map Classic's "Local" -> ServerDefault for parity + # and log a warning so the operator sees the remap happened. + data = Userdata() + data.populate(["username", "pword", "fname", "creator", "none", "yes", "email", "Local"]) + with self.assertLogs("tabcmd.commands.user.user_data", level="WARNING") as logs: + user = data.to_tsc_user() + assert user.auth_setting == TSC.UserItem.Auth.ServerDefault, user.auth_setting + # Assert the warning names the affected user so the operator can act on + # it. Avoid coupling to the localize key -- once the .mo catalog carries + # this msgid, `_()` returns the translated text instead of the key. + assert any("username" in msg for msg in logs.output), logs.output + + def test_local_auth_case_insensitive(self): + data = Userdata() + data.populate(["username", "pword", "fname", "creator", "none", "yes", "email", "local"]) + with self.assertLogs("tabcmd.commands.user.user_data", level="WARNING"): + user = data.to_tsc_user() + assert user.auth_setting == TSC.UserItem.Auth.ServerDefault, user.auth_setting + + def test_non_local_auth_passes_through(self): + # SAML/OpenID/etc. are not remapped. + data = Userdata() + data.populate(["username", "pword", "fname", "creator", "none", "yes", "email", TSC.UserItem.Auth.SAML]) + user = data.to_tsc_user() + assert user.auth_setting == TSC.UserItem.Auth.SAML, user.auth_setting