Repository navigation
PYTHON-5786 Add test variant for the Secure Frontend Processor (monguard) #2948
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # Copyright 2026-present MongoDB, Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """Test connectivity and authentication through an Atlas Secure Frontend Processor (SFP/Monguard).""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import os | ||
| import sys | ||
| import unittest | ||
| from typing import Any | ||
|
|
||
| import pytest | ||
|
|
||
| sys.path[0:0] = [""] | ||
|
|
||
| from bson import ObjectId | ||
| from pymongo.asynchronous.mongo_client import AsyncMongoClient | ||
| from pymongo.server_api import ServerApi | ||
| from test.asynchronous import AsyncPyMongoTestCase | ||
|
|
||
| _IS_SYNC = False | ||
|
|
||
| pytestmark = pytest.mark.sfp | ||
|
|
||
| # Each authenticated test must run under each of these variations: | ||
| # no additional configuration, a compressor enabled, and Server API v1. | ||
| VARIATIONS: dict[str, dict[str, Any]] = { | ||
| "baseline": {}, | ||
| "compressor": {"compressors": "zlib"}, | ||
| "server_api": {"server_api": ServerApi("1")}, | ||
| } | ||
|
|
||
|
|
||
| def _require_env(name: str) -> str: | ||
| value = os.environ.get(name) | ||
| if not value: | ||
| raise Exception(f"Must set {name} env variable to test.") | ||
| return value | ||
|
|
||
|
|
||
| class TestAtlasSFP(AsyncPyMongoTestCase): | ||
| async def assert_ping(self, client: AsyncMongoClient) -> None: | ||
| response = await client.admin.command("ping") | ||
| self.assertEqual(response["ok"], 1) | ||
|
|
||
| async def assert_connection_status(self, client: AsyncMongoClient, authenticated: bool) -> None: | ||
| response = await client.admin.command("connectionStatus") | ||
| self.assertEqual(response["ok"], 1) | ||
| users = response["authInfo"]["authenticatedUsers"] | ||
| if authenticated: | ||
| self.assertGreaterEqual(len(users), 1) | ||
| else: | ||
| self.assertEqual(users, []) | ||
|
|
||
| async def assert_crud(self, client: AsyncMongoClient) -> None: | ||
| # Use a unique collection name for each test run and drop it | ||
| # afterward, regardless of test success or failure. | ||
| collection = client.db[f"sfp_test_{ObjectId()}"] | ||
| self.addAsyncCleanup(collection.drop) | ||
| result = await collection.insert_one({"_id": 0}) | ||
| self.assertEqual(result.inserted_id, 0) | ||
| document = await collection.find_one({"_id": 0}) | ||
| self.assertEqual(document, {"_id": 0}) | ||
|
|
||
| async def test_unauthenticated(self): | ||
| client = self.simple_client(_require_env("SFP_ATLAS_URI")) | ||
| await self.assert_ping(client) | ||
| await self.assert_connection_status(client, authenticated=False) | ||
|
|
||
| async def test_scram_sha_256(self): | ||
| uri = _require_env("SFP_ATLAS_URI") | ||
| username = _require_env("SFP_ATLAS_USER") | ||
| password = _require_env("SFP_ATLAS_PASSWORD") | ||
| for variation, kwargs in VARIATIONS.items(): | ||
| with self.subTest(variation=variation): | ||
| client = self.simple_client( | ||
| uri, | ||
| username=username, | ||
| password=password, | ||
| authMechanism="SCRAM-SHA-256", | ||
| **kwargs, | ||
| ) | ||
| await self.assert_ping(client) | ||
| await self.assert_connection_status(client, authenticated=True) | ||
| await self.assert_crud(client) | ||
|
|
||
| async def test_x509(self): | ||
| uri = _require_env("SFP_ATLAS_X509_URI") | ||
| cert = _require_env("SFP_ATLAS_X509_CERT") | ||
| for variation, kwargs in VARIATIONS.items(): | ||
| with self.subTest(variation=variation): | ||
| client = self.simple_client(uri, tlsCertificateKeyFile=cert, **kwargs) | ||
| await self.assert_ping(client) | ||
| await self.assert_connection_status(client, authenticated=True) | ||
| await self.assert_crud(client) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # Copyright 2026-present MongoDB, Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """Test connectivity and authentication through an Atlas Secure Frontend Processor (SFP/Monguard).""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import os | ||
| import sys | ||
| import unittest | ||
| from typing import Any | ||
|
|
||
| import pytest | ||
|
|
||
| sys.path[0:0] = [""] | ||
|
|
||
| from bson import ObjectId | ||
| from pymongo.server_api import ServerApi | ||
| from pymongo.synchronous.mongo_client import MongoClient | ||
| from test import PyMongoTestCase | ||
|
|
||
| _IS_SYNC = True | ||
|
|
||
| pytestmark = pytest.mark.sfp | ||
|
|
||
| # Each authenticated test must run under each of these variations: | ||
| # no additional configuration, a compressor enabled, and Server API v1. | ||
| VARIATIONS: dict[str, dict[str, Any]] = { | ||
| "baseline": {}, | ||
| "compressor": {"compressors": "zlib"}, | ||
| "server_api": {"server_api": ServerApi("1")}, | ||
| } | ||
|
|
||
|
|
||
| def _require_env(name: str) -> str: | ||
| value = os.environ.get(name) | ||
| if not value: | ||
| raise Exception(f"Must set {name} env variable to test.") | ||
| return value | ||
|
|
||
|
|
||
| class TestAtlasSFP(PyMongoTestCase): | ||
| def assert_ping(self, client: MongoClient) -> None: | ||
| response = client.admin.command("ping") | ||
| self.assertEqual(response["ok"], 1) | ||
|
|
||
| def assert_connection_status(self, client: MongoClient, authenticated: bool) -> None: | ||
| response = client.admin.command("connectionStatus") | ||
| self.assertEqual(response["ok"], 1) | ||
| users = response["authInfo"]["authenticatedUsers"] | ||
| if authenticated: | ||
| self.assertGreaterEqual(len(users), 1) | ||
| else: | ||
| self.assertEqual(users, []) | ||
|
|
||
| def assert_crud(self, client: MongoClient) -> None: | ||
| # Use a unique collection name for each test run and drop it | ||
| # afterward, regardless of test success or failure. | ||
| collection = client.db[f"sfp_test_{ObjectId()}"] | ||
| self.addCleanup(collection.drop) | ||
| result = collection.insert_one({"_id": 0}) | ||
| self.assertEqual(result.inserted_id, 0) | ||
| document = collection.find_one({"_id": 0}) | ||
| self.assertEqual(document, {"_id": 0}) | ||
|
|
||
| def test_unauthenticated(self): | ||
| client = self.simple_client(_require_env("SFP_ATLAS_URI")) | ||
| self.assert_ping(client) | ||
| self.assert_connection_status(client, authenticated=False) | ||
|
|
||
| def test_scram_sha_256(self): | ||
| uri = _require_env("SFP_ATLAS_URI") | ||
| username = _require_env("SFP_ATLAS_USER") | ||
| password = _require_env("SFP_ATLAS_PASSWORD") | ||
| for variation, kwargs in VARIATIONS.items(): | ||
| with self.subTest(variation=variation): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This requires pytest 9.0+. We need to bump our min version in requirements and pytest.ini config
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually unittest.subTest, not the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "In addition, unittest.TestCase.subTest() is now also supported."
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I explicitly looked this up because I knew this was a feature gap, I've run into it before
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With pytest < 9.0, subtests still run, but if one fails it stops there instead of running the others.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, thanks for verifying |
||
| client = self.simple_client( | ||
| uri, | ||
| username=username, | ||
| password=password, | ||
| authMechanism="SCRAM-SHA-256", | ||
| **kwargs, | ||
| ) | ||
| self.assert_ping(client) | ||
| self.assert_connection_status(client, authenticated=True) | ||
| self.assert_crud(client) | ||
|
|
||
| def test_x509(self): | ||
| uri = _require_env("SFP_ATLAS_X509_URI") | ||
| cert = _require_env("SFP_ATLAS_X509_CERT") | ||
| for variation, kwargs in VARIATIONS.items(): | ||
| with self.subTest(variation=variation): | ||
| client = self.simple_client(uri, tlsCertificateKeyFile=cert, **kwargs) | ||
| self.assert_ping(client) | ||
| self.assert_connection_status(client, authenticated=True) | ||
| self.assert_crud(client) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() | ||
Uh oh!
There was an error while loading. Please reload this page.