We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support updated credential helpers syntax.
https://github.com/docker/compose/issues/4885

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `docker/auth.py`
Content:
```
1 import base64
2 import json
3 import logging
4 import os
5 
6 import dockerpycreds
7 import six
8 
9 from . import errors
10 from .constants import IS_WINDOWS_PLATFORM
11 
12 INDEX_NAME = 'docker.io'
13 INDEX_URL = 'https://{0}/v1/'.format(INDEX_NAME)
14 DOCKER_CONFIG_FILENAME = os.path.join('.docker', 'config.json')
15 LEGACY_DOCKER_CONFIG_FILENAME = '.dockercfg'
16 TOKEN_USERNAME = '<token>'
17 
18 log = logging.getLogger(__name__)
19 
20 
21 def resolve_repository_name(repo_name):
22     if '://' in repo_name:
23         raise errors.InvalidRepository(
24             'Repository name cannot contain a scheme ({0})'.format(repo_name)
25         )
26 
27     index_name, remote_name = split_repo_name(repo_name)
28     if index_name[0] == '-' or index_name[-1] == '-':
29         raise errors.InvalidRepository(
30             'Invalid index name ({0}). Cannot begin or end with a'
31             ' hyphen.'.format(index_name)
32         )
33     return resolve_index_name(index_name), remote_name
34 
35 
36 def resolve_index_name(index_name):
37     index_name = convert_to_hostname(index_name)
38     if index_name == 'index.' + INDEX_NAME:
39         index_name = INDEX_NAME
40     return index_name
41 
42 
43 def get_config_header(client, registry):
44     log.debug('Looking for auth config')
45     if not client._auth_configs:
46         log.debug(
47             "No auth config in memory - loading from filesystem"
48         )
49         client._auth_configs = load_config()
50     authcfg = resolve_authconfig(client._auth_configs, registry)
51     # Do not fail here if no authentication exists for this
52     # specific registry as we can have a readonly pull. Just
53     # put the header if we can.
54     if authcfg:
55         log.debug('Found auth config')
56         # auth_config needs to be a dict in the format used by
57         # auth.py username , password, serveraddress, email
58         return encode_header(authcfg)
59     log.debug('No auth config found')
60     return None
61 
62 
63 def split_repo_name(repo_name):
64     parts = repo_name.split('/', 1)
65     if len(parts) == 1 or (
66         '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost'
67     ):
68         # This is a docker index repo (ex: username/foobar or ubuntu)
69         return INDEX_NAME, repo_name
70     return tuple(parts)
71 
72 
73 def resolve_authconfig(authconfig, registry=None):
74     """
75     Returns the authentication data from the given auth configuration for a
76     specific registry. As with the Docker client, legacy entries in the config
77     with full URLs are stripped down to hostnames before checking for a match.
78     Returns None if no match was found.
79     """
80     if 'credsStore' in authconfig:
81         log.debug(
82             'Using credentials store "{0}"'.format(authconfig['credsStore'])
83         )
84         return _resolve_authconfig_credstore(
85             authconfig, registry, authconfig['credsStore']
86         )
87     # Default to the public index server
88     registry = resolve_index_name(registry) if registry else INDEX_NAME
89     log.debug("Looking for auth entry for {0}".format(repr(registry)))
90 
91     if registry in authconfig:
92         log.debug("Found {0}".format(repr(registry)))
93         return authconfig[registry]
94 
95     for key, config in six.iteritems(authconfig):
96         if resolve_index_name(key) == registry:
97             log.debug("Found {0}".format(repr(key)))
98             return config
99 
100     log.debug("No entry found")
101     return None
102 
103 
104 def _resolve_authconfig_credstore(authconfig, registry, credstore_name):
105     if not registry or registry == INDEX_NAME:
106         # The ecosystem is a little schizophrenic with index.docker.io VS
107         # docker.io - in that case, it seems the full URL is necessary.
108         registry = 'https://index.docker.io/v1/'
109     log.debug("Looking for auth entry for {0}".format(repr(registry)))
110     store = dockerpycreds.Store(credstore_name)
111     try:
112         data = store.get(registry)
113         res = {
114             'ServerAddress': registry,
115         }
116         if data['Username'] == TOKEN_USERNAME:
117             res['IdentityToken'] = data['Secret']
118         else:
119             res.update({
120                 'Username': data['Username'],
121                 'Password': data['Secret'],
122             })
123         return res
124     except dockerpycreds.CredentialsNotFound as e:
125         log.debug('No entry found')
126         return None
127     except dockerpycreds.StoreError as e:
128         raise errors.DockerException(
129             'Credentials store error: {0}'.format(repr(e))
130         )
131 
132 
133 def convert_to_hostname(url):
134     return url.replace('http://', '').replace('https://', '').split('/', 1)[0]
135 
136 
137 def decode_auth(auth):
138     if isinstance(auth, six.string_types):
139         auth = auth.encode('ascii')
140     s = base64.b64decode(auth)
141     login, pwd = s.split(b':', 1)
142     return login.decode('utf8'), pwd.decode('utf8')
143 
144 
145 def encode_header(auth):
146     auth_json = json.dumps(auth).encode('ascii')
147     return base64.urlsafe_b64encode(auth_json)
148 
149 
150 def parse_auth(entries, raise_on_error=False):
151     """
152     Parses authentication entries
153 
154     Args:
155       entries:        Dict of authentication entries.
156       raise_on_error: If set to true, an invalid format will raise
157                       InvalidConfigFile
158 
159     Returns:
160       Authentication registry.
161     """
162 
163     conf = {}
164     for registry, entry in six.iteritems(entries):
165         if not isinstance(entry, dict):
166             log.debug(
167                 'Config entry for key {0} is not auth config'.format(registry)
168             )
169             # We sometimes fall back to parsing the whole config as if it was
170             # the auth config by itself, for legacy purposes. In that case, we
171             # fail silently and return an empty conf if any of the keys is not
172             # formatted properly.
173             if raise_on_error:
174                 raise errors.InvalidConfigFile(
175                     'Invalid configuration for registry {0}'.format(registry)
176                 )
177             return {}
178         if 'identitytoken' in entry:
179             log.debug('Found an IdentityToken entry for registry {0}'.format(
180                 registry
181             ))
182             conf[registry] = {
183                 'IdentityToken': entry['identitytoken']
184             }
185             continue  # Other values are irrelevant if we have a token, skip.
186 
187         if 'auth' not in entry:
188             # Starting with engine v1.11 (API 1.23), an empty dictionary is
189             # a valid value in the auths config.
190             # https://github.com/docker/compose/issues/3265
191             log.debug(
192                 'Auth data for {0} is absent. Client might be using a '
193                 'credentials store instead.'
194             )
195             conf[registry] = {}
196             continue
197 
198         username, password = decode_auth(entry['auth'])
199         log.debug(
200             'Found entry (registry={0}, username={1})'
201             .format(repr(registry), repr(username))
202         )
203 
204         conf[registry] = {
205             'username': username,
206             'password': password,
207             'email': entry.get('email'),
208             'serveraddress': registry,
209         }
210     return conf
211 
212 
213 def find_config_file(config_path=None):
214     paths = list(filter(None, [
215         config_path,  # 1
216         config_path_from_environment(),  # 2
217         os.path.join(home_dir(), DOCKER_CONFIG_FILENAME),  # 3
218         os.path.join(home_dir(), LEGACY_DOCKER_CONFIG_FILENAME),  # 4
219     ]))
220 
221     log.debug("Trying paths: {0}".format(repr(paths)))
222 
223     for path in paths:
224         if os.path.exists(path):
225             log.debug("Found file at path: {0}".format(path))
226             return path
227 
228     log.debug("No config file found")
229 
230     return None
231 
232 
233 def config_path_from_environment():
234     config_dir = os.environ.get('DOCKER_CONFIG')
235     if not config_dir:
236         return None
237     return os.path.join(config_dir, os.path.basename(DOCKER_CONFIG_FILENAME))
238 
239 
240 def home_dir():
241     """
242     Get the user's home directory, using the same logic as the Docker Engine
243     client - use %USERPROFILE% on Windows, $HOME/getuid on POSIX.
244     """
245     if IS_WINDOWS_PLATFORM:
246         return os.environ.get('USERPROFILE', '')
247     else:
248         return os.path.expanduser('~')
249 
250 
251 def load_config(config_path=None):
252     """
253     Loads authentication data from a Docker configuration file in the given
254     root directory or if config_path is passed use given path.
255     Lookup priority:
256         explicit config_path parameter > DOCKER_CONFIG environment variable >
257         ~/.docker/config.json > ~/.dockercfg
258     """
259     config_file = find_config_file(config_path)
260 
261     if not config_file:
262         return {}
263 
264     try:
265         with open(config_file) as f:
266             data = json.load(f)
267             res = {}
268             if data.get('auths'):
269                 log.debug("Found 'auths' section")
270                 res.update(parse_auth(data['auths'], raise_on_error=True))
271             if data.get('HttpHeaders'):
272                 log.debug("Found 'HttpHeaders' section")
273                 res.update({'HttpHeaders': data['HttpHeaders']})
274             if data.get('credsStore'):
275                 log.debug("Found 'credsStore' section")
276                 res.update({'credsStore': data['credsStore']})
277             if res:
278                 return res
279             else:
280                 log.debug("Couldn't find 'auths' or 'HttpHeaders' sections")
281                 f.seek(0)
282                 return parse_auth(json.load(f))
283     except (IOError, KeyError, ValueError) as e:
284         # Likely missing new Docker config file or it's in an
285         # unknown format, continue to attempt to read old location
286         # and format.
287         log.debug(e)
288 
289     log.debug("Attempting to parse legacy auth file format")
290     try:
291         data = []
292         with open(config_file) as f:
293             for line in f.readlines():
294                 data.append(line.strip().split(' = ')[1])
295             if len(data) < 2:
296                 # Not enough data
297                 raise errors.InvalidConfigFile(
298                     'Invalid or empty configuration file!'
299                 )
300 
301         username, password = decode_auth(data[0])
302         return {
303             INDEX_NAME: {
304                 'username': username,
305                 'password': password,
306                 'email': data[1],
307                 'serveraddress': INDEX_URL,
308             }
309         }
310     except Exception as e:
311         log.debug(e)
312         pass
313 
314     log.debug("All parsing attempts failed - returning empty config")
315     return {}
316
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/docker/auth.py b/docker/auth.py
--- a/docker/auth.py
+++ b/docker/auth.py
@@ -70,6 +70,15 @@
     return tuple(parts)
 
 
+def get_credential_store(authconfig, registry):
+    if not registry or registry == INDEX_NAME:
+        registry = 'https://index.docker.io/v1/'
+
+    return authconfig.get('credHelpers', {}).get(registry) or authconfig.get(
+        'credsStore'
+    )
+
+
 def resolve_authconfig(authconfig, registry=None):
     """
     Returns the authentication data from the given auth configuration for a
@@ -77,13 +86,17 @@
     with full URLs are stripped down to hostnames before checking for a match.
     Returns None if no match was found.
     """
-    if 'credsStore' in authconfig:
-        log.debug(
-            'Using credentials store "{0}"'.format(authconfig['credsStore'])
-        )
-        return _resolve_authconfig_credstore(
-            authconfig, registry, authconfig['credsStore']
-        )
+
+    if 'credHelpers' in authconfig or 'credsStore' in authconfig:
+        store_name = get_credential_store(authconfig, registry)
+        if store_name is not None:
+            log.debug(
+                'Using credentials store "{0}"'.format(store_name)
+            )
+            return _resolve_authconfig_credstore(
+                authconfig, registry, store_name
+            )
+
     # Default to the public index server
     registry = resolve_index_name(registry) if registry else INDEX_NAME
     log.debug("Looking for auth entry for {0}".format(repr(registry)))
@@ -274,6 +287,9 @@
             if data.get('credsStore'):
                 log.debug("Found 'credsStore' section")
                 res.update({'credsStore': data['credsStore']})
+            if data.get('credHelpers'):
+                log.debug("Found 'credHelpers' section")
+                res.update({'credHelpers': data['credHelpers']})
             if res:
                 return res
             else:

{"golden_diff": "diff --git a/docker/auth.py b/docker/auth.py\n--- a/docker/auth.py\n+++ b/docker/auth.py\n@@ -70,6 +70,15 @@\n     return tuple(parts)\n \n \n+def get_credential_store(authconfig, registry):\n+    if not registry or registry == INDEX_NAME:\n+        registry = 'https://index.docker.io/v1/'\n+\n+    return authconfig.get('credHelpers', {}).get(registry) or authconfig.get(\n+        'credsStore'\n+    )\n+\n+\n def resolve_authconfig(authconfig, registry=None):\n     \"\"\"\n     Returns the authentication data from the given auth configuration for a\n@@ -77,13 +86,17 @@\n     with full URLs are stripped down to hostnames before checking for a match.\n     Returns None if no match was found.\n     \"\"\"\n-    if 'credsStore' in authconfig:\n-        log.debug(\n-            'Using credentials store \"{0}\"'.format(authconfig['credsStore'])\n-        )\n-        return _resolve_authconfig_credstore(\n-            authconfig, registry, authconfig['credsStore']\n-        )\n+\n+    if 'credHelpers' in authconfig or 'credsStore' in authconfig:\n+        store_name = get_credential_store(authconfig, registry)\n+        if store_name is not None:\n+            log.debug(\n+                'Using credentials store \"{0}\"'.format(store_name)\n+            )\n+            return _resolve_authconfig_credstore(\n+                authconfig, registry, store_name\n+            )\n+\n     # Default to the public index server\n     registry = resolve_index_name(registry) if registry else INDEX_NAME\n     log.debug(\"Looking for auth entry for {0}\".format(repr(registry)))\n@@ -274,6 +287,9 @@\n             if data.get('credsStore'):\n                 log.debug(\"Found 'credsStore' section\")\n                 res.update({'credsStore': data['credsStore']})\n+            if data.get('credHelpers'):\n+                log.debug(\"Found 'credHelpers' section\")\n+                res.update({'credHelpers': data['credHelpers']})\n             if res:\n                 return res\n             else:\n", "issue": "Support updated credential helpers syntax.\nhttps://github.com/docker/compose/issues/4885\n", "before_files": [{"content": "import base64\nimport json\nimport logging\nimport os\n\nimport dockerpycreds\nimport six\n\nfrom . import errors\nfrom .constants import IS_WINDOWS_PLATFORM\n\nINDEX_NAME = 'docker.io'\nINDEX_URL = 'https://{0}/v1/'.format(INDEX_NAME)\nDOCKER_CONFIG_FILENAME = os.path.join('.docker', 'config.json')\nLEGACY_DOCKER_CONFIG_FILENAME = '.dockercfg'\nTOKEN_USERNAME = '<token>'\n\nlog = logging.getLogger(__name__)\n\n\ndef resolve_repository_name(repo_name):\n    if '://' in repo_name:\n        raise errors.InvalidRepository(\n            'Repository name cannot contain a scheme ({0})'.format(repo_name)\n        )\n\n    index_name, remote_name = split_repo_name(repo_name)\n    if index_name[0] == '-' or index_name[-1] == '-':\n        raise errors.InvalidRepository(\n            'Invalid index name ({0}). Cannot begin or end with a'\n            ' hyphen.'.format(index_name)\n        )\n    return resolve_index_name(index_name), remote_name\n\n\ndef resolve_index_name(index_name):\n    index_name = convert_to_hostname(index_name)\n    if index_name == 'index.' + INDEX_NAME:\n        index_name = INDEX_NAME\n    return index_name\n\n\ndef get_config_header(client, registry):\n    log.debug('Looking for auth config')\n    if not client._auth_configs:\n        log.debug(\n            \"No auth config in memory - loading from filesystem\"\n        )\n        client._auth_configs = load_config()\n    authcfg = resolve_authconfig(client._auth_configs, registry)\n    # Do not fail here if no authentication exists for this\n    # specific registry as we can have a readonly pull. Just\n    # put the header if we can.\n    if authcfg:\n        log.debug('Found auth config')\n        # auth_config needs to be a dict in the format used by\n        # auth.py username , password, serveraddress, email\n        return encode_header(authcfg)\n    log.debug('No auth config found')\n    return None\n\n\ndef split_repo_name(repo_name):\n    parts = repo_name.split('/', 1)\n    if len(parts) == 1 or (\n        '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost'\n    ):\n        # This is a docker index repo (ex: username/foobar or ubuntu)\n        return INDEX_NAME, repo_name\n    return tuple(parts)\n\n\ndef resolve_authconfig(authconfig, registry=None):\n    \"\"\"\n    Returns the authentication data from the given auth configuration for a\n    specific registry. As with the Docker client, legacy entries in the config\n    with full URLs are stripped down to hostnames before checking for a match.\n    Returns None if no match was found.\n    \"\"\"\n    if 'credsStore' in authconfig:\n        log.debug(\n            'Using credentials store \"{0}\"'.format(authconfig['credsStore'])\n        )\n        return _resolve_authconfig_credstore(\n            authconfig, registry, authconfig['credsStore']\n        )\n    # Default to the public index server\n    registry = resolve_index_name(registry) if registry else INDEX_NAME\n    log.debug(\"Looking for auth entry for {0}\".format(repr(registry)))\n\n    if registry in authconfig:\n        log.debug(\"Found {0}\".format(repr(registry)))\n        return authconfig[registry]\n\n    for key, config in six.iteritems(authconfig):\n        if resolve_index_name(key) == registry:\n            log.debug(\"Found {0}\".format(repr(key)))\n            return config\n\n    log.debug(\"No entry found\")\n    return None\n\n\ndef _resolve_authconfig_credstore(authconfig, registry, credstore_name):\n    if not registry or registry == INDEX_NAME:\n        # The ecosystem is a little schizophrenic with index.docker.io VS\n        # docker.io - in that case, it seems the full URL is necessary.\n        registry = 'https://index.docker.io/v1/'\n    log.debug(\"Looking for auth entry for {0}\".format(repr(registry)))\n    store = dockerpycreds.Store(credstore_name)\n    try:\n        data = store.get(registry)\n        res = {\n            'ServerAddress': registry,\n        }\n        if data['Username'] == TOKEN_USERNAME:\n            res['IdentityToken'] = data['Secret']\n        else:\n            res.update({\n                'Username': data['Username'],\n                'Password': data['Secret'],\n            })\n        return res\n    except dockerpycreds.CredentialsNotFound as e:\n        log.debug('No entry found')\n        return None\n    except dockerpycreds.StoreError as e:\n        raise errors.DockerException(\n            'Credentials store error: {0}'.format(repr(e))\n        )\n\n\ndef convert_to_hostname(url):\n    return url.replace('http://', '').replace('https://', '').split('/', 1)[0]\n\n\ndef decode_auth(auth):\n    if isinstance(auth, six.string_types):\n        auth = auth.encode('ascii')\n    s = base64.b64decode(auth)\n    login, pwd = s.split(b':', 1)\n    return login.decode('utf8'), pwd.decode('utf8')\n\n\ndef encode_header(auth):\n    auth_json = json.dumps(auth).encode('ascii')\n    return base64.urlsafe_b64encode(auth_json)\n\n\ndef parse_auth(entries, raise_on_error=False):\n    \"\"\"\n    Parses authentication entries\n\n    Args:\n      entries:        Dict of authentication entries.\n      raise_on_error: If set to true, an invalid format will raise\n                      InvalidConfigFile\n\n    Returns:\n      Authentication registry.\n    \"\"\"\n\n    conf = {}\n    for registry, entry in six.iteritems(entries):\n        if not isinstance(entry, dict):\n            log.debug(\n                'Config entry for key {0} is not auth config'.format(registry)\n            )\n            # We sometimes fall back to parsing the whole config as if it was\n            # the auth config by itself, for legacy purposes. In that case, we\n            # fail silently and return an empty conf if any of the keys is not\n            # formatted properly.\n            if raise_on_error:\n                raise errors.InvalidConfigFile(\n                    'Invalid configuration for registry {0}'.format(registry)\n                )\n            return {}\n        if 'identitytoken' in entry:\n            log.debug('Found an IdentityToken entry for registry {0}'.format(\n                registry\n            ))\n            conf[registry] = {\n                'IdentityToken': entry['identitytoken']\n            }\n            continue  # Other values are irrelevant if we have a token, skip.\n\n        if 'auth' not in entry:\n            # Starting with engine v1.11 (API 1.23), an empty dictionary is\n            # a valid value in the auths config.\n            # https://github.com/docker/compose/issues/3265\n            log.debug(\n                'Auth data for {0} is absent. Client might be using a '\n                'credentials store instead.'\n            )\n            conf[registry] = {}\n            continue\n\n        username, password = decode_auth(entry['auth'])\n        log.debug(\n            'Found entry (registry={0}, username={1})'\n            .format(repr(registry), repr(username))\n        )\n\n        conf[registry] = {\n            'username': username,\n            'password': password,\n            'email': entry.get('email'),\n            'serveraddress': registry,\n        }\n    return conf\n\n\ndef find_config_file(config_path=None):\n    paths = list(filter(None, [\n        config_path,  # 1\n        config_path_from_environment(),  # 2\n        os.path.join(home_dir(), DOCKER_CONFIG_FILENAME),  # 3\n        os.path.join(home_dir(), LEGACY_DOCKER_CONFIG_FILENAME),  # 4\n    ]))\n\n    log.debug(\"Trying paths: {0}\".format(repr(paths)))\n\n    for path in paths:\n        if os.path.exists(path):\n            log.debug(\"Found file at path: {0}\".format(path))\n            return path\n\n    log.debug(\"No config file found\")\n\n    return None\n\n\ndef config_path_from_environment():\n    config_dir = os.environ.get('DOCKER_CONFIG')\n    if not config_dir:\n        return None\n    return os.path.join(config_dir, os.path.basename(DOCKER_CONFIG_FILENAME))\n\n\ndef home_dir():\n    \"\"\"\n    Get the user's home directory, using the same logic as the Docker Engine\n    client - use %USERPROFILE% on Windows, $HOME/getuid on POSIX.\n    \"\"\"\n    if IS_WINDOWS_PLATFORM:\n        return os.environ.get('USERPROFILE', '')\n    else:\n        return os.path.expanduser('~')\n\n\ndef load_config(config_path=None):\n    \"\"\"\n    Loads authentication data from a Docker configuration file in the given\n    root directory or if config_path is passed use given path.\n    Lookup priority:\n        explicit config_path parameter > DOCKER_CONFIG environment variable >\n        ~/.docker/config.json > ~/.dockercfg\n    \"\"\"\n    config_file = find_config_file(config_path)\n\n    if not config_file:\n        return {}\n\n    try:\n        with open(config_file) as f:\n            data = json.load(f)\n            res = {}\n            if data.get('auths'):\n                log.debug(\"Found 'auths' section\")\n                res.update(parse_auth(data['auths'], raise_on_error=True))\n            if data.get('HttpHeaders'):\n                log.debug(\"Found 'HttpHeaders' section\")\n                res.update({'HttpHeaders': data['HttpHeaders']})\n            if data.get('credsStore'):\n                log.debug(\"Found 'credsStore' section\")\n                res.update({'credsStore': data['credsStore']})\n            if res:\n                return res\n            else:\n                log.debug(\"Couldn't find 'auths' or 'HttpHeaders' sections\")\n                f.seek(0)\n                return parse_auth(json.load(f))\n    except (IOError, KeyError, ValueError) as e:\n        # Likely missing new Docker config file or it's in an\n        # unknown format, continue to attempt to read old location\n        # and format.\n        log.debug(e)\n\n    log.debug(\"Attempting to parse legacy auth file format\")\n    try:\n        data = []\n        with open(config_file) as f:\n            for line in f.readlines():\n                data.append(line.strip().split(' = ')[1])\n            if len(data) < 2:\n                # Not enough data\n                raise errors.InvalidConfigFile(\n                    'Invalid or empty configuration file!'\n                )\n\n        username, password = decode_auth(data[0])\n        return {\n            INDEX_NAME: {\n                'username': username,\n                'password': password,\n                'email': data[1],\n                'serveraddress': INDEX_URL,\n            }\n        }\n    except Exception as e:\n        log.debug(e)\n        pass\n\n    log.debug(\"All parsing attempts failed - returning empty config\")\n    return {}\n", "path": "docker/auth.py"}], "after_files": [{"content": "import base64\nimport json\nimport logging\nimport os\n\nimport dockerpycreds\nimport six\n\nfrom . import errors\nfrom .constants import IS_WINDOWS_PLATFORM\n\nINDEX_NAME = 'docker.io'\nINDEX_URL = 'https://{0}/v1/'.format(INDEX_NAME)\nDOCKER_CONFIG_FILENAME = os.path.join('.docker', 'config.json')\nLEGACY_DOCKER_CONFIG_FILENAME = '.dockercfg'\nTOKEN_USERNAME = '<token>'\n\nlog = logging.getLogger(__name__)\n\n\ndef resolve_repository_name(repo_name):\n    if '://' in repo_name:\n        raise errors.InvalidRepository(\n            'Repository name cannot contain a scheme ({0})'.format(repo_name)\n        )\n\n    index_name, remote_name = split_repo_name(repo_name)\n    if index_name[0] == '-' or index_name[-1] == '-':\n        raise errors.InvalidRepository(\n            'Invalid index name ({0}). Cannot begin or end with a'\n            ' hyphen.'.format(index_name)\n        )\n    return resolve_index_name(index_name), remote_name\n\n\ndef resolve_index_name(index_name):\n    index_name = convert_to_hostname(index_name)\n    if index_name == 'index.' + INDEX_NAME:\n        index_name = INDEX_NAME\n    return index_name\n\n\ndef get_config_header(client, registry):\n    log.debug('Looking for auth config')\n    if not client._auth_configs:\n        log.debug(\n            \"No auth config in memory - loading from filesystem\"\n        )\n        client._auth_configs = load_config()\n    authcfg = resolve_authconfig(client._auth_configs, registry)\n    # Do not fail here if no authentication exists for this\n    # specific registry as we can have a readonly pull. Just\n    # put the header if we can.\n    if authcfg:\n        log.debug('Found auth config')\n        # auth_config needs to be a dict in the format used by\n        # auth.py username , password, serveraddress, email\n        return encode_header(authcfg)\n    log.debug('No auth config found')\n    return None\n\n\ndef split_repo_name(repo_name):\n    parts = repo_name.split('/', 1)\n    if len(parts) == 1 or (\n        '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost'\n    ):\n        # This is a docker index repo (ex: username/foobar or ubuntu)\n        return INDEX_NAME, repo_name\n    return tuple(parts)\n\n\ndef get_credential_store(authconfig, registry):\n    if not registry or registry == INDEX_NAME:\n        registry = 'https://index.docker.io/v1/'\n\n    return authconfig.get('credHelpers', {}).get(registry) or authconfig.get(\n        'credsStore'\n    )\n\n\ndef resolve_authconfig(authconfig, registry=None):\n    \"\"\"\n    Returns the authentication data from the given auth configuration for a\n    specific registry. As with the Docker client, legacy entries in the config\n    with full URLs are stripped down to hostnames before checking for a match.\n    Returns None if no match was found.\n    \"\"\"\n\n    if 'credHelpers' in authconfig or 'credsStore' in authconfig:\n        store_name = get_credential_store(authconfig, registry)\n        if store_name is not None:\n            log.debug(\n                'Using credentials store \"{0}\"'.format(store_name)\n            )\n            return _resolve_authconfig_credstore(\n                authconfig, registry, store_name\n            )\n\n    # Default to the public index server\n    registry = resolve_index_name(registry) if registry else INDEX_NAME\n    log.debug(\"Looking for auth entry for {0}\".format(repr(registry)))\n\n    if registry in authconfig:\n        log.debug(\"Found {0}\".format(repr(registry)))\n        return authconfig[registry]\n\n    for key, config in six.iteritems(authconfig):\n        if resolve_index_name(key) == registry:\n            log.debug(\"Found {0}\".format(repr(key)))\n            return config\n\n    log.debug(\"No entry found\")\n    return None\n\n\ndef _resolve_authconfig_credstore(authconfig, registry, credstore_name):\n    if not registry or registry == INDEX_NAME:\n        # The ecosystem is a little schizophrenic with index.docker.io VS\n        # docker.io - in that case, it seems the full URL is necessary.\n        registry = 'https://index.docker.io/v1/'\n    log.debug(\"Looking for auth entry for {0}\".format(repr(registry)))\n    store = dockerpycreds.Store(credstore_name)\n    try:\n        data = store.get(registry)\n        res = {\n            'ServerAddress': registry,\n        }\n        if data['Username'] == TOKEN_USERNAME:\n            res['IdentityToken'] = data['Secret']\n        else:\n            res.update({\n                'Username': data['Username'],\n                'Password': data['Secret'],\n            })\n        return res\n    except dockerpycreds.CredentialsNotFound as e:\n        log.debug('No entry found')\n        return None\n    except dockerpycreds.StoreError as e:\n        raise errors.DockerException(\n            'Credentials store error: {0}'.format(repr(e))\n        )\n\n\ndef convert_to_hostname(url):\n    return url.replace('http://', '').replace('https://', '').split('/', 1)[0]\n\n\ndef decode_auth(auth):\n    if isinstance(auth, six.string_types):\n        auth = auth.encode('ascii')\n    s = base64.b64decode(auth)\n    login, pwd = s.split(b':', 1)\n    return login.decode('utf8'), pwd.decode('utf8')\n\n\ndef encode_header(auth):\n    auth_json = json.dumps(auth).encode('ascii')\n    return base64.urlsafe_b64encode(auth_json)\n\n\ndef parse_auth(entries, raise_on_error=False):\n    \"\"\"\n    Parses authentication entries\n\n    Args:\n      entries:        Dict of authentication entries.\n      raise_on_error: If set to true, an invalid format will raise\n                      InvalidConfigFile\n\n    Returns:\n      Authentication registry.\n    \"\"\"\n\n    conf = {}\n    for registry, entry in six.iteritems(entries):\n        if not isinstance(entry, dict):\n            log.debug(\n                'Config entry for key {0} is not auth config'.format(registry)\n            )\n            # We sometimes fall back to parsing the whole config as if it was\n            # the auth config by itself, for legacy purposes. In that case, we\n            # fail silently and return an empty conf if any of the keys is not\n            # formatted properly.\n            if raise_on_error:\n                raise errors.InvalidConfigFile(\n                    'Invalid configuration for registry {0}'.format(registry)\n                )\n            return {}\n        if 'identitytoken' in entry:\n            log.debug('Found an IdentityToken entry for registry {0}'.format(\n                registry\n            ))\n            conf[registry] = {\n                'IdentityToken': entry['identitytoken']\n            }\n            continue  # Other values are irrelevant if we have a token, skip.\n\n        if 'auth' not in entry:\n            # Starting with engine v1.11 (API 1.23), an empty dictionary is\n            # a valid value in the auths config.\n            # https://github.com/docker/compose/issues/3265\n            log.debug(\n                'Auth data for {0} is absent. Client might be using a '\n                'credentials store instead.'\n            )\n            conf[registry] = {}\n            continue\n\n        username, password = decode_auth(entry['auth'])\n        log.debug(\n            'Found entry (registry={0}, username={1})'\n            .format(repr(registry), repr(username))\n        )\n\n        conf[registry] = {\n            'username': username,\n            'password': password,\n            'email': entry.get('email'),\n            'serveraddress': registry,\n        }\n    return conf\n\n\ndef find_config_file(config_path=None):\n    paths = list(filter(None, [\n        config_path,  # 1\n        config_path_from_environment(),  # 2\n        os.path.join(home_dir(), DOCKER_CONFIG_FILENAME),  # 3\n        os.path.join(home_dir(), LEGACY_DOCKER_CONFIG_FILENAME),  # 4\n    ]))\n\n    log.debug(\"Trying paths: {0}\".format(repr(paths)))\n\n    for path in paths:\n        if os.path.exists(path):\n            log.debug(\"Found file at path: {0}\".format(path))\n            return path\n\n    log.debug(\"No config file found\")\n\n    return None\n\n\ndef config_path_from_environment():\n    config_dir = os.environ.get('DOCKER_CONFIG')\n    if not config_dir:\n        return None\n    return os.path.join(config_dir, os.path.basename(DOCKER_CONFIG_FILENAME))\n\n\ndef home_dir():\n    \"\"\"\n    Get the user's home directory, using the same logic as the Docker Engine\n    client - use %USERPROFILE% on Windows, $HOME/getuid on POSIX.\n    \"\"\"\n    if IS_WINDOWS_PLATFORM:\n        return os.environ.get('USERPROFILE', '')\n    else:\n        return os.path.expanduser('~')\n\n\ndef load_config(config_path=None):\n    \"\"\"\n    Loads authentication data from a Docker configuration file in the given\n    root directory or if config_path is passed use given path.\n    Lookup priority:\n        explicit config_path parameter > DOCKER_CONFIG environment variable >\n        ~/.docker/config.json > ~/.dockercfg\n    \"\"\"\n    config_file = find_config_file(config_path)\n\n    if not config_file:\n        return {}\n\n    try:\n        with open(config_file) as f:\n            data = json.load(f)\n            res = {}\n            if data.get('auths'):\n                log.debug(\"Found 'auths' section\")\n                res.update(parse_auth(data['auths'], raise_on_error=True))\n            if data.get('HttpHeaders'):\n                log.debug(\"Found 'HttpHeaders' section\")\n                res.update({'HttpHeaders': data['HttpHeaders']})\n            if data.get('credsStore'):\n                log.debug(\"Found 'credsStore' section\")\n                res.update({'credsStore': data['credsStore']})\n            if data.get('credHelpers'):\n                log.debug(\"Found 'credHelpers' section\")\n                res.update({'credHelpers': data['credHelpers']})\n            if res:\n                return res\n            else:\n                log.debug(\"Couldn't find 'auths' or 'HttpHeaders' sections\")\n                f.seek(0)\n                return parse_auth(json.load(f))\n    except (IOError, KeyError, ValueError) as e:\n        # Likely missing new Docker config file or it's in an\n        # unknown format, continue to attempt to read old location\n        # and format.\n        log.debug(e)\n\n    log.debug(\"Attempting to parse legacy auth file format\")\n    try:\n        data = []\n        with open(config_file) as f:\n            for line in f.readlines():\n                data.append(line.strip().split(' = ')[1])\n            if len(data) < 2:\n                # Not enough data\n                raise errors.InvalidConfigFile(\n                    'Invalid or empty configuration file!'\n                )\n\n        username, password = decode_auth(data[0])\n        return {\n            INDEX_NAME: {\n                'username': username,\n                'password': password,\n                'email': data[1],\n                'serveraddress': INDEX_URL,\n            }\n        }\n    except Exception as e:\n        log.debug(e)\n        pass\n\n    log.debug(\"All parsing attempts failed - returning empty config\")\n    return {}\n", "path": "docker/auth.py"}]}