Authenticate using MONGODB-AWS.
(credentials: MongoCredential, conn: AsyncConnection)
| 30 | |
| 31 | |
| 32 | async def _authenticate_aws(credentials: MongoCredential, conn: AsyncConnection) -> None: |
| 33 | """Authenticate using MONGODB-AWS.""" |
| 34 | try: |
| 35 | import pymongo_auth_aws # type:ignore[import] |
| 36 | except ImportError as e: |
| 37 | raise ConfigurationError( |
| 38 | "MONGODB-AWS authentication requires pymongo-auth-aws: " |
| 39 | "install with: python -m pip install 'pymongo[aws]'" |
| 40 | ) from e |
| 41 | # Delayed import. |
| 42 | from pymongo_auth_aws.auth import ( # type:ignore[import] |
| 43 | set_cached_credentials, |
| 44 | set_use_cached_credentials, |
| 45 | ) |
| 46 | |
| 47 | set_use_cached_credentials(True) |
| 48 | |
| 49 | if conn.max_wire_version < 9: |
| 50 | raise ConfigurationError("MONGODB-AWS authentication requires MongoDB version 4.4 or later") |
| 51 | |
| 52 | class AwsSaslContext(pymongo_auth_aws.AwsSaslContext): # type: ignore |
| 53 | # Dependency injection: |
| 54 | def binary_type(self) -> Type[Binary]: |
| 55 | """Return the bson.binary.Binary type.""" |
| 56 | return Binary |
| 57 | |
| 58 | def bson_encode(self, doc: Mapping[str, Any]) -> bytes: |
| 59 | """Encode a dictionary to BSON.""" |
| 60 | return bson.encode(doc) |
| 61 | |
| 62 | def bson_decode(self, data: _ReadableBuffer) -> Mapping[str, Any]: |
| 63 | """Decode BSON to a dictionary.""" |
| 64 | return bson.decode(data) |
| 65 | |
| 66 | try: |
| 67 | ctx = AwsSaslContext( |
| 68 | pymongo_auth_aws.AwsCredential( |
| 69 | credentials.username, |
| 70 | credentials.password, |
| 71 | credentials.mechanism_properties.aws_session_token, |
| 72 | ) |
| 73 | ) |
| 74 | client_payload = ctx.step(None) |
| 75 | client_first = {"saslStart": 1, "mechanism": "MONGODB-AWS", "payload": client_payload} |
| 76 | server_first = await conn.command("$external", client_first) |
| 77 | res = server_first |
| 78 | # Limit how many times we loop to catch protocol / library issues |
| 79 | for _ in range(10): |
| 80 | client_payload = ctx.step(res["payload"]) |
| 81 | cmd = { |
| 82 | "saslContinue": 1, |
| 83 | "conversationId": server_first["conversationId"], |
| 84 | "payload": client_payload, |
| 85 | } |
| 86 | res = await conn.command("$external", cmd) |
| 87 | if res["done"]: |
| 88 | # SASL complete. |
| 89 | break |
nothing calls this directly
no test coverage detected