Refactor auth code to be cleaner and more extensible
We move all the auth handling, serialization and deserialization into a new AuthContext interface, and then standardize a registration model for handling of specific auth context types (user, robot, token, etc).
This commit is contained in:
parent
8ba2e71fb1
commit
e220b50543
31 changed files with 822 additions and 436 deletions
|
@ -1,11 +1,5 @@
|
|||
from enum import Enum
|
||||
from flask_principal import Identity, identity_changed
|
||||
|
||||
from app import app
|
||||
from auth.auth_context import (set_authenticated_user, set_validated_token, set_grant_context,
|
||||
set_validated_oauth_token, set_validated_app_specific_token)
|
||||
from auth.scopes import scopes_from_scope_string
|
||||
from auth.permissions import QuayDeferredPermissionUser
|
||||
from auth.auth_context_type import ValidatedAuthContext, ContextEntityKind
|
||||
|
||||
|
||||
class AuthKind(Enum):
|
||||
|
@ -22,94 +16,37 @@ class ValidateResult(object):
|
|||
robot=None, appspecifictoken=None, signed_data=None, error_message=None):
|
||||
self.kind = kind
|
||||
self.missing = missing
|
||||
self.user = user
|
||||
self.robot = robot
|
||||
self.token = token
|
||||
self.oauthtoken = oauthtoken
|
||||
self.appspecifictoken = appspecifictoken
|
||||
self.signed_data = signed_data
|
||||
self.error_message = error_message
|
||||
self.context = ValidatedAuthContext(user=user, token=token, oauthtoken=oauthtoken, robot=robot,
|
||||
appspecifictoken=appspecifictoken, signed_data=signed_data)
|
||||
|
||||
def tuple(self):
|
||||
return (self.kind, self.missing, self.user, self.token, self.oauthtoken, self.robot,
|
||||
self.appspecifictoken, self.signed_data, self.error_message)
|
||||
return (self.kind, self.missing, self.error_message, self.context.tuple())
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.tuple() == other.tuple()
|
||||
|
||||
def apply_to_context(self):
|
||||
""" Applies this auth result to the auth context and Flask-Principal. """
|
||||
# Set the various pieces of the auth context.
|
||||
if self.oauthtoken:
|
||||
set_authenticated_user(self.authed_user)
|
||||
set_validated_oauth_token(self.oauthtoken)
|
||||
elif self.appspecifictoken:
|
||||
set_authenticated_user(self.authed_user)
|
||||
set_validated_app_specific_token(self.appspecifictoken)
|
||||
elif self.authed_user:
|
||||
set_authenticated_user(self.authed_user)
|
||||
elif self.token:
|
||||
set_validated_token(self.token)
|
||||
elif self.signed_data:
|
||||
if self.signed_data['user_context']:
|
||||
set_grant_context({
|
||||
'user': self.signed_data['user_context'],
|
||||
'kind': 'user',
|
||||
})
|
||||
|
||||
# Set the identity for Flask-Principal.
|
||||
if self.identity:
|
||||
identity_changed.send(app, identity=self.identity)
|
||||
self.context.apply_to_request_context()
|
||||
|
||||
def with_kind(self, kind):
|
||||
""" Returns a copy of this result, but with the kind replaced. """
|
||||
return ValidateResult(kind, self.missing, self.user, self.token, self.oauthtoken, self.robot,
|
||||
self.appspecifictoken, self.signed_data, self.error_message)
|
||||
result = ValidateResult(kind, missing=self.missing, error_message=self.error_message)
|
||||
result.context = self.context
|
||||
return result
|
||||
|
||||
@property
|
||||
def authed_user(self):
|
||||
""" Returns the authenticated user, whether directly, or via an OAuth token. """
|
||||
if not self.auth_valid:
|
||||
return None
|
||||
|
||||
if self.oauthtoken:
|
||||
return self.oauthtoken.authorized_user
|
||||
|
||||
if self.appspecifictoken:
|
||||
return self.appspecifictoken.user
|
||||
|
||||
return self.user if self.user else self.robot
|
||||
return self.context.authed_user
|
||||
|
||||
@property
|
||||
def identity(self):
|
||||
""" Returns the identity for the auth result. """
|
||||
if not self.auth_valid:
|
||||
return None
|
||||
|
||||
if self.oauthtoken:
|
||||
scope_set = scopes_from_scope_string(self.oauthtoken.scope)
|
||||
return QuayDeferredPermissionUser.for_user(self.oauthtoken.authorized_user, scope_set)
|
||||
|
||||
if self.authed_user:
|
||||
return QuayDeferredPermissionUser.for_user(self.authed_user)
|
||||
|
||||
if self.token:
|
||||
return Identity(self.token.code, 'token')
|
||||
|
||||
if self.signed_data:
|
||||
identity = Identity(None, 'signed_grant')
|
||||
identity.provides.update(self.signed_data['grants'])
|
||||
return identity
|
||||
|
||||
return None
|
||||
|
||||
@property
|
||||
def has_user(self):
|
||||
def has_nonrobot_user(self):
|
||||
""" Returns whether a user (not a robot) was authenticated successfully. """
|
||||
return bool(self.user)
|
||||
return self.context.has_nonrobot_user
|
||||
|
||||
@property
|
||||
def auth_valid(self):
|
||||
""" Returns whether authentication successfully occurred. """
|
||||
return (self.user or self.token or self.oauthtoken or self.appspecifictoken or self.robot or
|
||||
self.signed_data)
|
||||
return self.context.entity_kind != ContextEntityKind.anonymous
|
||||
|
|
Reference in a new issue