from __future__ import annotations import tomllib from typing import Any import tomli_w from Settings import GetDataDirectory AdminGroupName = "Admin" def GetGroupsPath(): return GetDataDirectory() / "Groups.toml" def LoadGroups() -> dict[str, list[str]]: GroupsPath = GetGroupsPath() if not GroupsPath.is_file(): # First run after upgrading from the old boolean-flag admin model: # seed the Admin group from whoever already had IsAdmin set, so # nobody silently loses admin access. from Users import LoadUsers SeededAdmins = [ Username for Username, Data in LoadUsers().items() if Data.get("IsAdmin", False) ] Groups = {AdminGroupName: SeededAdmins} SaveGroups(Groups) return Groups with GroupsPath.open("rb") as GroupsFile: Loaded = tomllib.load(GroupsFile) Groups = Loaded.get("Groups", {}) if AdminGroupName not in Groups: Groups[AdminGroupName] = [] SaveGroups(Groups) return Groups def SaveGroups(Groups: dict[str, list[str]]) -> None: GetDataDirectory().mkdir(parents=True, exist_ok=True) with GetGroupsPath().open("wb") as GroupsFile: tomli_w.dump({"Groups": Groups}, GroupsFile) def ListGroups() -> list[dict[str, Any]]: Groups = LoadGroups() return [{"Name": Name, "Members": Members} for Name, Members in sorted(Groups.items())] def CreateGroup(Name: str) -> None: Name = Name.strip() if not Name: raise ValueError("Group name cannot be empty.") Groups = LoadGroups() if Name in Groups: raise ValueError(f"Group '{Name}' already exists.") Groups[Name] = [] SaveGroups(Groups) def DeleteGroup(Name: str) -> None: if Name == AdminGroupName: raise ValueError("The Admin group can't be deleted.") Groups = LoadGroups() if Name not in Groups: raise ValueError(f"Group '{Name}' does not exist.") del Groups[Name] SaveGroups(Groups) def SetGroupMembers(Name: str, Members: list[str]) -> None: Groups = LoadGroups() if Name not in Groups: raise ValueError(f"Group '{Name}' does not exist.") # De-duplicate while keeping things predictable. Groups[Name] = sorted(set(Members)) SaveGroups(Groups) def AddMember(GroupName: str, Username: str) -> None: Groups = LoadGroups() if GroupName not in Groups: raise ValueError(f"Group '{GroupName}' does not exist.") if Username not in Groups[GroupName]: Groups[GroupName].append(Username) SaveGroups(Groups) def RemoveMember(GroupName: str, Username: str) -> None: Groups = LoadGroups() if GroupName not in Groups: raise ValueError(f"Group '{GroupName}' does not exist.") if Username in Groups[GroupName]: Groups[GroupName].remove(Username) SaveGroups(Groups) def GetGroupsForUser(Username: str) -> list[str]: Groups = LoadGroups() return [Name for Name, Members in Groups.items() if Username in Members] def IsUserInGroup(Username: str, GroupName: str) -> bool: return Username in LoadGroups().get(GroupName, []) def RenameUserInGroups(OldUsername: str, NewUsername: str) -> None: Groups = LoadGroups() Changed = False for Members in Groups.values(): if OldUsername in Members: Members.remove(OldUsername) Members.append(NewUsername) Changed = True if Changed: SaveGroups(Groups)