bekkalokk/gitea/import-users: refactor + add members to groups

This commit is contained in:
Oystein Kristoffer Tveit 2024-08-26 21:01:40 +02:00
parent bb4662b345
commit e09d1cc3e1
Signed by: oysteikt
GPG Key ID: 9F2F7D8250F35146
1 changed files with 123 additions and 39 deletions

View File

@ -2,18 +2,89 @@ import requests
import secrets import secrets
import os import os
EMAIL_DOMAIN = os.getenv('EMAIL_DOMAIN') EMAIL_DOMAIN = os.getenv('EMAIL_DOMAIN')
if EMAIL_DOMAIN is None: if EMAIL_DOMAIN is None:
EMAIL_DOMAIN = 'pvv.ntnu.no' EMAIL_DOMAIN = 'pvv.ntnu.no'
API_TOKEN = os.getenv('API_TOKEN') API_TOKEN = os.getenv('API_TOKEN')
if API_TOKEN is None: if API_TOKEN is None:
raise Exception('API_TOKEN not set') raise Exception('API_TOKEN not set')
GITEA_API_URL = os.getenv('GITEA_API_URL') GITEA_API_URL = os.getenv('GITEA_API_URL')
if GITEA_API_URL is None: if GITEA_API_URL is None:
GITEA_API_URL = 'https://git.pvv.ntnu.no/api/v1' GITEA_API_URL = 'https://git.pvv.ntnu.no/api/v1'
def gitea_list_all_users() -> dict[str, dict[str, any]] | None:
r = requests.get(
GITEA_API_URL + '/admin/users',
headers={'Authorization': 'token ' + API_TOKEN}
)
if r.status_code != 200:
print('Failed to get users:', r.text)
return None
return {user['login'] : user for user in r.json()}
def gitea_create_user(username: str, userdata: dict[str,any]) -> bool:
r = requests.post(
GITEA_API_URL + '/admin/users',
json=userdata,
headers={'Authorization': 'token ' + API_TOKEN},
)
if r.status_code != 201:
print(f'ERR: Failed to create user {username}:', r.text)
return False
return True
def gitea_edit_user(username: str, userdata: dict[str,any]) -> bool:
r = requests.patch(
GITEA_API_URL + f'/admin/users/{username}',
json=userdata,
headers={'Authorization': 'token ' + API_TOKEN},
)
if r.status_code != 200:
print(f'ERR: Failed to update user {username}:', r.text)
return False
return True
def gitea_list_teams_for_organization(org: str) -> dict[str, any] | None:
r = requests.get(
GITEA_API_URL + f'/orgs/{org}/teams',
headers={'Authorization': 'token ' + API_TOKEN},
)
if r.status_code != 200:
print(f"ERR: Failed to list teams for {org}:", r.text)
return None
return {team['name'] : team for team in r.json()}
def gitea_add_user_to_organization_team(team_id: int, username: str) -> bool:
r = requests.put(
GITEA_API_URL + f'/teams/{team_id}/members/{username}',
headers={'Authorization': 'token ' + API_TOKEN},
)
if r.status_code != 204:
print(f'ERR: Failed to add user {username} to org team {team_id}:', r.text)
return False
return True
BANNED_SHELLS = [ BANNED_SHELLS = [
"/usr/bin/nologin", "/usr/bin/nologin",
"/usr/sbin/nologin", "/usr/sbin/nologin",
@ -22,12 +93,26 @@ BANNED_SHELLS = [
"/bin/msgsh", "/bin/msgsh",
] ]
existing_users = {}
# Reads out a passwd-file line for line, and filters out
# real PVV users (as opposed to system users meant for daemons and such)
def passwd_file_parser(passwd_path):
with open(passwd_path, 'r') as f:
for line in f.readlines():
uid = int(line.split(':')[2])
if uid < 1000:
continue
shell = line.split(':')[-1]
if shell in BANNED_SHELLS:
continue
username = line.split(':')[0]
name = line.split(':')[4].split(',')[0]
yield (username, name)
# This function should only ever be called when adding users def add_or_patch_gitea_user(username: str, name: str, existing_users: dict[str, dict[str, any]]) -> None:
# from the passwd file
def add_user(username, name):
user = { user = {
"full_name": name, "full_name": name,
"username": username, "username": username,
@ -41,53 +126,52 @@ def add_user(username, name):
user["visibility"] = "private" user["visibility"] = "private"
user["email"] = username + '@' + EMAIL_DOMAIN user["email"] = username + '@' + EMAIL_DOMAIN
r = requests.post(GITEA_API_URL + '/admin/users', json=user, if not gitea_create_user(username, user):
headers={'Authorization': 'token ' + API_TOKEN})
if r.status_code != 201:
print('ERR: Failed to create user ' + username + ': ' + r.text)
return return
print('Created user ' + username) print('Created user', username)
existing_users[username] = user existing_users[username] = user
else: else:
user["visibility"] = existing_users[username]["visibility"] user["visibility"] = existing_users[username]["visibility"]
r = requests.patch(GITEA_API_URL + f'/admin/users/{username}',
json=user, if not gitea_edit_user(username, user):
headers={'Authorization': 'token ' + API_TOKEN})
if r.status_code != 200:
print('ERR: Failed to update user ' + username + ': ' + r.text)
return return
print('Updated user ' + username) print('Updated user', username)
def ensure_gitea_user_is_part_of_team(username: str, org: str, team_name: str) -> None:
teams = gitea_list_teams_for_organization(org)
if teams is None:
return
if team_name not in teams:
print(f'ERR: could not find team "{team_name}" in organization "{org}"')
gitea_add_user_to_organization_team(username, teams[team_name].id)
print(f'User {username} is now part of {org}/{team_name}')
COMMON_USER_TEAMS = [
("Projects", "Members"),
("Kurs", "Members"),
]
def main(): def main():
# Fetch existing users existing_users = gitea_list_all_users()
r = requests.get(GITEA_API_URL + '/admin/users', if existing_users is None:
headers={'Authorization': 'token ' + API_TOKEN}) exit(1)
if r.status_code != 200: for username, name in passwd_file_parser("/tmp/passwd-import"):
raise Exception('Failed to get users: ' + r.text) print(f"Processing {username}")
add_or_patch_gitea_user(username, name, existing_users)
for user in r.json(): for org, team_name in COMMON_USER_TEAMS:
existing_users[user['login']] = user ensure_gitea_user_is_part_of_team(username, org, team_name)
print()
# Read the file, add each user
with open("/tmp/passwd-import", 'r') as f:
for line in f.readlines():
uid = int(line.split(':')[2])
if uid < 1000:
continue
shell = line.split(':')[-1]
if shell in BANNED_SHELLS:
continue
username = line.split(':')[0]
name = line.split(':')[4].split(',')[0]
add_user(username, name)
if __name__ == '__main__': if __name__ == '__main__':