Fix cookie auth to work with oauth token auth. Make sure user loading is truly deferred to save DB connections.
This commit is contained in:
parent
e759066ae0
commit
5bb4008880
6 changed files with 65 additions and 26 deletions
2
app.py
2
app.py
|
@ -35,7 +35,7 @@ else:
|
||||||
|
|
||||||
app.config.from_object(config)
|
app.config.from_object(config)
|
||||||
|
|
||||||
Principal(app, use_sessions=True)
|
Principal(app, use_sessions=False)
|
||||||
|
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
login_manager.init_app(app)
|
login_manager.init_app(app)
|
||||||
|
|
32
auth/auth.py
32
auth/auth.py
|
@ -2,16 +2,19 @@ import logging
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from flask import request, _request_ctx_stack, session
|
from flask import request, session
|
||||||
from flask.ext.principal import identity_changed, Identity
|
from flask.ext.principal import identity_changed, Identity
|
||||||
|
from flask.ext.login import current_user
|
||||||
from base64 import b64decode
|
from base64 import b64decode
|
||||||
|
|
||||||
|
import scopes
|
||||||
|
|
||||||
from data import model
|
from data import model
|
||||||
from data.model import oauth
|
from data.model import oauth
|
||||||
from app import app
|
from app import app
|
||||||
from permissions import QuayDeferredPermissionUser
|
from permissions import QuayDeferredPermissionUser
|
||||||
import scopes
|
from auth_context import (set_authenticated_user, set_validated_token,
|
||||||
|
set_authenticated_user_deferred)
|
||||||
from util.http import abort
|
from util.http import abort
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,8 +37,7 @@ def process_basic_auth(auth):
|
||||||
try:
|
try:
|
||||||
token = model.load_token_data(credentials[1])
|
token = model.load_token_data(credentials[1])
|
||||||
logger.debug('Successfully validated token: %s' % credentials[1])
|
logger.debug('Successfully validated token: %s' % credentials[1])
|
||||||
ctx = _request_ctx_stack.top
|
set_validated_token(token)
|
||||||
ctx.validated_token = token
|
|
||||||
|
|
||||||
identity_changed.send(app, identity=Identity(token.code, 'token'))
|
identity_changed.send(app, identity=Identity(token.code, 'token'))
|
||||||
return
|
return
|
||||||
|
@ -49,8 +51,7 @@ def process_basic_auth(auth):
|
||||||
try:
|
try:
|
||||||
robot = model.verify_robot(credentials[0], credentials[1])
|
robot = model.verify_robot(credentials[0], credentials[1])
|
||||||
logger.debug('Successfully validated robot: %s' % credentials[0])
|
logger.debug('Successfully validated robot: %s' % credentials[0])
|
||||||
ctx = _request_ctx_stack.top
|
set_authenticated_user(robot)
|
||||||
ctx.authenticated_user = robot
|
|
||||||
|
|
||||||
deferred_robot = QuayDeferredPermissionUser(robot.username, 'username')
|
deferred_robot = QuayDeferredPermissionUser(robot.username, 'username')
|
||||||
identity_changed.send(app, identity=deferred_robot)
|
identity_changed.send(app, identity=deferred_robot)
|
||||||
|
@ -63,8 +64,7 @@ def process_basic_auth(auth):
|
||||||
|
|
||||||
if authenticated:
|
if authenticated:
|
||||||
logger.debug('Successfully validated user: %s' % authenticated.username)
|
logger.debug('Successfully validated user: %s' % authenticated.username)
|
||||||
ctx = _request_ctx_stack.top
|
set_authenticated_user(authenticated)
|
||||||
ctx.authenticated_user = authenticated
|
|
||||||
|
|
||||||
new_identity = QuayDeferredPermissionUser(authenticated.username, 'username')
|
new_identity = QuayDeferredPermissionUser(authenticated.username, 'username')
|
||||||
identity_changed.send(app, identity=new_identity)
|
identity_changed.send(app, identity=new_identity)
|
||||||
|
@ -102,8 +102,7 @@ def process_token(auth):
|
||||||
auth=auth)
|
auth=auth)
|
||||||
|
|
||||||
logger.debug('Successfully validated token: %s', token_data.code)
|
logger.debug('Successfully validated token: %s', token_data.code)
|
||||||
ctx = _request_ctx_stack.top
|
set_validated_token(token_data)
|
||||||
ctx.validated_token = token_data
|
|
||||||
|
|
||||||
identity_changed.send(app, identity=Identity(token_data.code, 'token'))
|
identity_changed.send(app, identity=Identity(token_data.code, 'token'))
|
||||||
|
|
||||||
|
@ -141,15 +140,18 @@ def process_oauth(f):
|
||||||
scope_set = scopes.scopes_from_scope_string(validated.scope)
|
scope_set = scopes.scopes_from_scope_string(validated.scope)
|
||||||
logger.debug('Successfully validated oauth access token: %s with scope: %s', token,
|
logger.debug('Successfully validated oauth access token: %s with scope: %s', token,
|
||||||
scope_set)
|
scope_set)
|
||||||
|
set_authenticated_user(validated.authorized_user)
|
||||||
ctx = _request_ctx_stack.top
|
|
||||||
ctx.authenticated_user = validated.authorized_user
|
|
||||||
|
|
||||||
new_identity = QuayDeferredPermissionUser(validated.authorized_user.username, 'username',
|
new_identity = QuayDeferredPermissionUser(validated.authorized_user.username, 'username',
|
||||||
scope_set)
|
scope_set)
|
||||||
identity_changed.send(app, identity=new_identity)
|
identity_changed.send(app, identity=new_identity)
|
||||||
|
elif not current_user.is_anonymous():
|
||||||
|
logger.debug('Loading user from cookie: %s', current_user.get_id())
|
||||||
|
set_authenticated_user_deferred(current_user.get_id())
|
||||||
|
loaded = QuayDeferredPermissionUser(current_user.get_id(), 'username')
|
||||||
|
identity_changed.send(app, identity=loaded)
|
||||||
else:
|
else:
|
||||||
logger.debug('No auth header.')
|
logger.debug('No auth header or user session.')
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,44 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
from flask import _request_ctx_stack
|
from flask import _request_ctx_stack
|
||||||
|
from data import model
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_authenticated_user():
|
def get_authenticated_user():
|
||||||
return getattr(_request_ctx_stack.top, 'authenticated_user', None)
|
user = getattr(_request_ctx_stack.top, 'authenticated_user', None)
|
||||||
|
if not user:
|
||||||
|
username = getattr(_request_ctx_stack.top, 'authenticated_username', None)
|
||||||
|
if not username:
|
||||||
|
logger.debug('No authenticated user or deferred username.')
|
||||||
|
return None
|
||||||
|
|
||||||
|
logger.debug('Loading deferred authenticated user.')
|
||||||
|
loaded = model.get_user(username)
|
||||||
|
set_authenticated_user(loaded)
|
||||||
|
user = loaded
|
||||||
|
|
||||||
|
logger.debug('Returning authenticated user: %s', user.username)
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
def set_authenticated_user(user_or_robot):
|
||||||
|
ctx = _request_ctx_stack.top
|
||||||
|
ctx.authenticated_user = user_or_robot
|
||||||
|
|
||||||
|
|
||||||
|
def set_authenticated_user_deferred(username_or_robotname):
|
||||||
|
logger.debug('Deferring loading of authenticated user object: %s', username_or_robotname)
|
||||||
|
ctx = _request_ctx_stack.top
|
||||||
|
ctx.authenticated_username = username_or_robotname
|
||||||
|
|
||||||
|
|
||||||
def get_validated_token():
|
def get_validated_token():
|
||||||
return getattr(_request_ctx_stack.top, 'validated_token', None)
|
return getattr(_request_ctx_stack.top, 'validated_token', None)
|
||||||
|
|
||||||
|
|
||||||
|
def set_validated_token(token):
|
||||||
|
ctx = _request_ctx_stack.top
|
||||||
|
ctx.validated_token = token
|
||||||
|
|
|
@ -2,6 +2,7 @@ import logging
|
||||||
|
|
||||||
from flask.ext.principal import (identity_loaded, UserNeed, Permission,
|
from flask.ext.principal import (identity_loaded, UserNeed, Permission,
|
||||||
Identity, identity_changed)
|
Identity, identity_changed)
|
||||||
|
from flask.ext.login import current_user
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
|
@ -171,14 +172,15 @@ def on_identity_loaded(sender, identity):
|
||||||
# We have verified an identity, load in all of the permissions
|
# We have verified an identity, load in all of the permissions
|
||||||
|
|
||||||
if isinstance(identity, QuayDeferredPermissionUser):
|
if isinstance(identity, QuayDeferredPermissionUser):
|
||||||
logger.debug('Deferring permissions for user: %s' % identity.id)
|
logger.debug('Deferring permissions for user: %s', identity.id)
|
||||||
|
|
||||||
elif identity.auth_type == 'username':
|
elif identity.auth_type == 'username':
|
||||||
|
logger.debug('Switching username permission to deferred object: %s', identity.id)
|
||||||
switch_to_deferred = QuayDeferredPermissionUser(identity.id, 'username')
|
switch_to_deferred = QuayDeferredPermissionUser(identity.id, 'username')
|
||||||
identity_changed.send(app, identity=switch_to_deferred)
|
identity_changed.send(app, identity=switch_to_deferred)
|
||||||
|
|
||||||
elif identity.auth_type == 'token':
|
elif identity.auth_type == 'token':
|
||||||
logger.debug('Loading permissions for token: %s' % identity.id)
|
logger.debug('Loading permissions for token: %s', identity.id)
|
||||||
token_data = model.load_token_data(identity.id)
|
token_data = model.load_token_data(identity.id)
|
||||||
|
|
||||||
repo_grant = _RepositoryNeed(token_data.repository.namespace,
|
repo_grant = _RepositoryNeed(token_data.repository.namespace,
|
||||||
|
@ -188,4 +190,4 @@ def on_identity_loaded(sender, identity):
|
||||||
identity.provides.add(repo_grant)
|
identity.provides.add(repo_grant)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.error('Unknown identity auth type: %s' % identity.auth_type)
|
logger.error('Unknown identity auth type: %s', identity.auth_type)
|
||||||
|
|
|
@ -111,11 +111,8 @@ class User(ApiResource):
|
||||||
@nickname('getLoggedInUser')
|
@nickname('getLoggedInUser')
|
||||||
def get(self):
|
def get(self):
|
||||||
""" Get user information for the authenticated user. """
|
""" Get user information for the authenticated user. """
|
||||||
if get_authenticated_user() is None:
|
|
||||||
return {'anonymous': True}
|
|
||||||
|
|
||||||
user = get_authenticated_user()
|
user = get_authenticated_user()
|
||||||
if not user or user.organization:
|
if user is None or user.organization:
|
||||||
return {'anonymous': True}
|
return {'anonymous': True}
|
||||||
|
|
||||||
return user_view(user)
|
return user_view(user)
|
||||||
|
|
|
@ -12,7 +12,7 @@ from data import model
|
||||||
from data.queue import dockerfile_build_queue
|
from data.queue import dockerfile_build_queue
|
||||||
from app import app, login_manager
|
from app import app, login_manager
|
||||||
from auth.permissions import QuayDeferredPermissionUser
|
from auth.permissions import QuayDeferredPermissionUser
|
||||||
from api.discovery import swagger_route_data
|
from endpoints.api.discovery import swagger_route_data
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -35,9 +35,10 @@ def truthy_param(param):
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(username):
|
def load_user(username):
|
||||||
logger.debug('Loading user: %s' % username)
|
logger.debug('User loader loading deferred user: %s' % username)
|
||||||
return _LoginWrappedDBUser(username)
|
return _LoginWrappedDBUser(username)
|
||||||
|
|
||||||
|
|
||||||
class _LoginWrappedDBUser(UserMixin):
|
class _LoginWrappedDBUser(UserMixin):
|
||||||
def __init__(self, db_username, db_user=None):
|
def __init__(self, db_username, db_user=None):
|
||||||
|
|
||||||
|
|
Reference in a new issue