Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change auhtorize account signature #456

Merged
merged 5 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
fail-fast: false
matrix:
os: ["ubuntu-latest", "macos-latest", "windows-latest"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.9", "pypy-3.10"]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "pypy-3.9", "pypy-3.9-nightly", "pypy-3.10"]
exclude:
- os: "macos-latest"
python-version: "pypy-3.9"
Expand All @@ -94,6 +94,13 @@ jobs:
python-version: "pypy-3.10"
- os: "windows-latest"
python-version: "pypy-3.10"
# TODO: pypy-3.9 started breaking on windows, due to some prints leftover in pypy release
- os: "ubuntu-latest"
python-version: "pypy-3.9-nightly"
- os: "macos-latest"
python-version: "pypy-3.9-nightly"
- os: "windows-latest"
python-version: "pypy-3.9"
steps:
- uses: actions/checkout@v3
with:
Expand All @@ -106,12 +113,12 @@ jobs:
- name: Install dependencies
run: python -m pip install --upgrade nox pip setuptools
- name: Run unit tests
run: nox -vs unit
run: nox -vs unit -- -v
env:
SKIP_COVERAGE: ${{ startsWith(matrix.python-version, env.SKIP_COVERAGE_PYTHON_VERSION_PREFIX) }}
- name: Run integration tests
if: ${{ env.B2_TEST_APPLICATION_KEY != '' && env.B2_TEST_APPLICATION_KEY_ID != '' }}
run: nox -vs integration -- --dont-cleanup-old-buckets
run: nox -vs integration -- --dont-cleanup-old-buckets -v
doc:
needs: build
runs-on: ubuntu-latest
Expand Down
4 changes: 2 additions & 2 deletions b2sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,13 @@ def authorize_automatically(self):
return self.session.authorize_automatically()

@limit_trace_arguments(only=('self', 'realm'))
def authorize_account(self, realm, application_key_id, application_key):
def authorize_account(self, application_key_id, application_key, realm='production'):
"""
Perform account authorization.

:param str realm: a realm to authorize account in (usually just "production")
:param str application_key_id: :term:`application key ID`
:param str application_key: user's :term:`application key`
:param str realm: a realm to authorize account in (usually just "production")
"""
self.session.authorize_account(realm, application_key_id, application_key)
self._populate_bucket_cache_from_key()
Expand Down
10 changes: 10 additions & 0 deletions b2sdk/v2/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Services(v3.Services):
# override to use legacy B2Session with legacy B2Http
# and to raise old style BucketIdNotFound exception
# and to use old style Bucket
# and to use legacy authorize_account signature
class B2Api(v3.B2Api):
SESSION_CLASS = staticmethod(B2Session)
BUCKET_CLASS = staticmethod(Bucket)
Expand All @@ -46,3 +47,12 @@ def get_bucket_by_id(self, bucket_id: str) -> v3.Bucket:
return super().get_bucket_by_id(bucket_id)
except v3BucketIdNotFound as e:
raise BucketIdNotFound(e.bucket_id)

# one could contemplate putting "@limit_trace_arguments(only=('self', 'realm'))" here but logfury meta magic copies
# the appropriate attributes from base classes
def authorize_account(self, realm, application_key_id, application_key):
return super().authorize_account(
application_key_id=application_key_id,
application_key=application_key,
realm=realm,
)
1 change: 1 addition & 0 deletions changelog.d/+authorize_account.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Change v3.B2Api.authorize_account signature to make `realm` optional and `"production"` by default.
1 change: 1 addition & 0 deletions changelog.d/+make_tests_in_ci_verbose.infrastructure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `-v` to pytest in CI.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Run windows pypy3.9 tests on nightly builds.
71 changes: 63 additions & 8 deletions test/unit/api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,29 @@ def setUp(self):
self.raw_api = self.api.session.raw_api
(self.application_key_id, self.master_key) = self.raw_api.create_account()

@pytest.mark.apiver(from_ver=3)
def test_authorize_signature_v3_default_realm(self):
self.api.authorize_account(
self.application_key_id,
self.master_key,
)

@pytest.mark.apiver(from_ver=3)
def test_authorize_signature_v3_explicit_realm(self):
self.api.authorize_account(
self.application_key_id,
self.master_key,
'production',
)

@pytest.mark.apiver(to_ver=2)
def test_authorize_signature_up_to_ver_2(self):
self.api.authorize_account(
'production',
self.application_key_id,
self.master_key,
)

def test_get_file_info(self):
self._authorize_account()
bucket = self.api.create_bucket('bucket1', 'allPrivate')
Expand Down Expand Up @@ -312,7 +335,11 @@ def test_buckets_with_encryption(self):

# now check it with no readBucketEncryption permission to see that it's unknown
key = create_key(self.api, ['listBuckets'], 'key1')
self.api.authorize_account('production', key.id_, key.application_key)
self.api.authorize_account(
application_key_id=key.id_,
application_key=key.application_key,
realm='production',
)
buckets = {
b.name: b
for b in self.api.list_buckets() # scan again with new key
Expand Down Expand Up @@ -352,7 +379,11 @@ def test_reauthorize_with_app_key(self):
key = create_key(self.api, ['listBuckets'], 'key1')

# authorize with the key
self.api.authorize_account('production', key.id_, key.application_key)
self.api.authorize_account(
application_key_id=key.id_,
application_key=key.application_key,
realm='production',
)

# expire the auth token we just got
self.raw_api.expire_auth_token(self.account_info.get_account_auth_token())
Expand All @@ -365,22 +396,34 @@ def test_list_buckets_with_restriction(self):
bucket1 = self.api.create_bucket('bucket1', 'allPrivate')
self.api.create_bucket('bucket2', 'allPrivate')
key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_)
self.api.authorize_account('production', key.id_, key.application_key)
self.api.authorize_account(
application_key_id=key.id_,
application_key=key.application_key,
realm='production',
)
assert [b.name for b in self.api.list_buckets(bucket_name=bucket1.name)] == ['bucket1']

def test_get_bucket_by_name_with_bucket_restriction(self):
self._authorize_account()
bucket1 = self.api.create_bucket('bucket1', 'allPrivate')
key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_)
self.api.authorize_account('production', key.id_, key.application_key)
self.api.authorize_account(
application_key_id=key.id_,
application_key=key.application_key,
realm='production',
)
assert self.api.get_bucket_by_name('bucket1').id_ == bucket1.id_

