github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/docs/sts/client_grants/__init__.py (about)

     1  # -*- coding: utf-8 -*-
     2  import json
     3  # standard.
     4  import os
     5  
     6  import certifi
     7  # Dependencies
     8  import urllib3
     9  from botocore.credentials import CredentialProvider, RefreshableCredentials
    10  from botocore.exceptions import CredentialRetrievalError
    11  from dateutil.parser import parse
    12  
    13  from .sts_element import STSElement
    14  
    15  
    16  class ClientGrantsCredentialProvider(CredentialProvider):
    17      """
    18      ClientGrantsCredentialProvider implements CredentialProvider compatible
    19      implementation to be used with boto_session
    20      """
    21      METHOD = 'assume-role-client-grants'
    22      CANONICAL_NAME = 'AssumeRoleClientGrants'
    23  
    24      def __init__(self, cid, csec,
    25                   idp_ep='http://localhost:8080/auth/realms/minio/protocol/openid-connect/token',
    26                   sts_ep='http://localhost:9000'):
    27          self.cid = cid
    28          self.csec = csec
    29          self.idp_ep = idp_ep
    30          self.sts_ep = sts_ep
    31  
    32          # Load CA certificates from SSL_CERT_FILE file if set
    33          ca_certs = os.environ.get('SSL_CERT_FILE')
    34          if not ca_certs:
    35              ca_certs = certifi.where()
    36  
    37          self._http = urllib3.PoolManager(
    38              timeout=urllib3.Timeout.DEFAULT_TIMEOUT,
    39              maxsize=10,
    40              cert_reqs='CERT_NONE',
    41              ca_certs=ca_certs,
    42              retries=urllib3.Retry(
    43                  total=5,
    44                  backoff_factor=0.2,
    45                  status_forcelist=[500, 502, 503, 504]
    46              )
    47          )
    48  
    49      def load(self):
    50          """
    51          Search for credentials with client_grants
    52          """
    53          if self.cid is not None:
    54              fetcher = self._create_credentials_fetcher()
    55              return RefreshableCredentials.create_from_metadata(
    56                  metadata=fetcher(),
    57                  refresh_using=fetcher,
    58                  method=self.METHOD,
    59              )
    60          else:
    61              return None
    62  
    63      def _create_credentials_fetcher(self):
    64          method = self.METHOD
    65  
    66          def fetch_credentials():
    67              # HTTP headers are case insensitive filter out
    68              # all duplicate headers and pick one.
    69              headers = {}
    70              headers['content-type'] = 'application/x-www-form-urlencoded'
    71              headers['authorization'] = urllib3.make_headers(
    72                  basic_auth='%s:%s' % (self.cid, self.csec))['authorization']
    73  
    74              response = self._http.urlopen('POST', self.idp_ep,
    75                                            body="grant_type=client_credentials",
    76                                            headers=headers,
    77                                            preload_content=True,
    78                                            )
    79              if response.status != 200:
    80                  message = "Credential refresh failed, response: %s"
    81                  raise CredentialRetrievalError(
    82                      provider=method,
    83                      error_msg=message % response.status,
    84                  )
    85  
    86              creds = json.loads(response.data)
    87  
    88              query = {}
    89              query['Action'] = 'AssumeRoleWithClientGrants'
    90              query['Token'] = creds['access_token']
    91              query['DurationSeconds'] = creds['expires_in']
    92              query['Version'] = '2011-06-15'
    93  
    94              query_components = []
    95              for key in query:
    96                  if query[key] is not None:
    97                      query_components.append("%s=%s" % (key, query[key]))
    98  
    99              query_string = '&'.join(query_components)
   100              sts_ep_url = self.sts_ep
   101              if query_string:
   102                  sts_ep_url = self.sts_ep + '?' + query_string
   103  
   104              response = self._http.urlopen(
   105                  'POST', sts_ep_url, preload_content=True)
   106              if response.status != 200:
   107                  message = "Credential refresh failed, response: %s"
   108                  raise CredentialRetrievalError(
   109                      provider=method,
   110                      error_msg=message % response.status,
   111                  )
   112  
   113              return parse_grants_response(response.data)
   114  
   115          def parse_grants_response(data):
   116              """
   117              Parser for AssumeRoleWithClientGrants response
   118  
   119              :param data: Response data for AssumeRoleWithClientGrants request
   120              :return: dict
   121              """
   122              root = STSElement.fromstring(
   123                  'AssumeRoleWithClientGrantsResponse', data)
   124              result = root.find('AssumeRoleWithClientGrantsResult')
   125              creds = result.find('Credentials')
   126              return dict(
   127                  access_key=creds.get_child_text('AccessKeyId'),
   128                  secret_key=creds.get_child_text('SecretAccessKey'),
   129                  token=creds.get_child_text('SessionToken'),
   130                  expiry_time=parse(creds.get_child_text('Expiration')).isoformat())
   131  
   132          return fetch_credentials