def remove_organization_member(org, user_obj): org_admins = [u.username for u in __get_org_admin_users(org)] if len(org_admins) == 1 and user_obj.username in org_admins: raise DataModelException( "Cannot remove user as they are the only organization admin") with db_transaction(): # Find and remove the user from any repositories under the org. permissions = list( RepositoryPermission.select( RepositoryPermission.id).join(Repository).where( Repository.namespace_user == org, RepositoryPermission.user == user_obj)) if permissions: RepositoryPermission.delete().where( RepositoryPermission.id << permissions).execute() # Find and remove the user from any teams under the org. members = list( TeamMember.select(TeamMember.id).join(Team).where( Team.organization == org, TeamMember.user == user_obj)) if members: TeamMember.delete().where(TeamMember.id << members).execute()
def _delete_user_linked_data(user): if user.organization: # Delete the organization's teams. with db_transaction(): for team in Team.select().where(Team.organization == user): team.delete_instance(recursive=True) # Delete any OAuth approvals and tokens associated with the user. with db_transaction(): 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. with db_transaction(): triggers = RepositoryBuildTrigger.select().where(RepositoryBuildTrigger.connected_user == user) for trigger in triggers: trigger.delete_instance(recursive=True, delete_nullable=False) # Delete any mirrors with robots owned by this user. with db_transaction(): robots = list(list_namespace_robots(user.username)) RepoMirrorConfig.delete().where(RepoMirrorConfig.internal_robot << robots).execute() # Delete any robots owned by this user. with db_transaction(): robots = list(list_namespace_robots(user.username)) for robot in robots: robot.delete_instance(recursive=True, delete_nullable=True) # 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()
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 add_or_invite_to_team(inviter, team, user_obj=None, email=None, requires_invite=True): # If the user is a member of the organization, then we simply add the # user directly to the team. Otherwise, an invite is created for the user/email. # We return None if the user was directly added and the invite object if the user was invited. if user_obj and requires_invite: orgname = team.organization.username # If the user is part of the organization (or a robot), then no invite is required. if user_obj.robot: requires_invite = False if not user_obj.username.startswith(orgname + "+"): raise InvalidTeamMemberException( "Cannot add the specified robot to this team, " + "as it is not a member of the organization") else: query = (TeamMember.select().where( TeamMember.user == user_obj).join(Team).join(User).where( User.username == orgname, User.organization == True)) requires_invite = not any(query) # If we have a valid user and no invite is required, simply add the user to the team. if user_obj and not requires_invite: add_user_to_team(user_obj, team) return None email_address = email if not user_obj else None return TeamMemberInvite.create(user=user_obj, email=email_address, team=team, inviter=inviter)
def filter_to_repos_for_user(query, user_id=None, namespace=None, repo_kind="image", include_public=True, start_id=None): if not include_public and not user_id: return Repository.select().where(Repository.id == "-1") # Filter on the type of repository. if repo_kind is not None: try: query = query.where( Repository.kind == Repository.kind.get_id(repo_kind)) except RepositoryKind.DoesNotExist: raise DataModelException("Unknown repository kind") # Add the start ID if necessary. if start_id is not None: query = query.where(Repository.id >= start_id) # Add a namespace filter if necessary. if namespace: query = query.where(Namespace.username == namespace) # Build a set of queries that, when unioned together, return the full set of visible repositories # for the filters specified. queries = [] if include_public: queries.append( query.where(Repository.visibility == get_public_repo_visibility())) if user_id is not None: AdminTeam = Team.alias() AdminTeamMember = TeamMember.alias() # Add repositories in which the user has permission. queries.append( query.switch(RepositoryPermission).where( RepositoryPermission.user == user_id)) # Add repositories in which the user is a member of a team that has permission. queries.append( query.switch(RepositoryPermission).join(Team).join( TeamMember).where(TeamMember.user == user_id)) # Add repositories under namespaces in which the user is the org admin. queries.append( query.switch(Repository).join( AdminTeam, on=(Repository.namespace_user == AdminTeam.organization)).join( AdminTeamMember, on=(AdminTeam.id == AdminTeamMember.team)).where( AdminTeam.role == _lookup_team_role("admin")).where( AdminTeamMember.user == user_id)) return reduce(lambda l, r: l | r, queries)
def list_notifications(user, kind_name=None, id_filter=None, include_dismissed=False, page=None, limit=None): base_query = Notification.select( Notification.id, Notification.uuid, Notification.kind, Notification.metadata_json, Notification.dismissed, Notification.lookup_path, Notification.created, Notification.created.alias("cd"), Notification.target, ).join(NotificationKind) if kind_name is not None: base_query = base_query.where(NotificationKind.name == kind_name) if id_filter is not None: base_query = base_query.where(Notification.uuid == id_filter) if not include_dismissed: base_query = base_query.where(Notification.dismissed == False) # Lookup directly for the user. user_direct = base_query.clone().where(Notification.target == user) # Lookup via organizations admined by the user. Org = User.alias() AdminTeam = Team.alias() AdminTeamMember = TeamMember.alias() AdminUser = User.alias() via_orgs = (base_query.clone().join( Org, on=(Org.id == Notification.target)).join( AdminTeam, on=(Org.id == AdminTeam.organization)).join( TeamRole, on=(AdminTeam.role == TeamRole.id)).switch(AdminTeam).join( AdminTeamMember, on=(AdminTeam.id == AdminTeamMember.team)).join( AdminUser, on=(AdminTeamMember.user == AdminUser.id )).where((AdminUser.id == user) & (TeamRole.name == "admin"))) query = user_direct | via_orgs if page: query = query.paginate(page, limit) elif limit: query = query.limit(limit) return query.order_by(SQL("cd desc"))
def delete_members_not_present(team, member_id_set): """ Deletes all members of the given team that are not found in the member ID set. """ with db_transaction(): user_ids = set([u.id for u in list_team_users(team)]) to_delete = list(user_ids - member_id_set) if to_delete: query = TeamMember.delete().where(TeamMember.team == team, TeamMember.user << to_delete) return query.execute() return 0
def get_teams_within_org(organization, has_external_auth=False): """ Returns a AttrDict of team info (id, name, description), its role under the org, the number of repositories on which it has permission, and the number of members. """ query = Team.select().where( Team.organization == organization).join(TeamRole) def _team_view(team): return { "id": team.id, "name": team.name, "description": team.description, "role_name": Team.role.get_name(team.role_id), "repo_count": 0, "member_count": 0, "is_synced": False, } teams = {team.id: _team_view(team) for team in query} if not teams: # Just in case. Should ideally never happen. return [] # Add repository permissions count. permission_tuples = (RepositoryPermission.select( RepositoryPermission.team, fn.Count(RepositoryPermission.id)).where( RepositoryPermission.team << list(teams.keys())).group_by( RepositoryPermission.team).tuples()) for perm_tuple in permission_tuples: teams[perm_tuple[0]]["repo_count"] = perm_tuple[1] # Add the member count. members_tuples = (TeamMember.select(TeamMember.team, fn.Count( TeamMember.id)).where(TeamMember.team << list(teams.keys())).group_by( TeamMember.team).tuples()) for member_tuple in members_tuples: teams[member_tuple[0]]["member_count"] = member_tuple[1] # Add syncing information. if has_external_auth: sync_query = TeamSync.select( TeamSync.team).where(TeamSync.team << list(teams.keys())) for team_sync in sync_query: teams[team_sync.team_id]["is_synced"] = True return [AttrDict(team_info) for team_info in list(teams.values())]
def _user_teams(user, resource): changed = False p_exact_teams = resource["exact_teams"] p_add_teams = resource["add_teams"] p_remove_teams = resource["remove_teams"] team_names = p_exact_teams or p_add_teams or p_remove_teams if team_names is None: return False teams = [] for name in team_names: try: teams.append(Team.get(Team.name == name)) except model.InvalidTeamException: abort(400, message="Team '%s' does not exist" % name) teams = set(teams) current_teams = set(Team.select().join(TeamMember).join(User)).where( User.username == user.username) teams_to_add = teams - current_teams teams_to_remove = current_teams - teams if p_add_teams: teams_to_remove = [] elif p_remove_teams: teams_to_add = [] for team in teams_to_add: changed = True model.team.add_user_to_team(user, team) query = TeamMember.select().join(User).switch(TeamMember).join(Team).join( TeamRole) for team in teams_to_remove: changed = True found = list( query.where(User.username == user.username, Team.name == team.name)) found[0].delete_instance() return changed
def remove_user_from_team(org_name, team_name, username, removed_by_username): Org = User.alias() joined = TeamMember.select().join(User).switch(TeamMember).join(Team) with_role = joined.join(TeamRole) with_org = with_role.switch(Team).join(Org, on=(Org.id == Team.organization)) found = list( with_org.where(User.username == username, Org.username == org_name, Team.name == team_name)) if not found: raise DataModelException("User %s does not belong to team %s" % (username, team_name)) if username == removed_by_username: admin_team_query = __get_user_admin_teams(org_name, username) admin_team_names = {team.name for team in admin_team_query} if team_name in admin_team_names and len(admin_team_names) <= 1: msg = "User cannot remove themselves from their only admin team." raise DataModelException(msg) user_in_team = found[0] user_in_team.delete_instance()
def _is_team_member(team, user): return user.id in [ member.user_id for member in TeamMember.select().where(TeamMember.team == team) ]
def add_user_to_team(user_obj, team): try: return TeamMember.create(user=user_obj, team=team) except Exception: raise UserAlreadyInTeam("User %s is already a member of team %s" % (user_obj.username, team.name))
def list_organization_members_by_teams(organization): query = (TeamMember.select( Team, User).join(Team).switch(TeamMember).join(User).where( Team.organization == organization)) return query
def fix_ident(ident): return str(ident).translate(None, "-/.") with open("outfile.dot", "w") as outfile: outfile.write("digraph relationships {\n") for repo in Repository.select(): ns = fix_ident(repo.namespace_user.username) outfile.write("%s_%s -> %s\n" % (ns, fix_ident(repo.name), ns)) teams_in_orgs = set() for member in TeamMember.select(): if "+" in member.user.username: continue org_name = fix_ident(member.team.organization.username) team_to_org = (member.team.name, member.team.organization.username) if not team_to_org in teams_in_orgs: teams_in_orgs.add(team_to_org) outfile.write("%s_%s -> %s\n" % (org_name, fix_ident(member.team.name), org_name)) team_name = fix_ident(member.team.name) outfile.write("%s -> %s_%s\n" % (fix_ident(member.user.username), org_name, team_name))