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:
jakedt 2014-03-17 12:01:13 -04:00
parent e759066ae0
commit 5bb4008880
6 changed files with 65 additions and 26 deletions

2
app.py
View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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):