diff --git a/auth/auth.py b/auth/auth.py index a81876e54..ed0c8d82a 100644 --- a/auth/auth.py +++ b/auth/auth.py @@ -25,7 +25,7 @@ def _load_user_from_cookie(): if 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', {scopes.DIRECT_LOGIN}) + loaded = QuayDeferredPermissionUser(current_user.get_id(), 'user_db_id', {scopes.DIRECT_LOGIN}) identity_changed.send(app, identity=loaded) return current_user.db_user() return None @@ -58,12 +58,10 @@ def _validate_and_apply_oauth_token(token): set_authenticated_user(validated.authorized_user) set_validated_oauth_token(validated) - new_identity = QuayDeferredPermissionUser(validated.authorized_user.username, 'username', - scope_set) + new_identity = QuayDeferredPermissionUser(validated.authorized_user.id, 'user_db_id', scope_set) identity_changed.send(app, identity=new_identity) - def process_basic_auth(auth): normalized = [part.strip() for part in auth.split(' ') if part] if normalized[0].lower() != 'basic' or len(normalized) != 2: @@ -100,8 +98,7 @@ def process_basic_auth(auth): logger.debug('Successfully validated robot: %s' % credentials[0]) set_authenticated_user(robot) - deferred_robot = QuayDeferredPermissionUser(robot.username, 'username', - {scopes.DIRECT_LOGIN}) + deferred_robot = QuayDeferredPermissionUser(robot.id, 'user_db_id', {scopes.DIRECT_LOGIN}) identity_changed.send(app, identity=deferred_robot) return except model.InvalidRobotException: @@ -114,7 +111,7 @@ def process_basic_auth(auth): logger.debug('Successfully validated user: %s' % authenticated.username) set_authenticated_user(authenticated) - new_identity = QuayDeferredPermissionUser(authenticated.username, 'username', + new_identity = QuayDeferredPermissionUser(authenticated.id, 'user_db_id', {scopes.DIRECT_LOGIN}) identity_changed.send(app, identity=new_identity) return diff --git a/auth/auth_context.py b/auth/auth_context.py index b97ffa02d..6c587f901 100644 --- a/auth/auth_context.py +++ b/auth/auth_context.py @@ -10,13 +10,13 @@ logger = logging.getLogger(__name__) def get_authenticated_user(): 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.') + db_id = getattr(_request_ctx_stack.top, 'authenticated_db_id', None) + if not db_id: + logger.debug('No authenticated user or deferred database id.') return None logger.debug('Loading deferred authenticated user.') - loaded = model.get_user(username) + loaded = model.get_user_by_id(db_id) set_authenticated_user(loaded) user = loaded @@ -30,10 +30,10 @@ def set_authenticated_user(user_or_robot): 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) +def set_authenticated_user_deferred(user_or_robot_db_id): + logger.debug('Deferring loading of authenticated user object: %s', user_or_robot_db_id) ctx = _request_ctx_stack.top - ctx.authenticated_username = username_or_robotname + ctx.authenticated_db_id = user_or_robot_db_id def get_validated_oauth_token(): diff --git a/auth/permissions.py b/auth/permissions.py index 2b27f9583..59b190b3c 100644 --- a/auth/permissions.py +++ b/auth/permissions.py @@ -58,8 +58,8 @@ SCOPE_MAX_USER_ROLES.update({ class QuayDeferredPermissionUser(Identity): - def __init__(self, id, auth_type, scopes): - super(QuayDeferredPermissionUser, self).__init__(id, auth_type) + def __init__(self, db_id, auth_type, scopes): + super(QuayDeferredPermissionUser, self).__init__(db_id, auth_type) self._permissions_loaded = False self._scope_set = scopes @@ -88,7 +88,7 @@ class QuayDeferredPermissionUser(Identity): def can(self, permission): if not self._permissions_loaded: logger.debug('Loading user permissions after deferring.') - user_object = model.get_user(self.id) + user_object = model.get_user_by_id(self.id) # Add the superuser need, if applicable. if (user_object.username is not None and @@ -230,9 +230,9 @@ def on_identity_loaded(sender, identity): if isinstance(identity, QuayDeferredPermissionUser): logger.debug('Deferring permissions for user: %s', identity.id) - elif identity.auth_type == 'username': + elif identity.auth_type == 'user_db_id': logger.debug('Switching username permission to deferred object: %s', identity.id) - switch_to_deferred = QuayDeferredPermissionUser(identity.id, 'username', {scopes.DIRECT_LOGIN}) + switch_to_deferred = QuayDeferredPermissionUser(identity.id, 'user_db_id', {scopes.DIRECT_LOGIN}) identity_changed.send(app, identity=switch_to_deferred) elif identity.auth_type == 'token': diff --git a/data/database.py b/data/database.py index b94badd21..760ca7f17 100644 --- a/data/database.py +++ b/data/database.py @@ -169,6 +169,7 @@ class Visibility(BaseModel): class Repository(BaseModel): namespace = CharField() + namespace_user = ForeignKeyField(User, null=True) name = CharField() visibility = ForeignKeyField(Visibility) description = TextField(null=True) diff --git a/data/migrations/versions/13da56878560_migrate_registry_namespaces_to_.py b/data/migrations/versions/13da56878560_migrate_registry_namespaces_to_.py new file mode 100644 index 000000000..68b2e83c1 --- /dev/null +++ b/data/migrations/versions/13da56878560_migrate_registry_namespaces_to_.py @@ -0,0 +1,54 @@ +"""Migrate registry namespaces to reference a user. + +Revision ID: 13da56878560 +Revises: 51d04d0e7e6f +Create Date: 2014-09-18 13:56:45.130455 + +""" + +# revision identifiers, used by Alembic. +revision = '13da56878560' +down_revision = '51d04d0e7e6f' + +from alembic import op +import sqlalchemy as sa + +from data.database import Repository, User + +def upgrade(tables): + # Add the namespace_user column, allowing it to be nullable + op.add_column('repository', sa.Column('namespace_user', sa.Integer(), sa.ForeignKey('user.id'))) + + # backfill the namespace_user column + namespace_to_user = {} + for user in User.select(User.name, User.id).where(User.robot == False): + namespace_to_user[user.username] = user + + for repo in Repository.select(): + repo.namespace_user = namespace_to_user[repo.namespace] + repo.save() + + # Ensure the backfill was a success, then remove the namespace column + op.alter_column('repository', 'namespace_user', nullable=False) + op.create_index('repository_namespace_user_name', 'repository', ['namespace_user', 'name'], + unique=True) + + op.drop_index('repository_namespace_user', table_name='repository') + op.drop_column('repository', 'namespace') + + +def downgrade(tables): + # Add the namespace column, allowing it to be nullable + op.add_column('repository', sa.Column('namespace', sa.String(length=255))) + + # backfill the namespace column + for repo in Repository.select(Repository, User.username).join(User): + repo.namespace = repo.namespace_user.username + repo.save() + + # Ensure the backfill was a success, then remove the namespace column + op.alter_column('repository', 'namespace', nullable=False) + op.create_index('repository_namespace_name', 'repository', ['namespace', 'name'], unique=True) + + op.drop_index('repository_namespace_user', table_name='repository') + op.drop_column('repository', 'namespace_user') diff --git a/data/model/legacy.py b/data/model/legacy.py index 28f73dafe..a50a675d1 100644 --- a/data/model/legacy.py +++ b/data/model/legacy.py @@ -560,6 +560,13 @@ def get_user_or_org(username): return None +def get_user_by_id(user_dbid): + try: + return User.get(User.id == user_dbid, User.organization == False) + except User.DoesNotExist: + return None + + def get_user_or_org_by_customer_id(customer_id): try: return User.get(User.stripe_id == customer_id) diff --git a/endpoints/common.py b/endpoints/common.py index 52715a1d1..5479be6e0 100644 --- a/endpoints/common.py +++ b/endpoints/common.py @@ -82,20 +82,20 @@ def param_required(param_name): @login_manager.user_loader -def load_user(username): - logger.debug('User loader loading deferred user: %s' % username) - return _LoginWrappedDBUser(username) +def load_user(user_dbid): + logger.debug('User loader loading deferred user id: %s' % user_dbid) + return _LoginWrappedDBUser(user_dbid) class _LoginWrappedDBUser(UserMixin): - def __init__(self, db_username, db_user=None): + def __init__(self, user_dbid, db_user=None): - self._db_username = db_username + self._db_id = int(user_dbid) self._db_user = db_user def db_user(self): if not self._db_user: - self._db_user = model.get_user(self._db_username) + self._db_user = model.get_user_by_id(self._db_id) return self._db_user def is_authenticated(self): @@ -105,13 +105,13 @@ class _LoginWrappedDBUser(UserMixin): return self.db_user().verified def get_id(self): - return unicode(self._db_username) + return unicode(self._db_id) def common_login(db_user): - if login_user(_LoginWrappedDBUser(db_user.username, db_user)): + if login_user(_LoginWrappedDBUser(db_user.id, db_user)): logger.debug('Successfully signed in as: %s' % db_user.username) - new_identity = QuayDeferredPermissionUser(db_user.username, 'username', {scopes.DIRECT_LOGIN}) + new_identity = QuayDeferredPermissionUser(db_user.id, 'user_db_id', {scopes.DIRECT_LOGIN}) identity_changed.send(app, identity=new_identity) session['login_time'] = datetime.datetime.now() return True