Merge pull request #1698 from coreos-inc/delete-namespace

Add support for deleting namespaces (users, organizations)
This commit is contained in:
josephschorr 2016-10-21 16:54:52 -04:00 committed by GitHub
commit edc2bc8b93
23 changed files with 407 additions and 33 deletions

View file

@ -9,7 +9,8 @@ from datetime import datetime, timedelta
from data.database import (User, LoginService, FederatedLogin, RepositoryPermission, TeamMember,
Team, Repository, TupleSelector, TeamRole, Namespace, Visibility,
EmailConfirmation, Role, db_for_update, random_string_generator,
UserRegion, ImageStorageLocation)
UserRegion, ImageStorageLocation, QueueItem, TeamMemberInvite,
ServiceKeyApproval, OAuthApplication, RepositoryBuildTrigger)
from data.model import (DataModelException, InvalidPasswordException, InvalidRobotException,
InvalidUsernameException, InvalidEmailAddressException,
TooManyLoginAttemptsException, db_transaction,
@ -647,11 +648,81 @@ def detach_external_login(user, service_name):
FederatedLogin.service == service).execute()
def delete_user(user):
def get_solely_admined_organizations(user_obj):
""" Returns the organizations admined solely by the given user. """
orgs = (User.select()
.where(User.organization == True)
.join(Team)
.join(TeamRole)
.where(TeamRole.name == 'admin')
.switch(Team)
.join(TeamMember)
.where(TeamMember.user == user_obj)
.distinct())
# Filter to organizations where the user is the sole admin.
solely_admined = []
for org in orgs:
admin_user_count = (TeamMember.select()
.join(Team)
.join(TeamRole)
.where(Team.organization == org, TeamRole.name == 'admin')
.switch(TeamMember)
.join(User)
.where(User.robot == False)
.distinct()
.count())
if admin_user_count == 1:
solely_admined.append(org)
return solely_admined
def delete_user(user, queues, force=False):
if not force and not user.organization:
# Ensure that the user is not the sole admin for any organizations. If so, then the user
# cannot be deleted before those organizations are deleted or reassigned.
organizations = get_solely_admined_organizations(user)
if len(organizations) > 0:
message = 'Cannot delete %s as you are the only admin for organizations: ' % user.username
for index, org in enumerate(organizations):
if index > 0:
message = message + ', '
message = message + org.username
raise DataModelException(message)
# Delete all queue items for the user.
for queue in queues:
queue.delete_namespaced_items(user.username)
# Delete any repositories under the user's namespace.
for repo in list(Repository.select().where(Repository.namespace_user == user)):
repository.purge_repository(user.username, repo.name)
if user.organization:
# Delete the organization's teams.
for team in Team.select().where(Team.organization == user):
team.delete_instance(recursive=True)
# Delete any OAuth approvals and tokens associated with the user.
for app in OAuthApplication.select().where(OAuthApplication.organization == user):
app.delete_instance(recursive=True)
else:
# Remove the user from any teams in which they are a member.
TeamMember.delete().where(TeamMember.user == user).execute()
# Delete any repository buildtriggers where the user is the connected user.
triggers = RepositoryBuildTrigger.select().where(RepositoryBuildTrigger.connected_user == user)
for trigger in triggers:
trigger.delete_instance(recursive=True, delete_nullable=False)
# Null out any service key approvals. We technically lose information here, but its better than
# falling and only occurs if a superuser is being deleted.
ServiceKeyApproval.update(approver=None).where(ServiceKeyApproval.approver == user).execute()
# Delete the user itself.
user.delete_instance(recursive=True, delete_nullable=True)