Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F9340979
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Subscribers
None
View Options
diff --git a/gitlab/manage_users_groups.py b/gitlab/manage_users_groups.py
new file mode 100755
index 0000000..a6f5263
--- /dev/null
+++ b/gitlab/manage_users_groups.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+
+import logging
+
+import click
+import yaml
+
+import gitlab
+
+logger = logging.getLogger(__name__)
+
+
+@click.command()
+@click.option(
+ "--gitlab",
+ "-g",
+ "gitlab_instance",
+ help="Which GitLab instance to use, as configured in the python-gitlab config",
+)
+@click.option(
+ "--do-it",
+ "do_it",
+ is_flag=True,
+ help="Actually perform the operations",
+)
+@click.argument("config_file")
+def cli(gitlab_instance, do_it, config_file):
+ """Ensure that GitLab users and group memberships match the structure defined in the
+ configuration file. This uses the python-gitlab configuration parsing."""
+ gl = gitlab.Gitlab.from_config(gitlab_instance)
+ gl.auth()
+
+ with open(config_file, "r") as f:
+ config = yaml.safe_load(f)
+
+ if not do_it:
+ logger.info(
+ "Will not perform any actions, please use --do-it once you're satisfied"
+ " with the expected actions."
+ )
+
+ for group_path, group_conf in config["groups"].items():
+ group = gl.groups.get(group_path, with_projects=False)
+ expected_members = {
+ username: gitlab.const.AccessLevel[access_level.upper()]
+ for username, access_level in group_conf["users"].items()
+ }
+ recorded_members = set()
+
+ expected_group_shares = {
+ other_path: gitlab.const.AccessLevel[access_level.upper()]
+ for other_path, access_level in group_conf.get(
+ "share_with_groups", {}
+ ).items()
+ }
+ recorded_members = set()
+
+ remove_extra_memberships = group_conf.get("remove_extra_memberships", False)
+ for member in group.members.list():
+ username = member.username
+ expected_access_level = expected_members.get(member.username)
+ if expected_access_level and member.access_level != expected_access_level:
+ logger.info(
+ "Adjusting membership for %s in %s to %s (was %s)",
+ username,
+ group_path,
+ expected_access_level.name,
+ member.access_level,
+ )
+ if do_it:
+ member.access_level = expected_access_level
+ member.save()
+
+ if remove_extra_memberships and not expected_access_level:
+ logger.info("Removing member %s from %s", username, group_path)
+ if do_it:
+ member.delete()
+
+ recorded_members.add(username)
+
+ for username, access_level in expected_members.items():
+ if username in recorded_members:
+ continue
+
+ users = gl.users.list(username=username)
+ if not users:
+ logger.warning(
+ "User %s not found, cannot add them to %s!", username, group_path
+ )
+ continue
+
+ user_id = users[0].id
+
+ logger.info("Adding member %s in %s at level %s", username, access_level)
+ if do_it:
+ group.members.create({"user_id": user_id, "access_level": access_level})
+
+ recorded_group_shares = set()
+
+ for group_share in group.shared_with_groups:
+ other_path = group_share["group_full_path"]
+ other_id = group_share["group_id"]
+ other_access_level = group_share["group_access_level"]
+ expected_access_level = expected_group_shares.get(other_path)
+ if expected_access_level and other_access_level != expected_access_level:
+ logger.info(
+ "Adjusting group_share for %s in %s to %s (was %s)",
+ other_path,
+ group_path,
+ expected_access_level,
+ other_access_level,
+ )
+ if do_it:
+ group.share(other_id, expected_access_level)
+
+ if remove_extra_memberships and not expected_access_level:
+ logger.info("Removing group %s from %s", other_path, group_path)
+ if do_it:
+ group.unshare(other_id)
+
+ recorded_group_shares.add(other_path)
+
+ for other_path, access_level in expected_group_shares.items():
+ if other_path in recorded_group_shares:
+ continue
+
+ other_group = gl.groups.get(other_path)
+ if not other_group:
+ logger.warning(
+ "Group %s not found, cannot add them to %s!", other_path, group_path
+ )
+ continue
+
+ logger.info(
+ "Adding group %s in %s at level %s",
+ other_path,
+ group_path,
+ access_level,
+ )
+ if do_it:
+ group.share(other_group.id, access_level)
+
+
+if __name__ == "__main__":
+ logging.basicConfig(
+ level=logging.INFO, format="%(asctime)s %(name)s:%(levelname)s %(message)s"
+ )
+ cli()
diff --git a/gitlab/users_groups.yml b/gitlab/users_groups.yml
new file mode 100644
index 0000000..a7a32ce
--- /dev/null
+++ b/gitlab/users_groups.yml
@@ -0,0 +1,80 @@
+# Owner
+# Maintainer
+# Developer
+# Reporter
+# Guest
+
+groups:
+ teams/staff:
+ users:
+ phabricator-migration: Owner
+ anlambert: Maintainer
+ ardumont: Maintainer
+ bchauvet: Owner
+ douardda: Maintainer
+ jayeshv: Maintainer
+ lunar: Maintainer
+ marla.dasilva: Maintainer
+ moranegg: Maintainer
+ olasd: Maintainer
+ rdicosmo: Owner
+ sgranger: Maintainer
+ vlorentz: Maintainer
+ vsellier: Maintainer
+ zack: Owner
+ remove_extra_memberships: true
+ teams/developers:
+ users:
+ phabricator-migration: Owner
+ anlambert: Maintainer
+ ardumont: Maintainer
+ bchauvet: Maintainer
+ douardda: Maintainer
+ jayeshv: Maintainer
+ lunar: Maintainer
+ moranegg: Maintainer
+ olasd: Maintainer
+ rdicosmo: Maintainer
+ vlorentz: Maintainer
+ vsellier: Maintainer
+ zack: Maintainer
+ remove_extra_memberships: true
+ teams/sysadmin:
+ users:
+ phabricator-migration: Owner
+ ardumont: Owner
+ olasd: Owner
+ vsellier: Owner
+ remove_extra_memberships: true
+ teams/interns:
+ users:
+ phabricator-migration: Owner
+ remove_extra_memberships: true
+ teams/management:
+ users:
+ phabricator-migration: Owner
+ bchauvet: Owner
+ douardda: Maintainer
+ rdicosmo: Owner
+ vsellier: Maintainer
+ zack: Owner
+ remove_extra_memberships: true
+ infra:
+ users:
+ phabricator-migration: Owner
+ remove_extra_memberships: true
+ share_with_groups:
+ teams/sysadmin: Owner
+ modules:
+ users:
+ phabricator-migration: Owner
+ share_with_groups:
+ teams/developers: Maintainer
+ remove_extra_memberships: true
+ migrated:
+ users:
+ phabricator-migration: Owner
+ share_with_groups:
+ teams/sysadmin: Owner
+ teams/developers: Maintainer
+ remove_extra_memberships: true
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Jul 4, 11:27 AM (3 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3300904
Attached To
rDSNIP Code snippets
Event Timeline
Log In to Comment