def _build_realm_token_url(self, response, repository, actions) -> str: www_authenticate_response = www_authenticate.parse( response.headers["WWW-Authenticate"] ) if "bearer" in www_authenticate_response: if self.actions: scope = f"repository:{repository}:" + ",".join(actions) elif "scope" in www_authenticate_response["bearer"]: scope = www_authenticate_response["bearer"]["scope"] else: scope = "" url_parts = list( parse.urlparse(www_authenticate_response["bearer"]["realm"]) ) query = parse.parse_qs(url_parts[4]) query.update( { "service": www_authenticate_response["bearer"]["service"], "scope": scope, } ) url_parts[4] = parse.urlencode(query, True) url_parts[0] = "https" return parse.urlunparse(url_parts)
def _get_auth_token(self, credentials: str, endpoint: str, scope: str) -> str: """ Retrieves the registry auth token for a given scope. Args: credentials: The credentials to use to retrieve the auth token. endpoint: Registry endpoint for which to retrieve the token. scope: The scope of the auth token. Returns: The corresponding auth token, or None. """ # https://github.com/docker/distribution/blob/master/docs/spec/auth/token.md if not self.token: # Test using HTTP basic authentication to retrieve the www-authenticate response header ... headers = {"Authorization": "Basic {0}".format(credentials)} url = "https://{0}/v2/".format(endpoint) response = requests.get(url, headers=headers) auth_params = www_authenticate.parse( response.headers["Www-Authenticate"]) bearer = auth_params["bearer"] url = RegistryV2ImageSource.DOCKERHUB_AUTH_URL_PATTERN.format( bearer["realm"], bearer["service"], scope) response = requests.get(url, headers=headers) # LOGGER.debug("Token Response: %s", response.content) must_be_equal(200, response.status_code, "Failed to retrieve bearer token") self.token = response.json()["token"] return self.token
def _request(self, method, url, *, headers=None, **kwargs): headers = headers or {} response = yield from super()._request(method, url, headers=headers, **kwargs) challenges = self.get_challenges(response) if response.status == UNAUTHORIZED and 'negotiate' in challenges: host = self.get_hostname(response) ctx = self.get_context(host) out_token = self.negotiate_step(ctx) while True: response.close() if out_token: headers['Authorization'] = 'Negotiate ' + out_token response = yield from super()._request(method, url, headers=headers, **kwargs) challenges = www_authenticate.parse( response.headers.get('WWW-Authenticate')) in_token = challenges['negotiate'] self.negotiate_step(ctx, in_token) if ctx.complete: break return response
def _get_token_auth(self, res, repository): parsed = www_authenticate.parse(res.headers['www-authenticate']) if 'bearer' not in parsed: return challenge = parsed['bearer'] realm = challenge.get('realm') service = challenge.get('service') scope = challenge.get('scope') if scope is None and repository: scope = f'repository:{repository}:pull' logger.info("Getting token auth, realm=%s, service=%s, scope=%s", realm, service, scope) if not realm: return False self.auth = self.orig_auth params = [] if service: params.append(('service', service)) if scope: params.append(('scope', scope)) url = realm + '?' + urlencode(params) res = self.session.get(url, **self._kwargs(dict())) if res.status_code != 200: return False token = res.json()['token'] self.auth = BearerAuth(token) return True
def get_challenges(self, response): challenges = {} for k, v in response.headers.items(): if k.lower() == 'www-authenticate': challenges.update(www_authenticate.parse(v)) logger.debug('Server challenges: {}'.format(challenges)) return challenges
def authenticate(self, username=None, password=None, actions=None, response=None): """ Authenticate to the registry, using a username and password if supplied, otherwise as the anonymous user. :param username: User name to authenticate as. :type username: str :param password: User's password. :type password: str :param actions: If you know which types of operation you need to make on the registry, specify them here. Valid actions are ``pull``, ``push`` and ``*``. :type actions: list :param response: When the ``auth`` function you passed to :class:`DXFBase`'s constructor is called, it is passed a HTTP response object. Pass it back to :meth:`authenticate` to have it automatically detect which actions are required. :type response: requests.Response :rtype: str :returns: Authentication token, if the registry supports bearer tokens. Otherwise ``None``, and HTTP Basic auth is used. """ if self._insecure: raise exceptions.DXFAuthInsecureError() if response is None: response = self._sessions[0].get(self._base_url) # pylint: disable=no-member if response.status_code != requests.codes.unauthorized: raise exceptions.DXFUnexpectedStatusCodeError(response.status_code, requests.codes.unauthorized) parsed = www_authenticate.parse(response.headers['www-authenticate']) if username is not None and password is not None: headers = { 'Authorization': 'Basic ' + base64.b64encode(_to_bytes_2and3(username + ':' + password)).decode('utf-8') } else: headers = {} if 'bearer' in parsed: info = parsed['bearer'] if actions and self._repo: scope = 'repository:' + self._repo + ':' + ','.join(actions) else: scope = info['scope'] url_parts = list(urlparse.urlparse(info['realm'])) query = urlparse.parse_qs(url_parts[4]) query.update({ 'service': info['service'], 'scope': scope }) url_parts[4] = urlencode(query, True) url_parts[0] = 'https' if self._auth_host: url_parts[1] = self._auth_host auth_url = urlparse.urlunparse(url_parts) r = self._sessions[0].get(auth_url, headers=headers) _raise_for_status(r) self.token = r.json()['token'] return self._token else: self._headers = headers
def bearer_request(self, method, url, auth, **kwargs): log.debug("bearer_request()") log.debug('[registry][request]: {0} {1}'.format(method, url)) if 'Authorization' in kwargs['headers']: log.debug('[registry][request]: Authorization header:') token_parsed = kwargs['headers']['Authorization'].split('.') log.debug( pprint.pformat(json.loads(decode_base64(token_parsed[0])))) log.debug( pprint.pformat(json.loads(decode_base64(token_parsed[1])))) res = requests.request(method, url, **kwargs) if str(res.status_code)[0] == '2': log.debug("[registry] accepted") return res, kwargs['headers']['Authorization'] if res.status_code == 401: log.debug("[registry] Access denied. Refreshing token...") oauth = www_authenticate.parse(res.headers['Www-Authenticate']) log.debug('[auth][answer] Auth header:') log.debug(pprint.pformat(oauth['bearer'])) log.info('retreiving bearer token for {0}'.format( oauth['bearer']['scope'])) # request_url = '{0}?service={1}&scope={2}'.format(oauth['bearer']['realm'], # oauth['bearer']['service'], # oauth['bearer']['scope']) request_url = '{0}?service={1}&scope={2}'.format( oauth['bearer']['realm'], oauth['bearer']['service'], oauth['bearer']['scope']) log.debug('[debug][auth][request] Refreshing auth token: POST {0}'. format(request_url)) try_oauth = requests.post(request_url, auth=auth, **kwargs) try: token = json.loads(try_oauth._content)['token'] log.info(">>> token: {}".format(token)) except SyntaxError: log.error("\n\ncouldn't accure token: {0}".format( try_oauth._content)) sys.exit(1) token_parsed = token.split('.') log.debug('[auth] token issued: ') log.debug( pprint.pformat(json.loads(decode_base64(token_parsed[0])))) log.debug( pprint.pformat(json.loads(decode_base64(token_parsed[1])))) kwargs['headers']['Authorization'] = 'Bearer {0}'.format(token) else: return res, kwargs['headers']['Authorization'] res = requests.request(method, url, **kwargs) return res, kwargs['headers']['Authorization']
def bearer_request(self, method, url, auth, **kwargs): global DEBUG if DEBUG: print("[debug][funcname]: bearer_request()") if DEBUG: print('[debug][registry][request]: {0} {1}'.format(method, url)) if 'Authorization' in kwargs['headers']: print('[debug][registry][request]: Authorization header:') token_parsed = kwargs['headers']['Authorization'].split('.') pprint.pprint(ast.literal_eval(decode_base64(token_parsed[0]))) pprint.pprint(ast.literal_eval(decode_base64(token_parsed[1]))) res = requests.request(method, url, **kwargs) if str(res.status_code)[0] == '2': if DEBUG: print("[debug][registry] accepted") return (res, kwargs['headers']['Authorization']) if res.status_code == 401: if DEBUG: print("[debug][registry] Access denied. Refreshing token...") oauth = www_authenticate.parse(res.headers['Www-Authenticate']) if DEBUG: print('[debug][auth][answer] Auth header:') pprint.pprint(oauth['bearer']) # print('[info] retreiving bearer token for {0}'.format(oauth['bearer']['scope'])) request_url = '{0}?service={1}&scope={2}'.format( oauth['bearer']['realm'], oauth['bearer']['service'], oauth['bearer']['scope']) if DEBUG: print('[debug][auth][request] Refreshing auth token: POST {0}'. format(request_url)) try_oauth = requests.post(request_url, auth=auth, **kwargs) try: token = ast.literal_eval(try_oauth._content)['token'] except SyntaxError: print('\n\n[ERROR] couldnt accure token: {0}'.format( try_oauth._content)) sys.exit(1) if DEBUG: print('[debug][auth] token issued: ') token_parsed = token.split('.') pprint.pprint(ast.literal_eval(decode_base64(token_parsed[0]))) pprint.pprint(ast.literal_eval(decode_base64(token_parsed[1]))) kwargs['headers']['Authorization'] = 'Bearer {0}'.format(token) else: return (res, kwargs['headers']['Authorization']) res = requests.request(method, url, **kwargs) return (res, kwargs['headers']['Authorization'])
def get_auth_header(url, method): r = getattr(requests, method)(url) if r.status_code == 401: try: r.headers['Www-Authenticate'] except KeyError: raise 'could not fetch bearer info from registry endpoint' else: return www_authenticate.parse(r.headers['Www-Authenticate']) else: raise 'invalid auth_header response code' + str(r.status_code)
def bearer_request(self, method, url, auth, **kwargs): log.debug("bearer_request()") log.debug('[registry][request]: {0} {1}'.format(method, url)) if 'Authorization' in kwargs['headers']: log.debug('[registry][request]: Authorization header:') token_parsed = kwargs['headers']['Authorization'].split('.') log.debug(pprint.pformat(json.loads(decode_base64(token_parsed[0])))) log.debug(pprint.pformat(json.loads(decode_base64(token_parsed[1])))) res = requests.request(method, url, **kwargs) if str(res.status_code)[0] == '2': log.debug("[registry] accepted") return res, kwargs['headers']['Authorization'] if res.status_code == 401: log.debug("[registry] Access denied. Refreshing token...") oauth = www_authenticate.parse(res.headers['Www-Authenticate']) log.debug('[auth][answer] Auth header:') log.debug(pprint.pformat(oauth['bearer'])) log.info('retreiving bearer token for {0}'.format(oauth['bearer']['scope'])) # request_url = '{0}?service={1}&scope={2}'.format(oauth['bearer']['realm'], # oauth['bearer']['service'], # oauth['bearer']['scope']) request_url = '{0}?service={1}&scope={2}'.format(oauth['bearer']['realm'], oauth['bearer']['service'], oauth['bearer']['scope']) log.debug('[debug][auth][request] Refreshing auth token: POST {0}'.format(request_url)) try_oauth = requests.post(request_url, auth=auth, **kwargs) try: token = json.loads(try_oauth._content)['token'] log.info(">>> token: {}".format(token)) except SyntaxError: log.error("\n\ncouldn't accure token: {0}".format(try_oauth._content)) sys.exit(1) token_parsed = token.split('.') log.debug('[auth] token issued: ') log.debug(pprint.pformat(json.loads(decode_base64(token_parsed[0])))) log.debug(pprint.pformat(json.loads(decode_base64(token_parsed[1])))) kwargs['headers']['Authorization'] = 'Bearer {0}'.format(token) else: return res, kwargs['headers']['Authorization'] res = requests.request(method, url, **kwargs) return res, kwargs['headers']['Authorization']
def authenticate( self, provider: str, creds: Optional[BasicAuthCreds] = None, cookies: Optional[Cookies] = None, ) -> None: """Authenticate against the specified provider. :param provider: Authorize against this provider. :param creds: The creds to use. If unspecified, assumes that creds are set in the netrc file. :param cookies: Store the auth cookies in this instance. If unspecified, uses the global instance. :raises pants.auth.basic_auth.BasicAuthException: If auth fails due to misconfiguration or rejection by the server. """ cookies = cookies or Cookies.global_instance() if not provider: raise BasicAuthException("No basic auth provider specified.") provider_config = self.options.providers.get(provider) if not provider_config: raise BasicAuthException( f"No config found for provider {provider}.") url = provider_config.get("url") if not url: raise BasicAuthException( f"No url found in config for provider {provider}.") if not self.options.allow_insecure_urls and not url.startswith( "https://"): raise BasicAuthException( f"Auth url for provider {provider} is not secure: {url}.") auth = requests.auth.HTTPBasicAuth(creds.username, creds.password) if creds else None response = requests.get(url, auth=auth, headers={"User-Agent": f"pants/v{VERSION}"}) if response.status_code != requests.codes.ok: if response.status_code == requests.codes.unauthorized: parsed = www_authenticate.parse( response.headers.get("WWW-Authenticate", "")) if "Basic" in parsed: raise Challenged(url, response.status_code, response.reason, parsed["Basic"]["realm"]) raise BasicAuthException(url, response.status_code, response.reason) cookies.update(response.cookies)
def _execute_get_request(url, headers): response = requests.get(url, headers=headers) if response.status_code == requests.codes.unauthorized: # Get authentication details from headers # Registry should tell how to authenticate www_authenticate_details = response.headers['Www-Authenticate'] log.debug(f'unauthorized: retrieving authentication details ' f'from response headers {www_authenticate_details}') bearer = www_authenticate.parse(www_authenticate_details)['bearer'] token = AuthenticationService.get_token(**bearer) headers['Authorization'] = f'Bearer {token}' # Repeat request response = requests.get(url, headers=headers) return response
def authenticate(self, provider, creds=None, cookies=None): """Authenticate against the specified provider. :param str provider: Authorize against this provider. :param pants.auth.basic_auth.BasicAuthCreds creds: The creds to use. If unspecified, assumes that creds are set in the netrc file. :param pants.auth.cookies.Cookies cookies: Store the auth cookies in this instance. If unspecified, uses the global instance. :raises pants.auth.basic_auth.BasicAuthException: If auth fails due to misconfiguration or rejection by the server. """ cookies = cookies or Cookies.global_instance() if not provider: raise BasicAuthException('No basic auth provider specified.') provider_config = self.get_options().providers.get(provider) if not provider_config: raise BasicAuthException( 'No config found for provider {}.'.format(provider)) url = provider_config.get('url') if not url: raise BasicAuthException( 'No url found in config for provider {}.'.format(provider)) if not self.get_options().allow_insecure_urls and not url.startswith( 'https://'): raise BasicAuthException( 'Auth url for provider {} is not secure: {}.'.format( provider, url)) if creds: auth = requests.auth.HTTPBasicAuth(creds.username, creds.password) else: auth = None # requests will use the netrc creds. response = requests.get(url, auth=auth) if response.status_code != requests.codes.ok: if response.status_code == requests.codes.unauthorized: parsed = www_authenticate.parse( response.headers.get('WWW-Authenticate', '')) if 'Basic' in parsed: raise Challenged(url, response.status_code, response.reason, parsed['Basic']['realm']) raise BasicAuthException(url, response.status_code, response.reason) cookies.update(response.cookies)
def get_auth_schemes(r, path): """ Returns list of auth schemes(lowcased) if www-authenticate: header exists returns None if no header found - www-authenticate: basic - www-authenticate: bearer """ try_oauth = requests.head('{0}{1}'.format(r.hostname, path), verify=not r.no_validate_ssl) if 'Www-Authenticate' in try_oauth.headers: oauth = www_authenticate.parse(try_oauth.headers['Www-Authenticate']) log.debug('[docker] Auth schemes found:{0}'.format([m for m in oauth])) return [m.lower() for m in oauth] else: log.debug('[docker] No Auth schemes found') return []
def testValid(self): for r in range(1, len(challenges) + 1): for permutation in itertools.permutations(challenges, r): # Skip those that have the same authentication scheme more than once. if len(set(challenge[1][0] for challenge in permutation)) != len(permutation): continue # Skip any permutation that contains a Negotiate challenge # with a token, if it's not the only challenge. if len(permutation) > 1 and \ any(challenge[1][0] == 'negotiate' and challenge[1][1] != None for challenge in permutation): continue full_challenge = ', '.join(challenge[0] for challenge in permutation) print(full_challenge) parsed = www_authenticate.parse(full_challenge) for left, right in zip(permutation, parsed): self.assertEqual(left[1][0], right) self.assertEqual(left[1][1], parsed[right])
def _request(self, method, url, *, headers=None, **kwargs): headers = headers or {} response = yield from super()._request(method, url, headers=headers, **kwargs) challenges = self.get_challenges(response) if response.status == UNAUTHORIZED and 'negotiate' in challenges: host = self.get_hostname(response) ctx = self.get_context(host) out_token = self.negotiate_step(ctx) while True: response.close() if out_token: headers['Authorization'] = 'Negotiate ' + out_token response = yield from super()._request(method, url, headers=headers, **kwargs) challenges = www_authenticate.parse(response.headers.get('WWW-Authenticate')) in_token = challenges['negotiate'] self.negotiate_step(ctx, in_token) if ctx.complete: break return response
def get_auth_headers(docker_registry_secure: DockerRegistrySecure, image_name: ImageName) -> Union[Dict[str, str], None]: # pylint: disable=protected-access """ Retrieves the authentication headers for a given image. Args: docker_registry_secure: The secure docker registry from which to retrieve the authentication headers. image_name: The name of the image for which to retrieve the headers. Returns: The authentication headers, or None. """ # Try to retrieve credentials first ... auth_header_src = None for endpoint in docker_registry_secure.docker_client.api._auth_configs.auths: if image_name.endpoint in endpoint: credentials = docker_registry_secure.docker_client.api._auth_configs.auths[ endpoint] auth = b64encode( f"{credentials['username']}:{credentials['password']}".encode( "utf-8")).decode("utf-8") auth_header_src = {"Authorization": f"Basic {auth}"} if not auth_header_src: return None # Try to retrieve an authentication token ... https_connection = HTTPSConnection(host=image_name.endpoint) https_connection.request("GET", url="/v2/", headers=auth_header_src) auth_params = www_authenticate.parse( https_connection.getresponse().headers["Www-Authenticate"]) bearer = auth_params["bearer"] https_connection = HTTPSConnection(host=image_name.endpoint) https_connection.request( "GET", url= f"{bearer['realm']}?service={bearer['service']}&scope=repository:{image_name.image}:pull&" f"client_id=pytest-docker-registry-fixtures", headers=auth_header_src, ) payload = loads(https_connection.getresponse().read()) assert payload["token"] return {"Authorization": f"Bearer {payload['token']}"}
def _call_docker_registry_api_auth(self, authenticate_header: str) -> str: parsed_hearder = www_authenticate.parse(authenticate_header) realm = parsed_hearder['bearer']['realm'] auth_params = [] for key in parsed_hearder['bearer']: if key != 'realm': auth_params.append('%s=%s' % (key, parsed_hearder['bearer'][key])) auth_url = '%s?%s' % (realm, '&'.join(auth_params)) headers = {'Content-Type': 'application/json'} response = requests.get(auth_url, headers=headers) if response.status_code == 200: content = json.loads(response.content.decode('utf-8')) return content['token'] else: raise WatchError("Authentication failed, response code %d" % response.status_code)
def get_auth_schemes(r, path): """ Returns list of auth schemes(lowcased) if www-authenticate: header exists returns None if no header found - www-authenticate: basic - www-authenticate: bearer """ if DEBUG: print("[debug][funcname]: get_auth_schemes()") try_oauth = requests.head('{0}{1}'.format(r.hostname, path)) if 'Www-Authenticate' in try_oauth.headers: oauth = www_authenticate.parse(try_oauth.headers['Www-Authenticate']) if DEBUG: print('[debug][docker] Auth schemes found:{0}'.format([m for m in oauth])) return [m.lower() for m in oauth] else: if DEBUG: print('[debug][docker] No Auth schemes found') return []
def authenticate(self, username=None, password=None, actions=None, response=None, authorization=None, user_agent='Docker-Client/19.03.2 (linux)'): # pylint: disable=too-many-arguments,too-many-locals,too-many-branches """ Authenticate to the registry using a username and password, an authorization header or otherwise as the anonymous user. :param username: User name to authenticate as. :type username: str :param password: User's password. :type password: str :param actions: If you know which types of operation you need to make on the registry, specify them here. Valid actions are ``pull``, ``push`` and ``*``. :type actions: list :param response: When the ``auth`` function you passed to :class:`DXFBase`'s constructor is called, it is passed a HTTP response object. Pass it back to :meth:`authenticate` to have it automatically detect which actions are required. :type response: requests.Response :param authorization: ``Authorization`` header value. :type authorization: str :param user_agent: ``User-Agent`` header value. :type user_agent: str :rtype: str :returns: Authentication token, if the registry supports bearer tokens. Otherwise ``None``, and HTTP Basic auth is used (if the registry requires authentication). """ if response is None: with warnings.catch_warnings(): _ignore_warnings(self) response = self._sessions[0].get(self._base_url, verify=self._tlsverify) if response.ok: return None # pylint: disable=no-member if response.status_code != requests.codes.unauthorized: raise exceptions.DXFUnexpectedStatusCodeError( response.status_code, requests.codes.unauthorized) parsed = www_authenticate.parse(response.headers['www-authenticate']) if username is not None and password is not None: headers = { 'Authorization': 'Basic ' + base64.b64encode( _to_bytes_2and3(username + ':' + password)).decode('utf-8') } elif authorization is not None: headers = {'Authorization': authorization} else: headers = {} headers["User-Agent"] = user_agent if 'bearer' in parsed: info = parsed['bearer'] if actions and self._repo: scope = 'repository:' + self._repo + ':' + ','.join(actions) elif 'scope' in info: scope = info['scope'] elif not self._repo: # Issue #28: gcr.io doesn't return scope for non-repo requests scope = 'registry:catalog:*' else: scope = '' url_parts = list(urlparse.urlparse(info['realm'])) query = urlparse.parse_qs(url_parts[4]) query.update({'service': info['service'], 'scope': scope}) url_parts[4] = urlencode(query, True) if self._insecure: url_parts[0] = 'http' else: url_parts[0] = 'https' if self._auth_host: url_parts[1] = self._auth_host auth_url = urlparse.urlunparse(url_parts) with warnings.catch_warnings(): _ignore_warnings(self) r = self._sessions[0].get(auth_url, headers=headers, verify=self._tlsverify) _raise_for_status(r) rjson = r.json() # Use 'access_token' value if present and not empty, else 'token' value. self.token = rjson.get('access_token') or rjson['token'] return self._token self._headers = headers return None
def _authenticate( self, image_reference: str, scope: str, ): if self.token_cache.token(scope=scope): return # no re-auth required, yet if 'push' in scope: privileges = oa.Privileges.READWRITE elif 'pull' in scope: privileges = oa.Privileges.READONLY else: privileges = None oci_creds = self.credentials_lookup( image_reference=image_reference, privileges=privileges, absent_ok=True, ) if not oci_creds: logger.info( f'no credentials for {image_reference=} - attempting anonymous-auth' ) url = base_api_url(image_reference=image_reference) res = self.session.get(url) auth_challenge = www_authenticate.parse( res.headers['www-authenticate']) bearer = auth_challenge['bearer'] service = bearer['service'] realm = bearer['realm'] + '?' + urllib.parse.urlencode( { 'scope': scope, 'service': service, }) if oci_creds: auth = requests.auth.HTTPBasicAuth( username=oci_creds.username, password=oci_creds.password, ) else: auth = None res = self.session.get( realm, auth=auth, ) res.raise_for_status() token_dict = res.json() token_dict['scope'] = scope token = dacite.from_dict( data=token_dict, data_class=OauthToken, ) self.token_cache.set_token(token)
def get_challenges(self, response): challenges = {} for k, v in response.headers.items(): if k.lower() == 'www-authenticate': challenges.update(www_authenticate.parse(v)) return challenges
if data: if isinstance(data, dict): data = urlencode(data) request = urllib2.Request(url, data=data, headers=headers) try: return urllib2.urlopen(request) except urllib2.HTTPError as e: return e response = open_url(uri, headers=dict(authorization='bearer bad-token')) if response.getcode() != 401: print "oops, expected 401" raise SystemExit(-1) www_auth = www_authenticate.parse( response.headers['WWW-Authenticate'])['Bearer'] if not all([x in www_auth for x in ['nonce', 'scope', 'token_pop_endpoint']]) or \ not all([x in www_auth['scope'].split() for x in ['openid', 'webid']]): print "oops, WWW-Authenticate isn't for webid-auth-protocol", response.headers[ 'WWW-Authenticate'] raise SystemExit(-1) jwk = make_jwk() id_token = make_id_token(args.webid, "cli-tool", nonce=str(uuid.uuid4()), lifetime=args.id_token_lifetime, redirect_uri=args.app_id, cnf=dict(jwk=jwk), sub_jwk=jwk if is_self_issued else None)
def authenticate(self, username=None, password=None, actions=None, response=None): """ Authenticate to the registry, using a username and password if supplied, otherwise as the anonymous user. :param username: User name to authenticate as. :type username: str :param password: User's password. :type password: str :param actions: If you know which types of operation you need to make on the registry, specify them here. Valid actions are ``pull``, ``push`` and ``*``. :type actions: list :param response: When the ``auth`` function you passed to :class:`DXFBase`'s constructor is called, it is passed a HTTP response object. Pass it back to :meth:`authenticate` to have it automatically detect which actions are required. :type response: requests.Response :rtype: str :returns: Authentication token, if the registry supports bearer tokens. Otherwise ``None``, and HTTP Basic auth is used. """ if self._insecure: raise exceptions.DXFAuthInsecureError() if response is None: response = self._sessions[0].get(self._base_url) # pylint: disable=no-member if response.status_code != requests.codes.unauthorized: raise exceptions.DXFUnexpectedStatusCodeError( response.status_code, requests.codes.unauthorized) parsed = www_authenticate.parse(response.headers['www-authenticate']) if username is not None and password is not None: headers = { 'Authorization': 'Basic ' + base64.b64encode( _to_bytes_2and3(username + ':' + password)).decode('utf-8') } else: headers = {} if 'bearer' in parsed: info = parsed['bearer'] if actions and self._repo: scope = 'repository:' + self._repo + ':' + ','.join(actions) else: scope = info['scope'] url_parts = list(urlparse.urlparse(info['realm'])) query = urlparse.parse_qs(url_parts[4]) query.update({'service': info['service'], 'scope': scope}) url_parts[4] = urlencode(query, True) url_parts[0] = 'https' if self._auth_host: url_parts[1] = self._auth_host auth_url = urlparse.urlunparse(url_parts) r = self._sessions[0].get(auth_url, headers=headers) _raise_for_status(r) self.token = r.json()['token'] return self._token else: self._headers = headers
def _authenticate( self, image_reference: typing.Union[str, om.OciImageReference], scope: str, ): if isinstance(image_reference, om.OciImageReference): image_reference = str(image_reference) cached_auth_method = self.token_cache.auth_method(image_reference=image_reference) if cached_auth_method is AuthMethod.BASIC: return # basic-auth does not require any additional preliminary steps if cached_auth_method is AuthMethod.BEARER and self.token_cache.token(scope=scope): return # no re-auth required, yet if 'push' in scope: privileges = oa.Privileges.READWRITE elif 'pull' in scope: privileges = oa.Privileges.READONLY else: privileges = None oci_creds = self.credentials_lookup( image_reference=image_reference, privileges=privileges, absent_ok=True, ) if not oci_creds: logger.warning(f'no credentials for {image_reference=} - attempting anonymous-auth') url = base_api_url( image_reference=str(image_reference), ) res = self.session.get( url=url, verify=not self.disable_tls_validation, ) auth_challenge = www_authenticate.parse(res.headers.get('www-authenticate')) # XXX HACK HACK: fallback to basic-auth if endpoints does not state what it wants if 'basic' in auth_challenge or not auth_challenge: self.token_cache.set_auth_method( image_reference=image_reference, auth_method=AuthMethod.BASIC, ) return # no additional preliminary steps required for basic-auth elif 'bearer' in auth_challenge: bearer = auth_challenge['bearer'] service = bearer['service'] self.token_cache.set_auth_method( image_reference=image_reference, auth_method=AuthMethod.BEARER, ) else: logger.warning(f'did not understand {auth_challenge=} - pbly a bug') realm = bearer['realm'] + '?' + urllib.parse.urlencode({ 'scope': scope, 'service': service, }) if oci_creds: auth = requests.auth.HTTPBasicAuth( username=oci_creds.username, password=oci_creds.password, ) else: auth = None res = self.session.get( url=realm, verify=not self.disable_tls_validation, auth=auth, ) if not res.ok: logger.warning( f'rq against {realm=} failed: {res.status_code=} {res.reason=} {res.content=}' ) res.raise_for_status() token_dict = res.json() token_dict['scope'] = scope token = dacite.from_dict( data=token_dict, data_class=OauthToken, ) self.token_cache.set_token(token)
async def authenticate(self, username: str = None, password: str = None, actions: list = None, response: aiohttp.ClientResponse = None, authorization: str = None) -> str: # pylint: disable=too-many-arguments,too-many-locals """ Authenticate to the registry using a username and password, an authorization header or otherwise as the anonymous user. :param username: User name to authenticate as. :param password: User's password. :param actions: If you know which types of operation you need to make on the registry, specify them here. Valid actions are ``pull``, ``push`` and ``*``. :param response: When the ``auth`` function you passed to :class:`DXFBase`'s constructor is called, it is passed a HTTP response object. Pass it back to :meth:`authenticate` to have it automatically detect which actions are required. :param authorization: ``Authorization`` header value. :returns: Authentication token, if the registry supports bearer tokens. Otherwise ``None``, and HTTP Basic auth is used (if the registry requires authentication). """ if response is None: response = await self._sessions[0].get(self._base_url, ssl=self._tlsverify) if response.status == 200: return None # pylint: disable=no-member if response.status != aiohttp.web.HTTPUnauthorized.status_code: raise exceptions.DXFUnexpectedStatusCodeError( response.status, aiohttp.web.HTTPUnauthorized.status_code) if self._insecure: raise exceptions.DXFAuthInsecureError() parsed = www_authenticate.parse(response.headers['www-authenticate']) if username is not None and password is not None: headers = { 'Authorization': 'Basic ' + base64.b64encode( _to_bytes_2and3(username + ':' + password)).decode('utf-8') } elif authorization is not None: headers = {'Authorization': authorization} else: headers = {} if 'bearer' in parsed: info = parsed['bearer'] if actions and self._repo: scope = 'repository:' + self._repo + ':' + ','.join(actions) elif 'scope' in info: scope = info['scope'] else: scope = '' url_parts = list(urlparse.urlparse(info['realm'])) query = urlparse.parse_qs(url_parts[4]) query.update({'service': info['service'], 'scope': scope}) url_parts[4] = urlencode(query, True) url_parts[0] = 'https' if self._auth_host: url_parts[1] = self._auth_host auth_url = urlparse.urlunparse(url_parts) r = await self._sessions[0].get(auth_url, headers=headers, ssl=self._tlsverify) _raise_for_status(r) rjson = await r.json() # Use 'access_token' value if present and not empty, else 'token' value. self.token = rjson.get('access_token') or rjson['token'] return self._token self._headers = headers return None
def get_challenges(self, response): challenges = {} for k, v in response.headers.items(): if k.lower() == 'proxy-authenticate': challenges.update(www_authenticate.parse(v)) return challenges