diff --git a/mastodon-kanidm-sync.py b/mastodon-kanidm-sync.py index 784dea8..ebf9e03 100644 --- a/mastodon-kanidm-sync.py +++ b/mastodon-kanidm-sync.py @@ -14,129 +14,134 @@ def getenv(name): print(f"[ERROR] Missing environment variable {name}. You should NOT run this script by hand, please use systemd mastodon-kanidm-sync.service.") exit(1) - # Import configuration KANIDM_URL = getenv("KANIDM_URL") KANIDM_TOKEN = read_file(getenv("KANIDM_TOKEN_PATH")).strip() OWNER_USERNAME = getenv("OWNER_USERNAME") +SLEEP_TIME = int(getenv("SLEEP_TIME")) -# Fetch kanidm users list from userdata file -# Userdata file is json list with information about what users are configured by kanidm -try: - USERDATA = read_file(getenv("USERDATA_FILE_PATH")).strip() - userdata = json.loads(USERDATA) - print("[INFO] ") -except FileNotFoundError: - userdata = [] +def sync_mastodon(): + # Fetch kanidm users list from userdata file + # Userdata file is json list with information about what users are configured by kanidm + try: + USERDATA = read_file(getenv("USERDATA_FILE_PATH")).strip() + userdata = json.loads(USERDATA) + print("[INFO] ") + except FileNotFoundError: + userdata = [] -# Load database -conn = ps.connect( - dbname=getenv("POSTGRES_DBNAME"), - user=getenv("POSTGRES_USER"), - host=getenv("POSTGRES_HOST") -) + # Load database + conn = ps.connect( + dbname=getenv("POSTGRES_DBNAME"), + user=getenv("POSTGRES_USER"), + host=getenv("POSTGRES_HOST") + ) -# Fetch current userdata from database -cur = conn.cursor() -cur.execute(''' - SELECT identities.uid, users.id, user_roles.name - FROM users - JOIN identities - ON users.id = identities.id - LEFT JOIN user_roles - ON users.role_id = user_roles.id; - ''' -) + # Fetch current userdata from database + cur = conn.cursor() + cur.execute(''' + SELECT identities.uid, users.id, user_roles.name + FROM users + JOIN identities + ON users.id = identities.id + LEFT JOIN user_roles + ON users.role_id = user_roles.id; + ''' + ) -state = cur.fetchall() + state = cur.fetchall() -users = {} -for i in state: - users[i[0]] = { - "id": i[1], - "role": i[2], - "isKanidmUser": False - } + users = {} + for i in state: + users[i[0]] = { + "id": i[1], + "role": i[2], + "isKanidmUser": False + } -# Fetch Kanidm userdata -kanidm_users_raw = requests.get( - f"{KANIDM_URL}/v1/person", - headers={ - "Authorization": f"Bearer {KANIDM_TOKEN}", - "Content-Type": "application/json", - }, - timeout=5, -).json() + # Fetch Kanidm userdata + kanidm_users_raw = requests.get( + f"{KANIDM_URL}/v1/person", + headers={ + "Authorization": f"Bearer {KANIDM_TOKEN}", + "Content-Type": "application/json", + }, + timeout=5, + ).json() -def give_role(uid, role, putUserdata = True): - if (uid not in userdata) and (putUserdata): - userdata.append(uid) - users[uid]["isKanidmUser"] = True - users[uid]["role"] = role - print(f"[INFO] {uid} is marked as {role}") - + def give_role(uid, role, putUserdata = True): + if (uid not in userdata) and (putUserdata): + userdata.append(uid) + users[uid]["isKanidmUser"] = True + users[uid]["role"] = role + print(f"[INFO] {uid} is marked as {role}") + -for i in kanidm_users_raw: - i = i["attrs"] - for uid in i["name"]: # [user].attrs.name is a list - if uid in users: # Don't apply anything for users who have no mastodon access (sp.mastodon.users) or didn't register - if uid == OWNER_USERNAME: - give_role(uid, "Owner", False) + for i in kanidm_users_raw: + i = i["attrs"] + for uid in i["name"]: # [user].attrs.name is a list + if uid in users: # Don't apply anything for users who have no mastodon access (sp.mastodon.users) or didn't register + if uid == OWNER_USERNAME: + give_role(uid, "Owner", False) + continue + + for group in i["memberof"]: + if group.startswith("sp.mastodon.admins@") or group.startswith("sp.admins@"): + give_role(uid, "Admin") + break + + elif group.startswith("sp.mastodon.moderators@"): + give_role(uid, "Moderator") + break + + elif uid in userdata: + # If user, who previously had a role, has no roles set by Kanidm, delete them from userdata list so allow setting roles directly by mastodon + give_role(uid, None, False) + userdata.remove(uid) + + print("[DEBUG]", users) + + # Fetch RoleIDs + cur = conn.cursor() + cur.execute("SELECT id, name FROM user_roles;") + + roles_raw = cur.fetchall() + roles = {} + for i in roles_raw: + roles[i[1]] = i[0] + + # Give roles + for uid in users: + if not users[uid]["isKanidmUser"]: continue + + if users[uid]["role"]: + rolename = users[uid]["role"] + roleid = roles[rolename] + else: + roleid = "NULL" - for group in i["memberof"]: - if group.startswith("sp.mastodon.admins@") or group.startswith("sp.admins@"): - give_role(uid, "Admin") - break + sqlcommand = f"UPDATE users SET role_id = {roleid} WHERE id = {users[uid]["id"]};" + print("[DEBUG] SQL:", sqlcommand) + cur.execute(sqlcommand) - elif group.startswith("sp.mastodon.moderators@"): - give_role(uid, "Moderator") - break + conn.commit() + cur.close() + conn.close() - elif uid in userdata: - # If user, who previously had a role, has no roles set by Kanidm, delete them from userdata list so allow setting roles directly by mastodon - give_role(uid, None, False) - userdata.remove(uid) + print("[INFO] Final userdata.json file content: ", userdata) -print("[DEBUG]", users) + def write_userdata(mode): + with open(getenv("USERDATA_FILE_PATH"), mode) as f: + f.write(json.dumps(userdata)) + f.close() -# Fetch RoleIDs -cur = conn.cursor() -cur.execute("SELECT id, name FROM user_roles;") + try: + write_userdata("w") + except FileNotFoundError: + print("[INFO] userdata.json file doesn't exist. Creating it") + write_userdata("x") -roles_raw = cur.fetchall() -roles = {} -for i in roles_raw: - roles[i[1]] = i[0] - -# Give roles -for uid in users: - if not users[uid]["isKanidmUser"]: - continue - - if users[uid]["role"]: - rolename = users[uid]["role"] - roleid = roles[rolename] - else: - roleid = "NULL" - - sqlcommand = f"UPDATE users SET role_id = {roleid} WHERE id = {users[uid]["id"]};" - print("[DEBUG] SQL:", sqlcommand) - cur.execute(sqlcommand) - -conn.commit() -cur.close() -conn.close() - -print("[INFO] Final userdata.json file content: ", userdata) - -def write_userdata(mode): - with open(getenv("USERDATA_FILE_PATH"), mode) as f: - f.write(json.dumps(userdata)) - f.close() - -try: - write_userdata("w") -except FileNotFoundError: - print("[INFO] userdata.json file doesn't exist. Creating it") - write_userdata("x") +while True: + sync_mastodon() + time.sleep(SLEEP_TIME) diff --git a/module.nix b/module.nix index f9a9aad..7be02ca 100644 --- a/module.nix +++ b/module.nix @@ -91,7 +91,7 @@ in enableUnixSocket = false; configureNginx = true; database.createLocally = true; - streamingProcesses = 3; + streamingProcesses = 2; smtp = { createLocally = false; @@ -105,8 +105,7 @@ in port = 465; }; extraConfig = { - # "SMTP_ENABLE_STARTTLS" = "never"; - "SMTP_ENABLE_STARTTLS_AUTO" = "true"; + "SMTP_ENABLE_STARTTLS_AUTO" = "true"; # Simple NixOS MailServer doesn't allow connections without SSL "SMTP_ENABLE_STARTTLS" = "always"; "SMTP_TLS" = "true"; "SMTP_SSL" = "true"; @@ -114,14 +113,7 @@ in "DISALLOW_UNAUTHENTICATED_API_ACCESS" = lib.boolToString cfg.dissallowUnauthenticatedAPI; }; }; - users.users.mastodon.isSystemUser = lib.mkForce false; - users.users.mastodon.isNormalUser = lib.mkForce true; - users.groups."email-users" = {}; - users.users."noreply.mastodon" = { - isSystemUser = true; - group = "email-users"; - }; selfprivacy.emails."noreply.mastodon" = { hashedPasswordFile = secrets.hashedPasswordFile; systemdTargets = [ "mastodon-email-password-setup.service" ]; @@ -155,7 +147,6 @@ in services.mastodon-kanidm-sync = { after = [ - # "mastodon.service" # TODO: ?? "postgresql.service" "kanidm.service" ]; @@ -173,6 +164,7 @@ in POSTGRES_HOST = db.host; USERDATA_FILE_PATH = "/var/lib/mastodon/.userdata.json"; OWNER_USERNAME = sp.username; + SLEEP_TIME = "30"; }; serviceConfig = { Slice = "mastodon.slice";