From 959016a6eb0057b0b9c189cdd8beeae8ff3ac3eb Mon Sep 17 00:00:00 2001 From: yackob03 Date: Tue, 15 Oct 2013 14:48:49 -0400 Subject: [PATCH] Remove unnecessary calls to the database for user and permission metadata. --- auth/auth.py | 6 ++++-- auth/permissions.py | 44 ++++++++++++++++++++++++++++++-------------- data/database.py | 6 ------ endpoints/api.py | 12 ++++++------ endpoints/web.py | 29 ++++++++++++++++++----------- 5 files changed, 58 insertions(+), 39 deletions(-) diff --git a/auth/auth.py b/auth/auth.py index 1e43c0492..21c28d420 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -7,6 +7,7 @@ from base64 import b64decode from data import model from app import app +from permissions import QuayDeferredPermissionUser from util.names import parse_namespace_repository @@ -40,8 +41,9 @@ def process_basic_auth(auth): ctx = _request_ctx_stack.top ctx.authenticated_user = authenticated - identity_changed.send(app, identity=Identity(authenticated.username, - 'username')) + new_identity = QuayDeferredPermissionUser(authenticated.username, + 'username') + identity_changed.send(app, identity=new_identity) return # We weren't able to authenticate via basic auth. diff --git a/auth/permissions.py b/auth/permissions.py index 1baa5d0dc..7ce41d7c9 100644 --- a/auth/permissions.py +++ b/auth/permissions.py @@ -1,13 +1,12 @@ import logging -from flask.ext.principal import identity_loaded, UserNeed, Permission -from flask.ext.login import current_user +from flask.ext.principal import (identity_loaded, UserNeed, Permission, + Identity, identity_changed) from collections import namedtuple from functools import partial from data import model from app import app -from auth import get_authenticated_user, get_validated_token logger = logging.getLogger(__name__) @@ -17,6 +16,29 @@ _ResourceNeed = namedtuple('resource', ['type', 'namespace', 'name', 'role']) _RepositoryNeed = partial(_ResourceNeed, 'repository') +class QuayDeferredPermissionUser(Identity): + def __init__(self, id, auth_type=None): + super(QuayDeferredPermissionUser, self).__init__(id, auth_type) + + self._permissions_loaded = False + + def can(self, permission): + if not self._permissions_loaded: + logger.debug('Loading user permissions after deferring.') + user_object = model.get_user(self.id) + + for user in model.get_all_user_permissions(user_object): + grant = _RepositoryNeed(user.repositorypermission.repository.namespace, + user.repositorypermission.repository.name, + user.repositorypermission.role.name) + logger.debug('User added permission: {0}'.format(grant)) + self.provides.add(grant) + + self._permissions_loaded = True + + return super(QuayDeferredPermissionUser, self).can(permission) + + class ModifyRepositoryPermission(Permission): def __init__(self, namespace, name): admin_need = _RepositoryNeed(namespace, name, 'admin') @@ -50,18 +72,12 @@ def on_identity_loaded(sender, identity): logger.debug('Identity loaded: %s' % identity) # We have verified an identity, load in all of the permissions - if identity.auth_type == 'username': - logger.debug('Computing permissions for user: %s' % identity.id) + if isinstance(identity, QuayDeferredPermissionUser): + logger.debug('Deferring permissions for user: %s' % identity.id) - user_object = model.get_user(identity.id) - - identity.provides.add(UserNeed(user_object.username)) - for user in model.get_all_user_permissions(user_object): - grant = _RepositoryNeed(user.repositorypermission.repository.namespace, - user.repositorypermission.repository.name, - user.repositorypermission.role.name) - logger.debug('User added permission: {0}'.format(grant)) - identity.provides.add(grant) + elif identity.auth_type == 'username': + switch_to_deferred = QuayDeferredPermissionUser(identity.id, 'username') + identity_changed.send(app, identity=switch_to_deferred) elif identity.auth_type == 'token': logger.debug('Computing permissions for token: %s' % identity.id) diff --git a/data/database.py b/data/database.py index d82ecbae1..8d2ad291f 100644 --- a/data/database.py +++ b/data/database.py @@ -14,18 +14,12 @@ db = app.config['DB_DRIVER'](app.config['DB_NAME'], **app.config['DB_CONNECTION_ARGS']) -def connect_db(): - logger.debug('Connectin to database.') - db.connect() - - def close_db(exc): if not db.is_closed(): logger.debug('Disconnecting from database.') db.close() -app.before_request(connect_db) app.teardown_request(close_db) diff --git a/endpoints/api.py b/endpoints/api.py index fadc7a54e..0a2ccf9a0 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -46,7 +46,7 @@ def get_logged_in_user(): if current_user.is_anonymous(): return jsonify({'anonymous': True}) - user = current_user.db_user + user = current_user.db_user() return jsonify({ 'verified': user.verified, 'anonymous': False, @@ -59,7 +59,7 @@ def get_logged_in_user(): @app.route('/api/user/', methods=['PUT']) @api_login_required def change_user_details(): - user = current_user.db_user + user = current_user.db_user() user_data = request.get_json(); @@ -184,7 +184,7 @@ def match_repos_api(): username = None if current_user.is_authenticated(): - username = current_user.db_user.username + username = current_user.db_user().username matching = model.get_matching_repositories(prefix, username) response = { @@ -219,7 +219,7 @@ def list_repos_api(): username = None if current_user.is_authenticated() and include_private: - username = current_user.db_user.username + username = current_user.db_user().username repo_query = model.get_visible_repositories(username, limit=limit, include_public=include_public, @@ -474,7 +474,7 @@ def subscribe(): request_data = request.get_json() plan = request_data['plan'] - user = current_user.db_user + user = current_user.db_user() private_repos = model.get_private_repo_count(user.username) if not user.stripe_id: @@ -518,7 +518,7 @@ def subscribe(): @app.route('/api/user/plan', methods=['GET']) @api_login_required def get_subscription(): - user = current_user.db_user + user = current_user.db_user() private_repos = model.get_private_repo_count(user.username) if user.stripe_id: diff --git a/endpoints/web.py b/endpoints/web.py index 604255f36..0259fcd08 100644 --- a/endpoints/web.py +++ b/endpoints/web.py @@ -8,30 +8,37 @@ from flask.ext.principal import identity_changed, Identity, AnonymousIdentity from data import model from app import app, login_manager, mixpanel +from auth.permissions import QuayDeferredPermissionUser logger = logging.getLogger(__name__) class _LoginWrappedDBUser(UserMixin): - def __init__(self, db_user): - self.db_user = db_user + def __init__(self, db_username): + + self._db_username = db_username + self._db_user = None + + def db_user(self): + if not self._db_user: + self._db_user = model.get_user(self._db_username) + return self._db_user + + def is_authenticated(self): + return self.db_user() is not None def is_active(self): - return self.db_user.verified + return self.db_user().verified def get_id(self): - return unicode(self.db_user.username) + return unicode(self._db_username) @login_manager.user_loader def load_user(username): logger.debug('Loading user: %s' % username) - db_user = model.get_user(username) - if db_user: - return _LoginWrappedDBUser(db_user) - else: - return None + return _LoginWrappedDBUser(username) @app.route('/', methods=['GET'], defaults={'path': ''}) @@ -78,8 +85,8 @@ def privacy(): def common_login(db_user): if login_user(_LoginWrappedDBUser(db_user)): logger.debug('Successfully signed in as: %s' % db_user.username) - identity_changed.send(app, - identity=Identity(db_user.username, 'username')) + new_identity = QuayDeferredPermissionUser(db_user.username, 'username') + identity_changed.send(app, identity=new_identity) return True else: logger.debug('User could not be logged in, inactive?.')