Example #1
0
    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
Example #2
0
    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