Skip to content

Commit

Permalink
don't show realm in stderr unless explictly set by user in command li…
Browse files Browse the repository at this point in the history
…ne or through ENV vars
  • Loading branch information
mjurbanski-reef committed Nov 29, 2023
1 parent 61f3eaa commit 0b221e5
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 117 deletions.
34 changes: 13 additions & 21 deletions b2/console_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion changelog.d/949.fixed.md
Original file line number Diff line number Diff line change
@@ -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.
17 changes: 17 additions & 0 deletions test/unit/console_tool/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

import pytest

import b2.console_tool


class ConsoleToolTester(BaseConsoleToolTest):
def authorize(self):
Expand All @@ -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()
Expand Down
124 changes: 124 additions & 0 deletions test/unit/console_tool/test_authorize_account.py
Original file line number Diff line number Diff line change
@@ -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,
)
95 changes: 0 additions & 95 deletions test/unit/test_console_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 0b221e5

Please sign in to comment.