Initial changes to move repositories from using a namespace string to referencing a user object. Also stores the user id in the cookie rather than the username, to allow users to be renamed. This commit must not be used unmodified because the database migration is too aggressive for live migration.
This commit is contained in:
parent
8c00eabedd
commit
8626d1cd70
7 changed files with 87 additions and 28 deletions
11
auth/auth.py
11
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
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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':
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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')
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Reference in a new issue