uri.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import calendar
  2. import json
  3. import time
  4. from urllib.parse import urlparse, parse_qs
  5. from alibabacloud_credentials.provider.refreshable import Credentials, RefreshResult, RefreshCachedSupplier
  6. from alibabacloud_credentials.http import HttpOptions
  7. from Tea.core import TeaCore
  8. from alibabacloud_credentials_api import ICredentialsProvider
  9. from alibabacloud_credentials.utils import auth_util as au
  10. from alibabacloud_credentials.utils import parameter_helper as ph
  11. from alibabacloud_credentials.exceptions import CredentialException
  12. def _get_stale_time(expiration: int) -> int:
  13. if expiration < 0:
  14. return int(time.mktime(time.localtime())) + 60 * 60
  15. return expiration - 15 * 60
  16. class URLCredentialsProvider(ICredentialsProvider):
  17. DEFAULT_CONNECT_TIMEOUT = 5000
  18. DEFAULT_READ_TIMEOUT = 10000
  19. def __init__(self, *,
  20. uri: str = None,
  21. protocol: str = 'http',
  22. http_options: HttpOptions = None):
  23. self._uri = uri or au.environment_credentials_uri
  24. if self._uri is None or self._uri == '':
  25. raise ValueError('uri or environment variable ALIBABA_CLOUD_CREDENTIALS_URI cannot be empty')
  26. self._protocol = protocol
  27. self._http_options = http_options if http_options is not None else HttpOptions()
  28. self._runtime_options = {
  29. 'connectTimeout': self._http_options.connect_timeout if self._http_options.connect_timeout is not None else URLCredentialsProvider.DEFAULT_CONNECT_TIMEOUT,
  30. 'readTimeout': self._http_options.read_timeout if self._http_options.read_timeout is not None else URLCredentialsProvider.DEFAULT_READ_TIMEOUT,
  31. 'httpsProxy': self._http_options.proxy
  32. }
  33. self._credentials_cache = RefreshCachedSupplier(
  34. refresh_callable=self._refresh_credentials,
  35. refresh_callable_async=self._refresh_credentials_async,
  36. )
  37. def get_credentials(self) -> Credentials:
  38. return self._credentials_cache._sync_call()
  39. async def get_credentials_async(self) -> Credentials:
  40. return await self._credentials_cache._async_call()
  41. def _refresh_credentials(self) -> RefreshResult[Credentials]:
  42. r = urlparse(self._uri)
  43. tea_request = ph.get_new_request()
  44. tea_request.headers['host'] = r.hostname
  45. tea_request.port = r.port
  46. tea_request.protocol = r.scheme or self._protocol or 'http'
  47. tea_request.method = 'GET'
  48. tea_request.pathname = r.path
  49. for key, values in parse_qs(r.query).items():
  50. for value in values:
  51. tea_request.query[key] = value
  52. response = TeaCore.do_action(tea_request, self._runtime_options)
  53. if response.status_code != 200:
  54. raise CredentialException(
  55. f'error refreshing credentials from {self._uri}, http_code={str(response.status_code)}, result: {response.body.decode("utf-8")}')
  56. body = response.body.decode('utf-8')
  57. dic = json.loads(body)
  58. content_code = dic.get('Code')
  59. if content_code != "Success" or 'AccessKeyId' not in dic or 'AccessKeySecret' not in dic or 'SecurityToken' not in dic or 'Expiration' not in dic:
  60. raise CredentialException(
  61. f'error retrieving credentials from {self._uri} result: {response.body.decode("utf-8")}')
  62. # 先转换为时间数组
  63. time_array = time.strptime(dic.get('Expiration'), '%Y-%m-%dT%H:%M:%SZ')
  64. # 转换为时间戳
  65. expiration = calendar.timegm(time_array)
  66. credentials = Credentials(
  67. access_key_id=dic.get('AccessKeyId'),
  68. access_key_secret=dic.get('AccessKeySecret'),
  69. security_token=dic.get('SecurityToken'),
  70. expiration=expiration,
  71. provider_name=self.get_provider_name()
  72. )
  73. return RefreshResult(value=credentials,
  74. stale_time=_get_stale_time(expiration))
  75. async def _refresh_credentials_async(self) -> RefreshResult[Credentials]:
  76. r = urlparse(self._uri)
  77. tea_request = ph.get_new_request()
  78. tea_request.headers['host'] = r.hostname
  79. tea_request.port = r.port
  80. tea_request.protocol = r.scheme or self._protocol or 'http'
  81. tea_request.method = 'GET'
  82. tea_request.pathname = r.path
  83. for key, values in parse_qs(r.query).items():
  84. for value in values:
  85. tea_request.query[key] = value
  86. response = await TeaCore.async_do_action(tea_request, self._runtime_options)
  87. if response.status_code != 200:
  88. raise CredentialException(
  89. f'error refreshing credentials from {self._uri}, http_code={str(response.status_code)}, result: {response.body.decode("utf-8")}')
  90. body = response.body.decode('utf-8')
  91. dic = json.loads(body)
  92. content_code = dic.get('Code')
  93. if content_code != "Success" or 'AccessKeyId' not in dic or 'AccessKeySecret' not in dic or 'SecurityToken' not in dic or 'Expiration' not in dic:
  94. raise CredentialException(
  95. f'error retrieving credentials from {self._uri} result: {response.body.decode("utf-8")}')
  96. # 先转换为时间数组
  97. time_array = time.strptime(dic.get('Expiration'), '%Y-%m-%dT%H:%M:%SZ')
  98. # 转换为时间戳
  99. expiration = calendar.timegm(time_array)
  100. credentials = Credentials(
  101. access_key_id=dic.get('AccessKeyId'),
  102. access_key_secret=dic.get('AccessKeySecret'),
  103. security_token=dic.get('SecurityToken'),
  104. expiration=expiration,
  105. provider_name=self.get_provider_name()
  106. )
  107. return RefreshResult(value=credentials,
  108. stale_time=_get_stale_time(expiration))
  109. def get_provider_name(self) -> str:
  110. return 'credential_uri'