def test_list_buckets_with_restriction_and_wrong_name(self):
self._authorize_account()
bucket1 = self.api.create_bucket('bucket1', 'allPrivate')
bucket2 = self.api.create_bucket('bucket2', 'allPrivate')
key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_)
self.api.authorize_account('production', key.id_, key.application_key)
self.api.authorize_account(
application_key_id=key.id_,
application_key=key.application_key,
realm='production',
)
with pytest.raises(RestrictedBucket) as excinfo:
self.api.list_buckets(bucket_name=bucket2.name)
assert str(excinfo.value) == 'Application key is restricted to bucket: bucket1'
Expand All @@ -390,7 +433,11 @@ def test_list_buckets_with_restriction_and_no_name(self):
bucket1 = self.api.create_bucket('bucket1', 'allPrivate')
self.api.create_bucket('bucket2', 'allPrivate')
key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_)
self.api.authorize_account('production', key.id_, key.application_key)
self.api.authorize_account(
application_key_id=key.id_,
application_key=key.application_key,
realm='production',
)
with pytest.raises(RestrictedBucket) as excinfo:
self.api.list_buckets()
assert str(excinfo.value) == 'Application key is restricted to bucket: bucket1'
Expand All @@ -400,13 +447,21 @@ def test_list_buckets_with_restriction_and_wrong_id(self):
bucket1 = self.api.create_bucket('bucket1', 'allPrivate')
self.api.create_bucket('bucket2', 'allPrivate')
key = create_key(self.api, ['listBuckets'], 'key1', bucket_id=bucket1.id_)
self.api.authorize_account('production', key.id_, key.application_key)
self.api.authorize_account(
application_key_id=key.id_,
application_key=key.application_key,
realm='production',
)
with pytest.raises(RestrictedBucket) as excinfo:
self.api.list_buckets(bucket_id='not the one bound to the key')
assert str(excinfo.value) == f'Application key is restricted to bucket: {bucket1.id_}'

