Kaydet (Commit) bc5d7c8c authored tarafından Joffrey F's avatar Joffrey F

Modernize auth management

Signed-off-by: 's avatarJoffrey F <joffrey@docker.com>
üst 9a67e203
...@@ -293,31 +293,11 @@ class BuildApiMixin(object): ...@@ -293,31 +293,11 @@ class BuildApiMixin(object):
# Send the full auth configuration (if any exists), since the build # Send the full auth configuration (if any exists), since the build
# could use any (or all) of the registries. # could use any (or all) of the registries.
if self._auth_configs: if self._auth_configs:
auth_cfgs = self._auth_configs auth_data = self._auth_configs.get_all_credentials()
auth_data = {}
if auth_cfgs.get('credsStore'):
# Using a credentials store, we need to retrieve the
# credentials for each registry listed in the config.json file
# Matches CLI behavior: https://github.com/docker/docker/blob/
# 67b85f9d26f1b0b2b240f2d794748fac0f45243c/cliconfig/
# credentials/native_store.go#L68-L83
for registry in auth_cfgs.get('auths', {}).keys():
auth_data[registry] = auth.resolve_authconfig(
auth_cfgs, registry,
credstore_env=self.credstore_env,
)
else:
for registry in auth_cfgs.get('credHelpers', {}).keys():
auth_data[registry] = auth.resolve_authconfig(
auth_cfgs, registry,
credstore_env=self.credstore_env
)
for registry, creds in auth_cfgs.get('auths', {}).items():
if registry not in auth_data:
auth_data[registry] = creds
# See https://github.com/docker/docker-py/issues/1683 # See https://github.com/docker/docker-py/issues/1683
if auth.INDEX_NAME in auth_data: if auth.INDEX_URL not in auth_data and auth.INDEX_URL in auth_data:
auth_data[auth.INDEX_URL] = auth_data[auth.INDEX_NAME] auth_data[auth.INDEX_URL] = auth_data.get(auth.INDEX_NAME, {})
log.debug( log.debug(
'Sending auth config ({0})'.format( 'Sending auth config ({0})'.format(
...@@ -325,6 +305,7 @@ class BuildApiMixin(object): ...@@ -325,6 +305,7 @@ class BuildApiMixin(object):
) )
) )
if auth_data:
headers['X-Registry-Config'] = auth.encode_header( headers['X-Registry-Config'] = auth.encode_header(
auth_data auth_data
) )
......
...@@ -124,13 +124,15 @@ class DaemonApiMixin(object): ...@@ -124,13 +124,15 @@ class DaemonApiMixin(object):
# If dockercfg_path is passed check to see if the config file exists, # If dockercfg_path is passed check to see if the config file exists,
# if so load that config. # if so load that config.
if dockercfg_path and os.path.exists(dockercfg_path): if dockercfg_path and os.path.exists(dockercfg_path):
self._auth_configs = auth.load_config(dockercfg_path) self._auth_configs = auth.load_config(
dockercfg_path, credstore_env=self.credstore_env
)
elif not self._auth_configs: elif not self._auth_configs:
self._auth_configs = auth.load_config() self._auth_configs = auth.load_config(
credstore_env=self.credstore_env
authcfg = auth.resolve_authconfig(
self._auth_configs, registry, credstore_env=self.credstore_env,
) )
authcfg = self._auth_configs.resolve_authconfig(registry)
# If we found an existing auth config for this registry and username # If we found an existing auth config for this registry and username
# combination, we can return it immediately unless reauth is requested. # combination, we can return it immediately unless reauth is requested.
if authcfg and authcfg.get('username', None) == username \ if authcfg and authcfg.get('username', None) == username \
...@@ -146,9 +148,7 @@ class DaemonApiMixin(object): ...@@ -146,9 +148,7 @@ class DaemonApiMixin(object):
response = self._post_json(self._url('/auth'), data=req_data) response = self._post_json(self._url('/auth'), data=req_data)
if response.status_code == 200: if response.status_code == 200:
if 'auths' not in self._auth_configs: self._auth_configs.add_auth(registry or auth.INDEX_NAME, req_data)
self._auth_configs['auths'] = {}
self._auth_configs['auths'][registry or auth.INDEX_NAME] = req_data
return self._result(response, json=True) return self._result(response, json=True)
def ping(self): def ping(self):
......
...@@ -70,101 +70,19 @@ def split_repo_name(repo_name): ...@@ -70,101 +70,19 @@ def split_repo_name(repo_name):
def get_credential_store(authconfig, registry): def get_credential_store(authconfig, registry):
if not registry or registry == INDEX_NAME: return authconfig.get_credential_store(registry)
registry = 'https://index.docker.io/v1/'
return authconfig.get('credHelpers', {}).get(registry) or authconfig.get(
'credsStore'
)
class AuthConfig(object):
def __init__(self, dct, credstore_env=None):
if 'auths' not in dct:
dct['auths'] = {}
self._dct = dct
self._credstore_env = credstore_env
self._stores = {}
def resolve_authconfig(authconfig, registry=None, credstore_env=None): @classmethod
""" def parse_auth(cls, entries, raise_on_error=False):
Returns the authentication data from the given auth configuration for a
specific registry. As with the Docker client, legacy entries in the config
with full URLs are stripped down to hostnames before checking for a match.
Returns None if no match was found.
"""
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)
)
cfg = _resolve_authconfig_credstore(
authconfig, registry, store_name, env=credstore_env
)
if cfg is not None:
return cfg
log.debug('No entry in credstore - fetching from auth dict')
# 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)))
authdict = authconfig.get('auths', {})
if registry in authdict:
log.debug("Found {0}".format(repr(registry)))
return authdict[registry]
for key, conf in six.iteritems(authdict):
if resolve_index_name(key) == registry:
log.debug("Found {0}".format(repr(key)))
return conf
log.debug("No entry found")
return None
def _resolve_authconfig_credstore(authconfig, registry, credstore_name,
env=None):
if not registry or registry == INDEX_NAME:
# The ecosystem is a little schizophrenic with index.docker.io VS
# docker.io - in that case, it seems the full URL is necessary.
registry = INDEX_URL
log.debug("Looking for auth entry for {0}".format(repr(registry)))
store = dockerpycreds.Store(credstore_name, environment=env)
try:
data = store.get(registry)
res = {
'ServerAddress': registry,
}
if data['Username'] == TOKEN_USERNAME:
res['IdentityToken'] = data['Secret']
else:
res.update({
'Username': data['Username'],
'Password': data['Secret'],
})
return res
except dockerpycreds.CredentialsNotFound as e:
log.debug('No entry found')
return None
except dockerpycreds.StoreError as e:
raise errors.DockerException(
'Credentials store error: {0}'.format(repr(e))
)
def convert_to_hostname(url):
return url.replace('http://', '').replace('https://', '').split('/', 1)[0]
def decode_auth(auth):
if isinstance(auth, six.string_types):
auth = auth.encode('ascii')
s = base64.b64decode(auth)
login, pwd = s.split(b':', 1)
return login.decode('utf8'), pwd.decode('utf8')
def encode_header(auth):
auth_json = json.dumps(auth).encode('ascii')
return base64.urlsafe_b64encode(auth_json)
def parse_auth(entries, raise_on_error=False):
""" """
Parses authentication entries Parses authentication entries
...@@ -181,25 +99,31 @@ def parse_auth(entries, raise_on_error=False): ...@@ -181,25 +99,31 @@ def parse_auth(entries, raise_on_error=False):
for registry, entry in six.iteritems(entries): for registry, entry in six.iteritems(entries):
if not isinstance(entry, dict): if not isinstance(entry, dict):
log.debug( log.debug(
'Config entry for key {0} is not auth config'.format(registry) 'Config entry for key {0} is not auth config'.format(
registry
)
) )
# We sometimes fall back to parsing the whole config as if it was # We sometimes fall back to parsing the whole config as if it
# the auth config by itself, for legacy purposes. In that case, we # was the auth config by itself, for legacy purposes. In that
# fail silently and return an empty conf if any of the keys is not # case, we fail silently and return an empty conf if any of the
# formatted properly. # keys is not formatted properly.
if raise_on_error: if raise_on_error:
raise errors.InvalidConfigFile( raise errors.InvalidConfigFile(
'Invalid configuration for registry {0}'.format(registry) 'Invalid configuration for registry {0}'.format(
registry
)
) )
return {} return {}
if 'identitytoken' in entry: if 'identitytoken' in entry:
log.debug('Found an IdentityToken entry for registry {0}'.format( log.debug(
'Found an IdentityToken entry for registry {0}'.format(
registry registry
)) )
)
conf[registry] = { conf[registry] = {
'IdentityToken': entry['identitytoken'] 'IdentityToken': entry['identitytoken']
} }
continue # Other values are irrelevant if we have a token, skip. continue # Other values are irrelevant if we have a token
if 'auth' not in entry: if 'auth' not in entry:
# Starting with engine v1.11 (API 1.23), an empty dictionary is # Starting with engine v1.11 (API 1.23), an empty dictionary is
...@@ -226,21 +150,21 @@ def parse_auth(entries, raise_on_error=False): ...@@ -226,21 +150,21 @@ def parse_auth(entries, raise_on_error=False):
} }
return conf return conf
@classmethod
def load_config(config_path=None, config_dict=None): def load_config(cls, config_path, config_dict, credstore_env=None):
""" """
Loads authentication data from a Docker configuration file in the given Loads authentication data from a Docker configuration file in the given
root directory or if config_path is passed use given path. root directory or if config_path is passed use given path.
Lookup priority: Lookup priority:
explicit config_path parameter > DOCKER_CONFIG environment variable > explicit config_path parameter > DOCKER_CONFIG environment
~/.docker/config.json > ~/.dockercfg variable > ~/.docker/config.json > ~/.dockercfg
""" """
if not config_dict: if not config_dict:
config_file = config.find_config_file(config_path) config_file = config.find_config_file(config_path)
if not config_file: if not config_file:
return {} return cls({}, credstore_env)
try: try:
with open(config_file) as f: with open(config_file) as f:
config_dict = json.load(f) config_dict = json.load(f)
...@@ -249,13 +173,15 @@ def load_config(config_path=None, config_dict=None): ...@@ -249,13 +173,15 @@ def load_config(config_path=None, config_dict=None):
# unknown format, continue to attempt to read old location # unknown format, continue to attempt to read old location
# and format. # and format.
log.debug(e) log.debug(e)
return _load_legacy_config(config_file) return cls(_load_legacy_config(config_file), credstore_env)
res = {} res = {}
if config_dict.get('auths'): if config_dict.get('auths'):
log.debug("Found 'auths' section") log.debug("Found 'auths' section")
res.update({ res.update({
'auths': parse_auth(config_dict.pop('auths'), raise_on_error=True) 'auths': cls.parse_auth(
config_dict.pop('auths'), raise_on_error=True
)
}) })
if config_dict.get('credsStore'): if config_dict.get('credsStore'):
log.debug("Found 'credsStore' section") log.debug("Found 'credsStore' section")
...@@ -264,13 +190,163 @@ def load_config(config_path=None, config_dict=None): ...@@ -264,13 +190,163 @@ def load_config(config_path=None, config_dict=None):
log.debug("Found 'credHelpers' section") log.debug("Found 'credHelpers' section")
res.update({'credHelpers': config_dict.pop('credHelpers')}) res.update({'credHelpers': config_dict.pop('credHelpers')})
if res: if res:
return res return cls(res, credstore_env)
log.debug( log.debug(
"Couldn't find auth-related section ; attempting to interpret " "Couldn't find auth-related section ; attempting to interpret "
"as auth-only file" "as auth-only file"
) )
return {'auths': parse_auth(config_dict)} return cls({'auths': cls.parse_auth(config_dict)}, credstore_env)
@property
def auths(self):
return self._dct.get('auths', {})
@property
def creds_store(self):
return self._dct.get('credsStore', None)
@property
def cred_helpers(self):
return self._dct.get('credHelpers', {})
def resolve_authconfig(self, registry=None):
"""
Returns the authentication data from the given auth configuration for a
specific registry. As with the Docker client, legacy entries in the
config with full URLs are stripped down to hostnames before checking
for a match. Returns None if no match was found.
"""
if self.creds_store or self.cred_helpers:
store_name = self.get_credential_store(registry)
if store_name is not None:
log.debug(
'Using credentials store "{0}"'.format(store_name)
)
cfg = self._resolve_authconfig_credstore(registry, store_name)
if cfg is not None:
return cfg
log.debug('No entry in credstore - fetching from auth dict')
# 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)))
if registry in self.auths:
log.debug("Found {0}".format(repr(registry)))
return self.auths[registry]
for key, conf in six.iteritems(self.auths):
if resolve_index_name(key) == registry:
log.debug("Found {0}".format(repr(key)))
return conf
log.debug("No entry found")
return None
def _resolve_authconfig_credstore(self, registry, credstore_name):
if not registry or registry == INDEX_NAME:
# The ecosystem is a little schizophrenic with index.docker.io VS
# docker.io - in that case, it seems the full URL is necessary.
registry = INDEX_URL
log.debug("Looking for auth entry for {0}".format(repr(registry)))
store = self._get_store_instance(credstore_name)
try:
data = store.get(registry)
res = {
'ServerAddress': registry,
}
if data['Username'] == TOKEN_USERNAME:
res['IdentityToken'] = data['Secret']
else:
res.update({
'Username': data['Username'],
'Password': data['Secret'],
})
return res
except dockerpycreds.CredentialsNotFound as e:
log.debug('No entry found')
return None
except dockerpycreds.StoreError as e:
raise errors.DockerException(
'Credentials store error: {0}'.format(repr(e))
)
def _get_store_instance(self, name):
if name not in self._stores:
self._stores[name] = dockerpycreds.Store(
name, environment=self._credstore_env
)
return self._stores[name]
def get_credential_store(self, registry):
if not registry or registry == INDEX_NAME:
registry = 'https://index.docker.io/v1/'
return self.cred_helpers.get(registry) or self.creds_store
def get_all_credentials(self):
auth_data = self.auths.copy()
if self.creds_store:
# Retrieve all credentials from the default store
store = self._get_store_instance(self.creds_store)
for k in store.list().keys():
auth_data[k] = self._resolve_authconfig_credstore(
k, self.creds_store
)
# credHelpers entries take priority over all others
for reg, store_name in self.cred_helpers.items():
auth_data[reg] = self._resolve_authconfig_credstore(
reg, store_name
)
return auth_data
def add_auth(self, reg, data):
self._dct['auths'][reg] = data
def resolve_authconfig(authconfig, registry=None, credstore_env=None):
return authconfig.resolve_authconfig(registry)
def convert_to_hostname(url):
return url.replace('http://', '').replace('https://', '').split('/', 1)[0]
def decode_auth(auth):
if isinstance(auth, six.string_types):
auth = auth.encode('ascii')
s = base64.b64decode(auth)
login, pwd = s.split(b':', 1)
return login.decode('utf8'), pwd.decode('utf8')
def encode_header(auth):
auth_json = json.dumps(auth).encode('ascii')
return base64.urlsafe_b64encode(auth_json)
def parse_auth(entries, raise_on_error=False):
"""
Parses authentication entries
Args:
entries: Dict of authentication entries.
raise_on_error: If set to true, an invalid format will raise
InvalidConfigFile
Returns:
Authentication registry.
"""
return AuthConfig.parse_auth(entries, raise_on_error)
def load_config(config_path=None, config_dict=None, credstore_env=None):
return AuthConfig.load_config(config_path, config_dict, credstore_env)
def _load_legacy_config(config_file): def _load_legacy_config(config_file):
......
...@@ -4,7 +4,7 @@ backports.ssl-match-hostname==3.5.0.1 ...@@ -4,7 +4,7 @@ backports.ssl-match-hostname==3.5.0.1
cffi==1.10.0 cffi==1.10.0
cryptography==1.9; python_version == '3.3' cryptography==1.9; python_version == '3.3'
cryptography==2.3; python_version > '3.3' cryptography==2.3; python_version > '3.3'
docker-pycreds==0.3.0 docker-pycreds==0.4.0
enum34==1.1.6 enum34==1.1.6
idna==2.5 idna==2.5
ipaddress==1.0.18 ipaddress==1.0.18
......
...@@ -12,7 +12,7 @@ SOURCE_DIR = os.path.join(ROOT_DIR) ...@@ -12,7 +12,7 @@ SOURCE_DIR = os.path.join(ROOT_DIR)
requirements = [ requirements = [
'six >= 1.4.0', 'six >= 1.4.0',
'websocket-client >= 0.32.0', 'websocket-client >= 0.32.0',
'docker-pycreds >= 0.3.0', 'docker-pycreds >= 0.4.0',
'requests >= 2.14.2, != 2.18.0', 'requests >= 2.14.2, != 2.18.0',
] ]
......
...@@ -65,7 +65,7 @@ class BuildTest(BaseAPIClientTest): ...@@ -65,7 +65,7 @@ class BuildTest(BaseAPIClientTest):
) )
def test_build_remote_with_registry_auth(self): def test_build_remote_with_registry_auth(self):
self.client._auth_configs = { self.client._auth_configs = auth.AuthConfig({
'auths': { 'auths': {
'https://example.com': { 'https://example.com': {
'user': 'example', 'user': 'example',
...@@ -73,7 +73,7 @@ class BuildTest(BaseAPIClientTest): ...@@ -73,7 +73,7 @@ class BuildTest(BaseAPIClientTest):
'email': 'example@example.com' 'email': 'example@example.com'
} }
} }
} })
expected_params = {'t': None, 'q': False, 'dockerfile': None, expected_params = {'t': None, 'q': False, 'dockerfile': None,
'rm': False, 'nocache': False, 'pull': False, 'rm': False, 'nocache': False, 'pull': False,
...@@ -81,7 +81,7 @@ class BuildTest(BaseAPIClientTest): ...@@ -81,7 +81,7 @@ class BuildTest(BaseAPIClientTest):
'remote': 'https://github.com/docker-library/mongo'} 'remote': 'https://github.com/docker-library/mongo'}
expected_headers = { expected_headers = {
'X-Registry-Config': auth.encode_header( 'X-Registry-Config': auth.encode_header(
self.client._auth_configs['auths'] self.client._auth_configs.auths
) )
} }
...@@ -115,7 +115,7 @@ class BuildTest(BaseAPIClientTest): ...@@ -115,7 +115,7 @@ class BuildTest(BaseAPIClientTest):
}) })
def test_set_auth_headers_with_empty_dict_and_auth_configs(self): def test_set_auth_headers_with_empty_dict_and_auth_configs(self):
self.client._auth_configs = { self.client._auth_configs = auth.AuthConfig({
'auths': { 'auths': {
'https://example.com': { 'https://example.com': {
'user': 'example', 'user': 'example',
...@@ -123,12 +123,12 @@ class BuildTest(BaseAPIClientTest): ...@@ -123,12 +123,12 @@ class BuildTest(BaseAPIClientTest):
'email': 'example@example.com' 'email': 'example@example.com'
} }
} }
} })
headers = {} headers = {}
expected_headers = { expected_headers = {
'X-Registry-Config': auth.encode_header( 'X-Registry-Config': auth.encode_header(
self.client._auth_configs['auths'] self.client._auth_configs.auths
) )
} }
...@@ -136,7 +136,7 @@ class BuildTest(BaseAPIClientTest): ...@@ -136,7 +136,7 @@ class BuildTest(BaseAPIClientTest):
assert headers == expected_headers assert headers == expected_headers
def test_set_auth_headers_with_dict_and_auth_configs(self): def test_set_auth_headers_with_dict_and_auth_configs(self):
self.client._auth_configs = { self.client._auth_configs = auth.AuthConfig({
'auths': { 'auths': {
'https://example.com': { 'https://example.com': {
'user': 'example', 'user': 'example',
...@@ -144,12 +144,12 @@ class BuildTest(BaseAPIClientTest): ...@@ -144,12 +144,12 @@ class BuildTest(BaseAPIClientTest):
'email': 'example@example.com' 'email': 'example@example.com'
} }
} }
} })
headers = {'foo': 'bar'} headers = {'foo': 'bar'}
expected_headers = { expected_headers = {
'X-Registry-Config': auth.encode_header( 'X-Registry-Config': auth.encode_header(
self.client._auth_configs['auths'] self.client._auth_configs.auths
), ),
'foo': 'bar' 'foo': 'bar'
} }
......
...@@ -221,14 +221,12 @@ class DockerApiTest(BaseAPIClientTest): ...@@ -221,14 +221,12 @@ class DockerApiTest(BaseAPIClientTest):
'username': 'sakuya', 'password': 'izayoi' 'username': 'sakuya', 'password': 'izayoi'
} }
assert args[1]['headers'] == {'Content-Type': 'application/json'} assert args[1]['headers'] == {'Content-Type': 'application/json'}
assert self.client._auth_configs['auths'] == { assert self.client._auth_configs.auths['docker.io'] == {
'docker.io': {
'email': None, 'email': None,
'password': 'izayoi', 'password': 'izayoi',
'username': 'sakuya', 'username': 'sakuya',
'serveraddress': None, 'serveraddress': None,
} }
}
def test_events(self): def test_events(self):
self.client.events() self.client.events()
......
...@@ -106,13 +106,13 @@ class ResolveAuthTest(unittest.TestCase): ...@@ -106,13 +106,13 @@ class ResolveAuthTest(unittest.TestCase):
private_config = {'auth': encode_auth({'username': 'privateuser'})} private_config = {'auth': encode_auth({'username': 'privateuser'})}
legacy_config = {'auth': encode_auth({'username': 'legacyauth'})} legacy_config = {'auth': encode_auth({'username': 'legacyauth'})}
auth_config = { auth_config = auth.AuthConfig({
'auths': auth.parse_auth({ 'auths': auth.parse_auth({
'https://index.docker.io/v1/': index_config, 'https://index.docker.io/v1/': index_config,
'my.registry.net': private_config, 'my.registry.net': private_config,
'http://legacy.registry.url/v1/': legacy_config, 'http://legacy.registry.url/v1/': legacy_config,
}) })
} })
def test_resolve_authconfig_hostname_only(self): def test_resolve_authconfig_hostname_only(self):
assert auth.resolve_authconfig( assert auth.resolve_authconfig(
...@@ -211,13 +211,15 @@ class ResolveAuthTest(unittest.TestCase): ...@@ -211,13 +211,15 @@ class ResolveAuthTest(unittest.TestCase):
) is None ) is None
def test_resolve_auth_with_empty_credstore_and_auth_dict(self): def test_resolve_auth_with_empty_credstore_and_auth_dict(self):
auth_config = { auth_config = auth.AuthConfig({
'auths': auth.parse_auth({ 'auths': auth.parse_auth({
'https://index.docker.io/v1/': self.index_config, 'https://index.docker.io/v1/': self.index_config,
}), }),
'credsStore': 'blackbox' 'credsStore': 'blackbox'
} })
with mock.patch('docker.auth._resolve_authconfig_credstore') as m: with mock.patch(
'docker.auth.AuthConfig._resolve_authconfig_credstore'
) as m:
m.return_value = None m.return_value = None
assert 'indexuser' == auth.resolve_authconfig( assert 'indexuser' == auth.resolve_authconfig(
auth_config, None auth_config, None
...@@ -226,13 +228,13 @@ class ResolveAuthTest(unittest.TestCase): ...@@ -226,13 +228,13 @@ class ResolveAuthTest(unittest.TestCase):
class CredStoreTest(unittest.TestCase): class CredStoreTest(unittest.TestCase):
def test_get_credential_store(self): def test_get_credential_store(self):
auth_config = { auth_config = auth.AuthConfig({
'credHelpers': { 'credHelpers': {
'registry1.io': 'truesecret', 'registry1.io': 'truesecret',
'registry2.io': 'powerlock' 'registry2.io': 'powerlock'
}, },
'credsStore': 'blackbox', 'credsStore': 'blackbox',
} })
assert auth.get_credential_store( assert auth.get_credential_store(
auth_config, 'registry1.io' auth_config, 'registry1.io'
...@@ -245,12 +247,12 @@ class CredStoreTest(unittest.TestCase): ...@@ -245,12 +247,12 @@ class CredStoreTest(unittest.TestCase):
) == 'blackbox' ) == 'blackbox'
def test_get_credential_store_no_default(self): def test_get_credential_store_no_default(self):
auth_config = { auth_config = auth.AuthConfig({
'credHelpers': { 'credHelpers': {
'registry1.io': 'truesecret', 'registry1.io': 'truesecret',
'registry2.io': 'powerlock' 'registry2.io': 'powerlock'
}, },
} })
assert auth.get_credential_store( assert auth.get_credential_store(
auth_config, 'registry2.io' auth_config, 'registry2.io'
) == 'powerlock' ) == 'powerlock'
...@@ -259,12 +261,12 @@ class CredStoreTest(unittest.TestCase): ...@@ -259,12 +261,12 @@ class CredStoreTest(unittest.TestCase):
) is None ) is None
def test_get_credential_store_default_index(self): def test_get_credential_store_default_index(self):
auth_config = { auth_config = auth.AuthConfig({
'credHelpers': { 'credHelpers': {
'https://index.docker.io/v1/': 'powerlock' 'https://index.docker.io/v1/': 'powerlock'
}, },
'credsStore': 'truesecret' 'credsStore': 'truesecret'
} })
assert auth.get_credential_store(auth_config, None) == 'powerlock' assert auth.get_credential_store(auth_config, None) == 'powerlock'
assert auth.get_credential_store( assert auth.get_credential_store(
...@@ -293,8 +295,8 @@ class LoadConfigTest(unittest.TestCase): ...@@ -293,8 +295,8 @@ class LoadConfigTest(unittest.TestCase):
cfg = auth.load_config(cfg_path) cfg = auth.load_config(cfg_path)
assert auth.resolve_authconfig(cfg) is not None assert auth.resolve_authconfig(cfg) is not None
assert cfg['auths'][auth.INDEX_NAME] is not None assert cfg.auths[auth.INDEX_NAME] is not None
cfg = cfg['auths'][auth.INDEX_NAME] cfg = cfg.auths[auth.INDEX_NAME]
assert cfg['username'] == 'sakuya' assert cfg['username'] == 'sakuya'
assert cfg['password'] == 'izayoi' assert cfg['password'] == 'izayoi'
assert cfg['email'] == 'sakuya@scarlet.net' assert cfg['email'] == 'sakuya@scarlet.net'
...@@ -312,8 +314,8 @@ class LoadConfigTest(unittest.TestCase): ...@@ -312,8 +314,8 @@ class LoadConfigTest(unittest.TestCase):
) )
cfg = auth.load_config(cfg_path) cfg = auth.load_config(cfg_path)
assert auth.resolve_authconfig(cfg) is not None assert auth.resolve_authconfig(cfg) is not None
assert cfg['auths'][auth.INDEX_URL] is not None assert cfg.auths[auth.INDEX_URL] is not None
cfg = cfg['auths'][auth.INDEX_URL] cfg = cfg.auths[auth.INDEX_URL]
assert cfg['username'] == 'sakuya' assert cfg['username'] == 'sakuya'
assert cfg['password'] == 'izayoi' assert cfg['password'] == 'izayoi'
assert cfg['email'] == email assert cfg['email'] == email
...@@ -335,8 +337,8 @@ class LoadConfigTest(unittest.TestCase): ...@@ -335,8 +337,8 @@ class LoadConfigTest(unittest.TestCase):
}, f) }, f)
cfg = auth.load_config(cfg_path) cfg = auth.load_config(cfg_path)
assert auth.resolve_authconfig(cfg) is not None assert auth.resolve_authconfig(cfg) is not None
assert cfg['auths'][auth.INDEX_URL] is not None assert cfg.auths[auth.INDEX_URL] is not None
cfg = cfg['auths'][auth.INDEX_URL] cfg = cfg.auths[auth.INDEX_URL]
assert cfg['username'] == 'sakuya' assert cfg['username'] == 'sakuya'
assert cfg['password'] == 'izayoi' assert cfg['password'] == 'izayoi'
assert cfg['email'] == email assert cfg['email'] == email
...@@ -360,7 +362,7 @@ class LoadConfigTest(unittest.TestCase): ...@@ -360,7 +362,7 @@ class LoadConfigTest(unittest.TestCase):
with open(dockercfg_path, 'w') as f: with open(dockercfg_path, 'w') as f:
json.dump(config, f) json.dump(config, f)
cfg = auth.load_config(dockercfg_path)['auths'] cfg = auth.load_config(dockercfg_path).auths
assert registry in cfg assert registry in cfg
assert cfg[registry] is not None assert cfg[registry] is not None
cfg = cfg[registry] cfg = cfg[registry]
...@@ -387,7 +389,7 @@ class LoadConfigTest(unittest.TestCase): ...@@ -387,7 +389,7 @@ class LoadConfigTest(unittest.TestCase):
json.dump(config, f) json.dump(config, f)
with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}): with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}):
cfg = auth.load_config(None)['auths'] cfg = auth.load_config(None).auths
assert registry in cfg assert registry in cfg
assert cfg[registry] is not None assert cfg[registry] is not None
cfg = cfg[registry] cfg = cfg[registry]
...@@ -417,8 +419,8 @@ class LoadConfigTest(unittest.TestCase): ...@@ -417,8 +419,8 @@ class LoadConfigTest(unittest.TestCase):
with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}): with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}):
cfg = auth.load_config(None) cfg = auth.load_config(None)
assert registry in cfg['auths'] assert registry in cfg.auths
cfg = cfg['auths'][registry] cfg = cfg.auths[registry]
assert cfg['username'] == 'sakuya' assert cfg['username'] == 'sakuya'
assert cfg['password'] == 'izayoi' assert cfg['password'] == 'izayoi'
assert cfg['email'] == 'sakuya@scarlet.net' assert cfg['email'] == 'sakuya@scarlet.net'
...@@ -446,8 +448,8 @@ class LoadConfigTest(unittest.TestCase): ...@@ -446,8 +448,8 @@ class LoadConfigTest(unittest.TestCase):
with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}): with mock.patch.dict(os.environ, {'DOCKER_CONFIG': folder}):
cfg = auth.load_config(None) cfg = auth.load_config(None)
assert registry in cfg['auths'] assert registry in cfg.auths
cfg = cfg['auths'][registry] cfg = cfg.auths[registry]
assert cfg['username'] == b'sakuya\xc3\xa6'.decode('utf8') assert cfg['username'] == b'sakuya\xc3\xa6'.decode('utf8')
assert cfg['password'] == b'izayoi\xc3\xa6'.decode('utf8') assert cfg['password'] == b'izayoi\xc3\xa6'.decode('utf8')
assert cfg['email'] == 'sakuya@scarlet.net' assert cfg['email'] == 'sakuya@scarlet.net'
...@@ -464,7 +466,7 @@ class LoadConfigTest(unittest.TestCase): ...@@ -464,7 +466,7 @@ class LoadConfigTest(unittest.TestCase):
json.dump(config, f) json.dump(config, f)
cfg = auth.load_config(dockercfg_path) cfg = auth.load_config(dockercfg_path)
assert cfg == {'auths': {}} assert cfg._dct == {'auths': {}}
def test_load_config_invalid_auth_dict(self): def test_load_config_invalid_auth_dict(self):
folder = tempfile.mkdtemp() folder = tempfile.mkdtemp()
...@@ -479,7 +481,7 @@ class LoadConfigTest(unittest.TestCase): ...@@ -479,7 +481,7 @@ class LoadConfigTest(unittest.TestCase):
json.dump(config, f) json.dump(config, f)
cfg = auth.load_config(dockercfg_path) cfg = auth.load_config(dockercfg_path)
assert cfg == {'auths': {'scarlet.net': {}}} assert cfg._dct == {'auths': {'scarlet.net': {}}}
def test_load_config_identity_token(self): def test_load_config_identity_token(self):
folder = tempfile.mkdtemp() folder = tempfile.mkdtemp()
...@@ -500,7 +502,7 @@ class LoadConfigTest(unittest.TestCase): ...@@ -500,7 +502,7 @@ class LoadConfigTest(unittest.TestCase):
json.dump(config, f) json.dump(config, f)
cfg = auth.load_config(dockercfg_path) cfg = auth.load_config(dockercfg_path)
assert registry in cfg['auths'] assert registry in cfg.auths
cfg = cfg['auths'][registry] cfg = cfg.auths[registry]
assert 'IdentityToken' in cfg assert 'IdentityToken' in cfg
assert cfg['IdentityToken'] == token assert cfg['IdentityToken'] == token
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment