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