Return "scope" in auth challenge

Containerd requires a "scope" in the www-authenticate header.
See https://github.com/containerd/containerd/issues/3556

Change-Id: I3f438c3595b8b864d47a735e736d309525d30a41
This commit is contained in:
James E. Blair 2021-07-31 18:09:19 -07:00
parent 720d091653
commit 0c03e1e054

View File

@ -71,9 +71,10 @@ class Authorization(cherrypy.Tool):
return False return False
return store[user] == password return store[user] == password
def unauthorized(self): def unauthorized(self, scope):
cherrypy.response.headers['www-authenticate'] = ( cherrypy.response.headers['www-authenticate'] = (
'Bearer realm="%s/auth/token"' % (self.public_url,) 'Bearer realm="%s/auth/token",scope="%s"' % (
self.public_url, scope)
) )
raise cherrypy.HTTPError(401, 'Authentication required') raise cherrypy.HTTPError(401, 'Authentication required')
@ -86,7 +87,7 @@ class Authorization(cherrypy.Tool):
self.log.debug('Auth ok %s', level) self.log.debug('Auth ok %s', level)
return return
self.log.debug('Unauthorized %s', level) self.log.debug('Unauthorized %s', level)
self.unauthorized() self.unauthorized(level)
def _get_level(self, scope): def _get_level(self, scope):
level = None level = None
@ -132,9 +133,9 @@ class Authorization(cherrypy.Tool):
level = self._get_level(kw.get('scope', '')) level = self._get_level(kw.get('scope', ''))
self.log.info('Authenticate level %s', level) self.log.info('Authenticate level %s', level)
if level == self.WRITE: if level == self.WRITE:
self._check_creds(auth_header, [self.rw]) self._check_creds(auth_header, [self.rw], level)
elif level == self.READ and not self.anonymous_read: elif level == self.READ and not self.anonymous_read:
self._check_creds(auth_header, [self.rw, self.ro]) self._check_creds(auth_header, [self.rw, self.ro], level)
# If we permit anonymous read and we're requesting read, no # If we permit anonymous read and we're requesting read, no
# check is performed. # check is performed.
self.log.debug('Generate %s token', level) self.log.debug('Generate %s token', level)
@ -142,7 +143,7 @@ class Authorization(cherrypy.Tool):
return {'token': token, return {'token': token,
'access_token': token} 'access_token': token}
def _check_creds(self, auth_header, credstores): def _check_creds(self, auth_header, credstores, level):
# If the password is okay, fall through; otherwise call # If the password is okay, fall through; otherwise call
# unauthorized for the side effect of raising an exception. # unauthorized for the side effect of raising an exception.
if auth_header and 'Basic' in auth_header: if auth_header and 'Basic' in auth_header:
@ -152,9 +153,9 @@ class Authorization(cherrypy.Tool):
# Return true on the first credstore with the user, false otherwise # Return true on the first credstore with the user, false otherwise
if not next(filter( if not next(filter(
lambda cs: self.check(cs, user, pw), credstores), False): lambda cs: self.check(cs, user, pw), credstores), False):
self.unauthorized() self.unauthorized(level)
else: else:
self.unauthorized() self.unauthorized(level)
class RegistryAPI: class RegistryAPI: