[inactive-gitlab-users] Import the main script from sysadmin-bin
- From: Bartłomiej Piotrowski <bpiotrowski src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [inactive-gitlab-users] Import the main script from sysadmin-bin
- Date: Wed, 8 Dec 2021 20:42:30 +0000 (UTC)
commit 16bcad259ec77699b299ef487b4ae7d75e0179e7
Author: Bartłomiej Piotrowski <bpiotrowski gnome org>
Date: Wed Dec 8 21:29:51 2021 +0100
Import the main script from sysadmin-bin
Dockerfile | 6 +-
inactive-gitlab-users.py | 292 +++++++++++++++++++++++++++++++++++++++++++++++
inactive-gitlab-users.sh | 11 +-
3 files changed, 299 insertions(+), 10 deletions(-)
---
diff --git a/Dockerfile b/Dockerfile
index 158e794..1712cd4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,9 +3,7 @@ FROM python:3
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
-RUN mkdir /usr/local/sysadmin-bin && \
- git clone https://gitlab.gnome.org/Infrastructure/sysadmin-bin.git /usr/local/sysadmin-bin
-
-COPY inactive-gitlab-users.sh /usr/local/bin
+ADD inactive-gitlab-users.py /usr/local/bin/inactive-gitlab-users.py
+ADD inactive-gitlab-users.sh /usr/local/bin/inactive-gitlab-users.sh
ENTRYPOINT ["/usr/local/bin/inactive-gitlab-users.sh"]
diff --git a/inactive-gitlab-users.py b/inactive-gitlab-users.py
new file mode 100644
index 0000000..f2e005c
--- /dev/null
+++ b/inactive-gitlab-users.py
@@ -0,0 +1,292 @@
+#!/usr/bin/python
+
+from __future__ import print_function
+
+import argparse
+import datetime
+import json
+import os
+import re
+
+import gitlab
+import pytz
+from dateutil.parser import parse as dateparser
+from dateutil.relativedelta import relativedelta
+
+
+def timestamp2date(timestamp):
+ if timestamp:
+ return timestamp.split("T")[0]
+ else:
+ return None
+
+
+def is_user_trusted(user):
+ trusted_domains = [
+ "archlinux.org",
+ "canonical.com",
+ "debian.org",
+ "endlessm.com",
+ "fedoraproject.org",
+ "gentoo.org",
+ "gitlab.gnome.org",
+ "gnome.org",
+ "igalia.com",
+ "kde.org",
+ "opensuse.org",
+ "redhat.com",
+ "suse.com",
+ "ubuntu.com",
+ ]
+
+ attrs = user.attributes
+ identities = [identity["provider"] for identity in attrs["identities"]]
+
+ if "ldapmain" in identities:
+ user.customattributes.set("trusted", "true")
+ return True
+
+ if attrs["email"].split("@")[1] in trusted_domains:
+ user.customattributes.set("trusted", "true")
+ return True
+
+ if attrs["two_factor_enabled"]:
+ user.customattributes.set("trusted", "true")
+ return True
+
+ if user.keys.list() or user.gpgkeys.list():
+ user.customattributes.set("trusted", "true")
+ return True
+
+ if len(user.identities) >= 2:
+ user.customattributes.set("trusted", "true")
+ return True
+
+ events = user.events.list(all=True)
+ target_types = ('DiffNote', 'MergeRequest', 'DiscussionNote', 'MergeRequest', 'Note', 'Issue')
+ for event in events:
+ if event.action_name == 'pushed to' or event.target_type in target_types:
+ project_id = event.project_id
+ project = gl.projects.get(project_id).path_with_namespace
+ if project.split('/')[0] in ('GNOME', 'Infrastructure', 'Teams'):
+ user.customattributes.set("trusted", "true")
+ return True
+
+ return False
+
+
+def get_inactive_users(gl, timedelta_unit, timedelta_value):
+ fields = [
+ "username",
+ "email",
+ "id",
+ "bio",
+ "website_url",
+ "created_at",
+ "current_sign_in_at",
+ "last_activity_on",
+ ]
+
+ trusted_users = gl.users.list(custom_attributes={"trusted": "true"}, all=True)
+ users = gl.users.list(as_list=False, order_by="created_at", sort="asc")
+ results = []
+
+ relativedelta_kwargs = {timedelta_unit: int(timedelta_value)}
+ timedelta = datetime.datetime.now(pytz.utc) - relativedelta(**relativedelta_kwargs)
+
+ for user in users:
+ attrs = user.attributes
+ userdata = {field: str(attrs[field]) for field in fields}
+
+ if user in trusted_users:
+ continue
+
+ if is_user_trusted(user):
+ continue
+
+ # Skip user if registered within timedelta
+ if dateparser(attrs["created_at"]) > timedelta:
+ continue
+
+ created_at = timestamp2date(attrs["created_at"])
+ current_sign_in_at = timestamp2date(attrs["current_sign_in_at"])
+ last_activity_on = timestamp2date(attrs["last_activity_on"])
+
+ # If user logged in only once or never, check if they made any action.
+ if (created_at == current_sign_in_at == last_activity_on) or not (
+ current_sign_in_at or last_activity_on
+ ):
+ events = user.events.list(all=True, lazy=True)
+ if len(events) == 0:
+ userdata["reason"] = "inactivity"
+ results.append(userdata)
+ continue
+ else:
+ user_projects = user.projects.list(all=True)
+ if len(user_projects) == 1:
+ userproj = user_projects[0]
+ if not userproj.attributes.get("forked_from_project"):
+ proj = gl.projects.get(userproj.id)
+ if len(proj.commits.list()) <= 1:
+ userdata["reason"] = "inactivity"
+ results.append(userdata)
+ continue
+ if len(user_projects) > 1:
+ continue
+
+ return results
+
+
+def delete_snippets_only_users(gl):
+ trusted_users = gl.users.list(custom_attributes={"trusted": "true"}, all=True)
+ snippets = gl.snippets.public(per_page=100)
+
+ snippets_users = {x.attributes["author"]["id"] for x in snippets}
+ for user in snippets_users:
+ user = gl.users.get(user)
+ if user in trusted_users:
+ continue
+
+ if is_user_trusted(user):
+ continue
+
+ events = user.events.list(all=True, lazy=True)
+ if len(events) == 0:
+ delete_user(gl, user.id, "snippets")
+
+
+def trust_user(gl, user_id):
+ user = gl.users.get(user_id, lazy=True)
+ user.customattributes.set("trusted", "true")
+ print(user_id)
+
+
+def untrust_user(gl, user_id):
+ user = gl.users.get(user_id, lazy=True)
+ user.customattributes.delete("trusted")
+ print(user_id)
+
+
+def delete_user(gl, user_id, reason=""):
+ try:
+ user = gl.users.get(user_id)
+ print(f"{user.username},{user.email},{reason}")
+ gl.users.delete(user_id)
+ except gitlab.exceptions.GitlabDeleteError:
+ pass
+
+
+def block_user(gl, user_id):
+ try:
+ user = gl.users.get(user_id)
+ print(f"{user.username},{user.email},spam")
+ user.block()
+ except gitlab.exceptions.GitlabBlockError:
+ pass
+
+
+def trust_all_groups(gl):
+ groups = gl.groups.list(all=True, visibility="public")
+ parent_groups = [grp for grp in groups if not grp.attributes["parent_id"]]
+ members = set()
+
+ for group in parent_groups:
+ group_members = group.members.all(all=True)
+ members.update(group_members)
+
+ for user in members:
+ trust_user(gl, user.id)
+
+
+def trust_2fa_users(gl):
+ users = gl.users.list(all=True, two_factor="enabled", as_list=False)
+
+ for user in users:
+ trust_user(gl, user.id)
+
+
+if __name__ == "__main__":
+ GITLAB_TOKEN = os.getenv("GITLAB_TOKEN")
+ if GITLAB_TOKEN is None:
+ with open("/home/admin/secret/gitlab_rw") as f:
+ tokenfile = f.readline()
+ GITLAB_TOKEN = tokenfile.rstrip().split("=")[1]
+
+ gl = gitlab.Gitlab(
+ "https://gitlab.gnome.org", private_token=GITLAB_TOKEN, per_page=100
+ )
+ gl.auth()
+
+ parser = argparse.ArgumentParser()
+ subparsers = parser.add_subparsers(dest="command")
+
+ trust_groups = subparsers.add_parser(
+ "trust-groups", help="mark users which are member of any group as trusted"
+ )
+ trust_2fa = subparsers.add_parser(
+ "trust-2fa",
+ help="mark users which have two factor authenticated enabled as trusted",
+ )
+
+ inactive = subparsers.add_parser("get-inactive", help="get inactive users")
+ inactive.add_argument(
+ "-t",
+ "--timedelta",
+ default="months:1",
+ help="grace period for users to become active (unit:value)",
+ )
+
+ trust = subparsers.add_parser("trust", help="mark users as trusted")
+ trust.add_argument("user_id", nargs="+", help="user IDs to mark as trusted")
+
+ untrust = subparsers.add_parser("untrust", help="mark users as untrusted")
+ untrust.add_argument("user_id", nargs="+", help="user IDs to mark as untrusted")
+
+ delete = subparsers.add_parser("delete", help="delete users")
+ delete.add_argument("user_id", nargs="+", help="user IDs to delete")
+
+ delete_from_file = subparsers.add_parser(
+ "delete-from-json",
+ help="delete users from json file generated with get-inactive",
+ )
+ delete_from_file.add_argument("filename", help="path to json file")
+
+ snippets = subparsers.add_parser(
+ "snippets",
+ help="remove users who have no activity other than posting snippets"
+ )
+
+ args = parser.parse_args()
+
+ if args.command == "get-inactive":
+ unit, value = args.timedelta.split(":")
+ inactive = get_inactive_users(gl, unit, value)
+ with open("/tmp/inactive.json", "w") as f:
+ json.dump(inactive, f)
+ elif args.command == "trust":
+ for id in args.user_id:
+ trust_user(gl, id)
+ elif args.command == "untrust":
+ for id in args.user_id:
+ untrust_user(gl, id)
+ elif args.command == "delete":
+ for id in args.user_id:
+ delete_user(gl, id)
+ elif args.command == "delete-from-json":
+ with open(args.filename, "r") as f:
+ users = json.load(f)
+
+ for user in users:
+ if user["reason"] == "spam":
+ block_user(gl, user["id"])
+ continue
+
+ delete_user(gl, user["id"], user["reason"])
+ elif args.command == "trust-groups":
+ trust_all_groups(gl)
+ elif args.command == "trust-2fa":
+ trust_2fa_users(gl)
+ elif args.command == "snippets":
+ delete_snippets_only_users(gl)
+ else:
+ parser.print_help()
diff --git a/inactive-gitlab-users.sh b/inactive-gitlab-users.sh
index 934ad60..2bd3c2c 100755
--- a/inactive-gitlab-users.sh
+++ b/inactive-gitlab-users.sh
@@ -4,9 +4,8 @@ set -e
echo "remove after ${TIMEDELTA_VALUE} ${TIMEDELTA_UNIT}"
echo
-cd /usr/local/sysadmin-bin/gitlab
-python3 ./inactive-gitlab-users.py trust-groups >/dev/null
-python3 ./inactive-gitlab-users.py trust-2fa >/dev/null
-python3 ./inactive-gitlab-users.py snippets
-python3 ./inactive-gitlab-users.py get-inactive -t ${TIMEDELTA_UNIT}:${TIMEDELTA_VALUE} > /tmp/inactive.json
-python3 ./inactive-gitlab-users.py delete-from-json /tmp/inactive.json
+python3 /usr/local/bin/inactive-gitlab-users.py trust-groups >/dev/null
+python3 /usr/local/bin/inactive-gitlab-users.py trust-2fa >/dev/null
+python3 /usr/local/bin/inactive-gitlab-users.py snippets
+python3 /usr/local/bin/inactive-gitlab-users.py get-inactive -t ${TIMEDELTA_UNIT}:${TIMEDELTA_VALUE} >
/tmp/inactive.json
+python3 /usr/local/bin/inactive-gitlab-users.py delete-from-json /tmp/inactive.json
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]