aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--applications/gitlab/README.org103
-rw-r--r--applications/gitlab/approvals.py38
-rw-r--r--applications/gitlab/branch_protections.py25
-rw-r--r--applications/gitlab/gitlab_admins.py25
-rw-r--r--applications/gitlab/passwords.py29
-rw-r--r--applications/gitlab/provisioning.py32
-rw-r--r--applications/gitlab/users.py53
7 files changed, 278 insertions, 27 deletions
diff --git a/applications/gitlab/README.org b/applications/gitlab/README.org
index 81d610d..37ce59b 100644
--- a/applications/gitlab/README.org
+++ b/applications/gitlab/README.org
@@ -1,11 +1,110 @@
#+title: GitLab Scripts
-* =gitlab_admins.py=
+* =approvals.py=
+
+\*This script requires an active Premium or Ultimate subscription.*\
+
+#+begin_src sh
+python ./approvals.py
+#+end_src
+
+#+begin_src text
+Rule: All Members
+ Approvals Required: 1
+ Rule type: any_approver
+Rule: Default
+ Approvals Required: 1
+ Rule type: regular
+ Protected Branch: master
+ Eligible Approver: Christian Cleberg
+#+end_src
+
+* =branch_protections.py=
+
+#+begin_src sh
+python ./branch_protections.py
+#+end_src
+
+#+begin_src json
+[
+ {
+ "id": 148448212,
+ "name": "main",
+ "push_access_levels": [
+ {
+ "id": 185900194,
+ "access_level": 40,
+ "access_level_description": "Maintainers",
+ "deploy_key_id": null,
+ "user_id": null,
+ "group_id": null
+ }
+ ],
+ "merge_access_levels": [
+ {
+ "id": 156461000,
+ "access_level": 40,
+ "access_level_description": "Maintainers",
+ "user_id": null,
+ "group_id": null
+ }
+ ],
+ "allow_force_push": false,
+ "unprotect_access_levels": [],
+ "code_owner_approval_required": false,
+ "inherited": false
+ }
+]
+#+end_src
+
+* =passwords.py=
+
+\*This script does not apply to GitLab.com. This is for self-hosted instances only.*\
+
+#+begin_src sh
+python ./passwords.py
+#+end_src
+
+#+begin_src text
+# TODO: Need access to a self-hosted version of GitLab to test this out.
+#+end_src
+
+* =provisioning.py=
+
+\*This script requires an active Premium or Ultimate subscription.*\
#+begin_src sh
-python ./gitlab_admins.py
+python ./provisioning.py
#+end_src
#+begin_src text
+Group: 105300140
+ 2025-04-08T03:33:17.055Z : Action: member_created, Member: 128029250, Author: 24608590
+#+end_src
+
+* =users.py=
+
+#+begin_src sh
+python ./users.py
+#+end_src
+
+#+begin_src text
+Access Level Roles:
+ 0 : No access
+ 5 : Minimal access
+ 10 : Guest
+ 15 : Planner
+ 20 : Reporter
+ 30 : Developer
+ 40 : Maintainer
+ 50 : Owner
+ 60 : Admin
+
+
+Group 97083755 Members:
+Username: ccleberg, Access Level: 50
+
+Project 68701468 Members:
Username: ccleberg, Access Level: 50
+Username: project_68701468_bot_2c7ee010a479c0e48cdb4c7c5cfae886, Access Level: 40
#+end_src
diff --git a/applications/gitlab/approvals.py b/applications/gitlab/approvals.py
new file mode 100644
index 0000000..b6ea3ef
--- /dev/null
+++ b/applications/gitlab/approvals.py
@@ -0,0 +1,38 @@
+"""
+Extract merge request approval rules and their statuses in GitLab.
+"""
+
+import requests
+
+BASE_URL = "https://gitlab.com/api/v4"
+PRIVATE_TOKEN = "your_access_token"
+PROJECT_ID = "your_project_id"
+TIMEOUT = 30
+
+URL = f"{BASE_URL}/projects/{PROJECT_ID}/approval_rules"
+HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
+
+if __name__ == "__main__":
+ # Get approval rules
+ response = requests.get(URL, headers=HEADERS, timeout=TIMEOUT)
+ if response.status_code == 200:
+ approval_rules = response.json()
+ for rule in approval_rules:
+ name = rule["name"]
+ approvals_required = rule["approvals_required"]
+ rule_type = rule["rule_type"]
+ protected_branches = rule["protected_branches"]
+ eligible_approvers = rule["eligible_approvers"]
+ print(f"Rule: {name}")
+ print(f" Approvals Required: {approvals_required}")
+ print(f" Rule type: {rule_type}")
+ for branch in protected_branches:
+ branch_name = branch["name"]
+ print(f" Protected Branch: {branch_name}")
+ for approver in eligible_approvers:
+ approver_username = approver["name"]
+ print(f" Eligible Approver: {approver_username}")
+ else:
+ print(
+ f"Failed to fetch approval rules: {response.status_code}, {response.text}"
+ )
diff --git a/applications/gitlab/branch_protections.py b/applications/gitlab/branch_protections.py
new file mode 100644
index 0000000..3d83165
--- /dev/null
+++ b/applications/gitlab/branch_protections.py
@@ -0,0 +1,25 @@
+"""
+List all branch protection rules and their configurations in GitLab.
+"""
+
+import requests
+import json
+
+BASE_URL = "https://gitlab.com/api/v4"
+PRIVATE_TOKEN = "your_access_token"
+PROJECT_ID = "your_project_id"
+TIMEOUT = 30
+
+URL = f"{BASE_URL}/projects/{PROJECT_ID}/protected_branches"
+HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
+
+if __name__ == "__main__":
+ # Get protected branches
+ response = requests.get(URL, headers=HEADERS, timeout=TIMEOUT)
+ if response.status_code == 200:
+ protected_branches = response.json()
+ print(json.dumps(protected_branches, indent=4))
+ else:
+ print(
+ f"Failed to fetch protected branches: {response.status_code}, {response.text}"
+ )
diff --git a/applications/gitlab/gitlab_admins.py b/applications/gitlab/gitlab_admins.py
deleted file mode 100644
index 091032c..0000000
--- a/applications/gitlab/gitlab_admins.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""
-Gather all members of a GitLab group and their access levels.
-"""
-
-import requests
-
-BASE_URL = "https://gitlab.com/api/v4"
-PRIVATE_TOKEN = "your_access_token"
-GROUP_ID = "your_group_id"
-TIMEOUT = 30
-
-URL = f"{BASE_URL}/groups/{GROUP_ID}/members"
-HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
-
-if __name__ == "__main__":
- # Get group members
- response = requests.get(URL, headers=HEADERS, timeout=TIMEOUT)
- if response.status_code == 200:
- members = response.json()
- for member in members:
- print(
- f"Username: {member['username']}, Access Level: {member['access_level']}"
- )
- else:
- print(f"Failed to fetch group members: {response.status_code}, {response.text}")
diff --git a/applications/gitlab/passwords.py b/applications/gitlab/passwords.py
new file mode 100644
index 0000000..84ab107
--- /dev/null
+++ b/applications/gitlab/passwords.py
@@ -0,0 +1,29 @@
+"""
+Verify if password policies are enforced in a self-hosted GitLab instance.
+
+ Ref: https://docs.gitlab.com/api/settings/
+"""
+
+import requests
+
+BASE_URL = "https://gitlab.com/api/v4"
+PRIVATE_TOKEN = "your_access_token"
+TIMEOUT = 30
+
+URL = f"{BASE_URL}/application/settings"
+HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
+
+if __name__ == "__main__":
+ # Get application settings
+ response = requests.get(URL, headers=HEADERS, timeout=TIMEOUT)
+ if response.status_code == 200:
+ settings = response.json()
+ password_length = settings.get("password_length", "Not set")
+ password_complexity = settings.get("password_complexity", "Not set")
+
+ print(f"Password Length: {password_length}")
+ print(f"Password Complexity: {password_complexity}")
+ else:
+ print(
+ f"Failed to fetch application settings: {response.status_code}, {response.text}"
+ )
diff --git a/applications/gitlab/provisioning.py b/applications/gitlab/provisioning.py
new file mode 100644
index 0000000..bd0e695
--- /dev/null
+++ b/applications/gitlab/provisioning.py
@@ -0,0 +1,32 @@
+"""
+Track user creation and deletion events in GitLab with timestamps.
+"""
+
+import requests
+
+BASE_URL = "https://gitlab.com/api/v4"
+PRIVATE_TOKEN = "your_access_token"
+GROUP_ID = "your_group_id"
+TIMEOUT = 30
+
+URL = f"{BASE_URL}/groups/{GROUP_ID}/audit_events"
+HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
+
+if __name__ == "__main__":
+ # Get audit events
+ response = requests.get(URL, headers=HEADERS, timeout=TIMEOUT)
+ if response.status_code == 200:
+ audit_events = response.json()
+ for event in audit_events:
+ if event["entity_type"] == "User" or event["entity_type"] == "Group":
+ action = event["event_name"]
+ member_id = event["details"].get("member_id")
+ created_at = event["created_at"]
+ author = event["author_id"]
+ if action in ["member_created", "member_destroyed", "member_updated"]:
+ print(
+ f"Group: {GROUP_ID}\n",
+ f" {created_at} : Action: {action}, Member: {member_id}, Author: {author}",
+ )
+ else:
+ print(f"Failed to fetch audit events: {response.status_code}, {response.text}")
diff --git a/applications/gitlab/users.py b/applications/gitlab/users.py
new file mode 100644
index 0000000..fab2646
--- /dev/null
+++ b/applications/gitlab/users.py
@@ -0,0 +1,53 @@
+"""
+Gather all members of specified GitLab groups and projects and their access levels.
+
+ Ref: https://docs.gitlab.com/api/members/
+"""
+
+import requests
+
+BASE_URL = "https://gitlab.com/api/v4"
+PRIVATE_TOKEN = "your_access_token"
+GROUP_IDS = ["group_id_1", "group_id_2"] # Add your group IDs here
+PROJECT_IDS = ["project_id_1", "project_id_2"] # Add your project IDs here
+TIMEOUT = 30
+
+HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
+
+
+def get_members(url, name):
+ response = requests.get(url, headers=HEADERS, timeout=TIMEOUT)
+ if response.status_code == 200:
+ members = response.json()
+ print(f"\n{name} Members:")
+ for member in members:
+ print(
+ f"Username: {member['username']}, Access Level: {member['access_level']}"
+ )
+ else:
+ print(
+ f"Failed to fetch members for {name}: {response.status_code}, {response.text}"
+ )
+
+
+if __name__ == "__main__":
+ access_levels = """Access Level Roles:
+ 0 : No access
+ 5 : Minimal access
+ 10 : Guest
+ 15 : Planner
+ 20 : Reporter
+ 30 : Developer
+ 40 : Maintainer
+ 50 : Owner
+ 60 : Admin
+ """
+ print(access_levels)
+
+ for group_id in GROUP_IDS:
+ group_url = f"{BASE_URL}/groups/{group_id}/members"
+ get_members(group_url, f"Group {group_id}")
+
+ for project_id in PROJECT_IDS:
+ project_url = f"{BASE_URL}/projects/{project_id}/members"
+ get_members(project_url, f"Project {project_id}")