def _authorize_account(self):
self.api.authorize_account('production', self.application_key_id, self.master_key)
self.api.authorize_account(
application_key_id=self.application_key_id,
application_key=self.master_key,
realm='production',
)

def test_update_file_retention(self):
self._authorize_account()
Expand Down
24 changes: 20 additions & 4 deletions test/unit/bucket/test_bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,11 @@ def setUp(self):
self.api = self.get_api()
self.simulator = self.api.session.raw_api
(self.account_id, self.master_key) = self.simulator.create_account()
self.api.authorize_account('production', self.account_id, self.master_key)
self.api.authorize_account(
application_key_id=self.account_id,
application_key=self.master_key,
realm='production',
)
self.api_url = self.account_info.get_api_url()
self.account_auth_token = self.account_info.get_account_auth_token()
self.bucket = self.api.create_bucket(self.bucket_name, 'allPublic')
Expand Down Expand Up @@ -463,7 +467,11 @@ def test_version_by_name_file_lock(self):
]
)

low_perm_api.authorize_account('production', low_perm_key.id_, low_perm_key.application_key)
low_perm_api.authorize_account(
application_key_id=low_perm_key.id_,
application_key=low_perm_key.application_key,
realm='production',
)
low_perm_bucket = low_perm_api.get_bucket_by_name('my-bucket-with-file-lock')

file_version = low_perm_bucket.get_file_info_by_name('a')
Expand Down Expand Up @@ -2680,7 +2688,11 @@ def test_authorize_for_bucket_ensures_cache(self):
bucket_id=self.bucket_id,
)

self.api.authorize_account('production', key.id_, key.application_key)
self.api.authorize_account(
application_key_id=key.id_,
application_key=key.application_key,
realm='production',
)

# Check whether the bucket fetching performs an API call.
with mock.patch.object(self.api, 'list_buckets') as mock_list_buckets:
Expand All @@ -2702,7 +2714,11 @@ def test_authorize_for_non_existing_bucket(self):
)

with self.assertRaises(RestrictedBucketMissing):
self.api.authorize_account('production', key.id_, key.application_key)
self.api.authorize_account(
application_key_id=key.id_,
application_key=key.application_key,
realm='production',
)


class TestDownloadLocalDirectoryIssues(TestCaseWithBucket):
Expand Down
6 changes: 5 additions & 1 deletion test/unit/file_version/test_file_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ def setUp(self):
)
self.raw_api = self.api.session.raw_api
(self.application_key_id, self.master_key) = self.raw_api.create_account()
self.api.authorize_account('production', self.application_key_id, self.master_key)
self.api.authorize_account(
application_key_id=self.application_key_id,
application_key=self.master_key,
realm='production',
)
self.bucket = self.api.create_bucket('testbucket', 'allPrivate', is_file_lock_enabled=True)
self.file_version = self.bucket.upload_bytes(
b'nothing', 'test_file', cache_control='private, max-age=3600'
Expand Down
6 changes: 5 additions & 1 deletion test/unit/replication/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@ def api() -> B2Api:

simulator = api.session.raw_api
account_id, master_key = simulator.create_account()
api.authorize_account('production', account_id, master_key)
api.authorize_account(
application_key_id=account_id,
application_key=master_key,
realm='production',
)
# api_url = account_info.get_api_url()
# account_auth_token = account_info.get_account_auth_token()1
return api
Expand Down
6 changes: 5 additions & 1 deletion test/unit/v_all/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,11 @@ def setUp(self):
(self.application_key_id, self.master_key) = self.raw_api.create_account()

def _authorize_account(self):
self.api.authorize_account('production', self.application_key_id, self.master_key)
self.api.authorize_account(
realm='production',
application_key_id=self.application_key_id,
application_key=self.master_key,
)

@pytest.mark.apiver(to_ver=1)
def test_get_bucket_by_id_up_to_v1(self):
Expand Down
6 changes: 5 additions & 1 deletion test/unit/v_all/test_replication.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ def setUp(self):
self.application_key_id, self.master_key = self.raw_api.create_account()

def _authorize_account(self):
self.api.authorize_account('production', self.application_key_id, self.master_key)
self.api.authorize_account(
realm='production',
application_key_id=self.application_key_id,
application_key=self.master_key,
)

@pytest.mark.apiver(from_ver=2)
def test_setup_both(self):
Expand Down