diff --git a/b2/console_tool.py b/b2/console_tool.py index 7c6222b03..ff305d038 100644 --- a/b2/console_tool.py +++ b/b2/console_tool.py @@ -1007,7 +1007,7 @@ def run(self, args): super().run(args) # Handle internal options for testing inside Backblaze. # These are not documented in the usage string. - realm, realm_from_env = self._get_realm(args) + realm = self._get_user_requested_realm(args) if args.applicationKeyId is None: args.applicationKeyId = ( @@ -1021,20 +1021,19 @@ def run(self, args): getpass.getpass('Backblaze application key: ') ) - return self.authorize( - args.applicationKeyId, args.applicationKey, realm, verbose_realm=realm_from_env - ) + return self.authorize(args.applicationKeyId, args.applicationKey, realm) - def authorize(self, application_key_id, application_key, realm, *, verbose_realm: bool = False): + def authorize(self, application_key_id, application_key, realm: str | None): """ Perform the authorization and capability checks, report errors. :param application_key_id: application key ID used to authenticate :param application_key: application key - :param realm: authorization realm - :param verbose_realm: if True, print the realm used for authorization + :param realm: authorization realm; if None, production is used :return: exit status """ + verbose_realm = bool(realm) + realm = realm or 'production' url = REALM_URLS.get(realm, realm) logger.info(f"Using {url}") if verbose_realm: @@ -1067,24 +1066,18 @@ def authorize(self, application_key_id, application_key, realm, *, verbose_realm return 1 @classmethod - def _get_realm(cls, args) -> tuple[str, bool]: + def _get_user_requested_realm(cls, args) -> str | None: """ Determine the realm to use for authorization. - - :return: tuple of realm and if REALM came from ENV vars """ if args.dev: - return 'dev', False + return 'dev' if args.staging: return 'staging' if args.environment: - return args.environment, False + return args.environment - realm = os.environ.get(B2_ENVIRONMENT_ENV_VAR) - if realm is None: - return 'production', False - - return realm, True + return os.environ.get(B2_ENVIRONMENT_ENV_VAR) @B2.register_subcommand @@ -4037,14 +4030,13 @@ def authorize_from_env(self, command_class): f'Please provide both "{B2_APPLICATION_KEY_ENV_VAR}" and "{B2_APPLICATION_KEY_ID_ENV_VAR}" environment variables or none of them' ) return 1 - realm_from_env = B2_ENVIRONMENT_ENV_VAR in os.environ - realm = os.environ.get(B2_ENVIRONMENT_ENV_VAR, 'production') + realm = os.environ.get(B2_ENVIRONMENT_ENV_VAR) - if self.api.account_info.is_same_key(key_id, realm): + if self.api.account_info.is_same_key(key_id, realm or 'production'): return 0 logger.info('authorize-account is being run from env variables') - return AuthorizeAccount(self).authorize(key_id, key, realm, verbose_realm=realm_from_env) + return AuthorizeAccount(self).authorize(key_id, key, realm) def _print(self, *args, **kwargs): print(*args, file=self.stdout, **kwargs) diff --git a/changelog.d/949.fixed.md b/changelog.d/949.fixed.md index 357a1c6d5..72b54cf28 100644 --- a/changelog.d/949.fixed.md +++ b/changelog.d/949.fixed.md @@ -1 +1 @@ -Don't print `Using https://REALM" in stderr unless B2_ENVIRONMENT env var was set. +Don't print `Using https://REALM" in stderr unless explicitly set by user. diff --git a/test/unit/console_tool/conftest.py b/test/unit/console_tool/conftest.py index aae3562fc..cd9bae128 100644 --- a/test/unit/console_tool/conftest.py +++ b/test/unit/console_tool/conftest.py @@ -13,6 +13,8 @@ import pytest +import b2.console_tool + class ConsoleToolTester(BaseConsoleToolTest): def authorize(self): @@ -22,6 +24,21 @@ def run(self, *args, **kwargs): return self._run_command(*args, **kwargs) +@pytest.fixture +def cwd_path(tmp_path): + """Set up a test directory and return its path.""" + prev_cwd = os.getcwd() + os.chdir(tmp_path) + yield tmp_path + os.chdir(prev_cwd) + + +@pytest.fixture +def b2_cli_log_fix(caplog): + caplog.set_level(0) # prevent pytest from blocking logs + b2.console_tool.logger.setLevel(0) # reset logger level to default + + @pytest.fixture def b2_cli(): cli_tester = ConsoleToolTester() diff --git a/test/unit/console_tool/test_authorize_account.py b/test/unit/console_tool/test_authorize_account.py new file mode 100644 index 000000000..85201b047 --- /dev/null +++ b/test/unit/console_tool/test_authorize_account.py @@ -0,0 +1,124 @@ +###################################################################### +# +# File: test/unit/console_tool/test_authorize_account.py +# +# Copyright 2023 Backblaze Inc. All Rights Reserved. +# +# License https://www.backblaze.com/using_b2_code.html +# +###################################################################### +from unittest import mock + +import pytest + +from b2._cli.const import ( + B2_APPLICATION_KEY_ENV_VAR, + B2_APPLICATION_KEY_ID_ENV_VAR, + B2_ENVIRONMENT_ENV_VAR, +) + + +@pytest.fixture +def b2_cli_is_authorized_afterwards(b2_cli): + assert b2_cli.account_info.get_account_auth_token() is None + yield b2_cli + assert b2_cli.account_info.get_account_auth_token() is not None + + +def test_authorize_with_bad_key(b2_cli): + expected_stdout = "" + expected_stderr = """ + ERROR: unable to authorize account: Invalid authorization token. Server said: secret key is wrong (unauthorized) + """ + + b2_cli._run_command( + ["authorize-account", b2_cli.account_id, "bad-app-key"], + expected_stdout, + expected_stderr, + 1, + ) + assert b2_cli.account_info.get_account_auth_token() is None + + +@pytest.mark.parametrize( + "command", + [ + "authorize-account", + "authorize_account", + ], +) +def test_authorize_with_good_key(b2_cli, b2_cli_is_authorized_afterwards, command): + assert b2_cli.account_info.get_account_auth_token() is None + + expected_stderr = """ + """ + + b2_cli._run_command([command, b2_cli.account_id, b2_cli.master_key], "", expected_stderr, 0) + + assert b2_cli.account_info.get_account_auth_token() is not None + + +def test_authorize_using_env_variables(b2_cli): + assert b2_cli.account_info.get_account_auth_token() is None + + expected_stderr = """ + """ + + with mock.patch.dict( + "os.environ", + { + B2_APPLICATION_KEY_ID_ENV_VAR: b2_cli.account_id, + B2_APPLICATION_KEY_ENV_VAR: b2_cli.master_key, + }, + ): + b2_cli._run_command(["authorize-account"], "", expected_stderr, 0) + + assert b2_cli.account_info.get_account_auth_token() is not None + + +@pytest.mark.parametrize( + "flags,realm_url", + [ + ([], "http://production.example.com"), + (["--debugLogs"], "http://production.example.com"), + (["--environment", "http://custom.example.com"], "http://custom.example.com"), + (["--environment", "production"], "http://production.example.com"), + (["--dev"], "http://api.backblazeb2.xyz:8180"), + (["--staging"], "https://api.backblaze.net"), + ], +) +def test_authorize_towards_realm( + b2_cli, b2_cli_is_authorized_afterwards, flags, realm_url, cwd_path, b2_cli_log_fix +): + expected_stderr = f"Using {realm_url}\n" if any(f != "--debugLogs" for f in flags) else "" + + b2_cli._run_command( + ["authorize-account", *flags, b2_cli.account_id, b2_cli.master_key], + "", + expected_stderr, + 0, + ) + log_path = cwd_path / "b2_cli.log" + if "--debugLogs" in flags: + assert f"Using {realm_url}\n" in log_path.read_text() + else: + assert not log_path.exists() + + +def test_authorize_towards_custom_realm_using_env(b2_cli, b2_cli_is_authorized_afterwards): + expected_stderr = """ + Using http://custom2.example.com + """ + + with mock.patch.dict( + "os.environ", + { + B2_ENVIRONMENT_ENV_VAR: "http://custom2.example.com", + }, + ): + b2_cli._run_command( + ["authorize-account", b2_cli.account_id, b2_cli.master_key], + "", + expected_stderr, + 0, + ) diff --git a/test/unit/test_console_tool.py b/test/unit/test_console_tool.py index d8d283d23..8593797e0 100644 --- a/test/unit/test_console_tool.py +++ b/test/unit/test_console_tool.py @@ -256,101 +256,6 @@ def _upload_multiple_files(cls, bucket): class TestConsoleTool(BaseConsoleToolTest): - def test_authorize_with_bad_key(self): - expected_stdout = '' - expected_stderr = ''' - ERROR: unable to authorize account: Invalid authorization token. Server said: secret key is wrong (unauthorized) - ''' - - self._run_command( - ['authorize-account', self.account_id, 'bad-app-key'], expected_stdout, expected_stderr, - 1 - ) - - def test_authorize_with_good_key_using_hyphen(self): - # Initial condition - assert self.account_info.get_account_auth_token() is None - - # Authorize an account with a good api key. - expected_stderr = """ - """ - - self._run_command( - ['authorize-account', self.account_id, self.master_key], '', expected_stderr, 0 - ) - - # Auth token should be in account info now - assert self.account_info.get_account_auth_token() is not None - - def test_authorize_with_good_key_using_underscore(self): - # Initial condition - assert self.account_info.get_account_auth_token() is None - - # Authorize an account with a good api key. - expected_stderr = """ - """ - self._run_command( - ['authorize_account', self.account_id, self.master_key], '', expected_stderr, 0 - ) - - # Auth token should be in account info now - assert self.account_info.get_account_auth_token() is not None - - def test_authorize_using_env_variables(self): - # Initial condition - assert self.account_info.get_account_auth_token() is None - - # Authorize an account with a good api key. - expected_stderr = """ - """ - - # Setting up environment variables - with mock.patch.dict( - 'os.environ', { - B2_APPLICATION_KEY_ID_ENV_VAR: self.account_id, - B2_APPLICATION_KEY_ENV_VAR: self.master_key, - } - ): - assert B2_APPLICATION_KEY_ID_ENV_VAR in os.environ - assert B2_APPLICATION_KEY_ENV_VAR in os.environ - - self._run_command(['authorize-account'], '', expected_stderr, 0) - - # Auth token should be in account info now - assert self.account_info.get_account_auth_token() is not None - - def test_authorize_towards_custom_realm(self): - # Initial condition - assert self.account_info.get_account_auth_token() is None - - # Authorize an account with a good api key. - expected_stderr = """ - """ - - # realm provided with args - self._run_command( - [ - 'authorize-account', '--environment', 'http://custom.example.com', self.account_id, - self.master_key - ], '', expected_stderr, 0 - ) - - # Auth token should be in account info now - assert self.account_info.get_account_auth_token() is not None - - expected_stderr = """ - Using http://custom2.example.com - """ - # realm provided with env var - with mock.patch.dict( - 'os.environ', { - B2_ENVIRONMENT_ENV_VAR: 'http://custom2.example.com', - } - ): - self._run_command( - ['authorize-account', self.account_id, self.master_key], '', expected_stderr, 0 - ) - def test_create_key_and_authorize_with_it(self): # Start with authorizing with the master key self._authorize_account()