auth_aws.py 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. # Copyright 2020-present MongoDB, Inc.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """MONGODB-AWS Authentication helpers."""
  15. try:
  16. import pymongo_auth_aws
  17. from pymongo_auth_aws import (AwsCredential,
  18. AwsSaslContext,
  19. PyMongoAuthAwsError)
  20. _HAVE_MONGODB_AWS = True
  21. except ImportError:
  22. class AwsSaslContext(object):
  23. def __init__(self, credentials):
  24. pass
  25. _HAVE_MONGODB_AWS = False
  26. import bson
  27. from bson.binary import Binary
  28. from bson.son import SON
  29. from pymongo.errors import ConfigurationError, OperationFailure
  30. class _AwsSaslContext(AwsSaslContext):
  31. # Dependency injection:
  32. def binary_type(self):
  33. """Return the bson.binary.Binary type."""
  34. return Binary
  35. def bson_encode(self, doc):
  36. """Encode a dictionary to BSON."""
  37. return bson.encode(doc)
  38. def bson_decode(self, data):
  39. """Decode BSON to a dictionary."""
  40. return bson.decode(data)
  41. def _authenticate_aws(credentials, sock_info):
  42. """Authenticate using MONGODB-AWS.
  43. """
  44. if not _HAVE_MONGODB_AWS:
  45. raise ConfigurationError(
  46. "MONGODB-AWS authentication requires pymongo-auth-aws: "
  47. "install with: python -m pip install 'pymongo[aws]'")
  48. if sock_info.max_wire_version < 9:
  49. raise ConfigurationError(
  50. "MONGODB-AWS authentication requires MongoDB version 4.4 or later")
  51. try:
  52. ctx = _AwsSaslContext(AwsCredential(
  53. credentials.username, credentials.password,
  54. credentials.mechanism_properties.aws_session_token))
  55. client_payload = ctx.step(None)
  56. client_first = SON([('saslStart', 1),
  57. ('mechanism', 'MONGODB-AWS'),
  58. ('payload', client_payload)])
  59. server_first = sock_info.command('$external', client_first)
  60. res = server_first
  61. # Limit how many times we loop to catch protocol / library issues
  62. for _ in range(10):
  63. client_payload = ctx.step(res['payload'])
  64. cmd = SON([('saslContinue', 1),
  65. ('conversationId', server_first['conversationId']),
  66. ('payload', client_payload)])
  67. res = sock_info.command('$external', cmd)
  68. if res['done']:
  69. # SASL complete.
  70. break
  71. except PyMongoAuthAwsError as exc:
  72. # Convert to OperationFailure and include pymongo-auth-aws version.
  73. raise OperationFailure('%s (pymongo-auth-aws version %s)' % (
  74. exc, pymongo_auth_aws.__version__))