user management fix

This commit is contained in:
2026-09-17 16:14:19 +05:00
parent 298124c30f
commit 77c00c4ed5
3 changed files with 327 additions and 391 deletions
+265
View File
@@ -0,0 +1,265 @@
import json
import socket
from pathlib import Path
SOCKET_PATH = Path("/run/parental-control.sock")
SOCKET_TIMEOUT = 10
class IPCError(Exception):
"""Base exception for IPC errors."""
class DatabaseError(IPCError):
"""Raised when a database operation fails."""
class AuthorizationError(IPCError):
"""Raised when an administrative operation is denied."""
class IPCClient:
@staticmethod
def call(method, **arguments):
request = {
"method": method,
"args": arguments,
}
payload = (
json.dumps(
request,
separators=(",", ":"),
).encode("utf-8")
+ b"\n"
)
data = bytearray()
try:
with socket.socket(
socket.AF_UNIX,
socket.SOCK_STREAM,
) as connection:
connection.settimeout(
SOCKET_TIMEOUT
)
connection.connect(
str(SOCKET_PATH)
)
connection.sendall(
payload
)
while True:
chunk = connection.recv(
65536
)
if not chunk:
break
data.extend(chunk)
if b"\n" in chunk:
break
except FileNotFoundError:
raise IPCError(
"Parental control service is not running."
)
except ConnectionRefusedError:
raise IPCError(
"Could not connect to the parental control service."
)
except TimeoutError:
raise IPCError(
"The parental control service did not respond."
)
except OSError as exc:
raise IPCError(
f"Could not connect to the parental control service: {exc}"
)
if not data:
raise IPCError(
"The parental control service returned no response."
)
line = bytes(data).split(
b"\n",
1,
)[0]
try:
response = json.loads(
line.decode("utf-8")
)
except (
UnicodeDecodeError,
json.JSONDecodeError,
):
raise IPCError(
"The parental control service returned an invalid response."
)
if response.get("ok"):
return response.get("result")
error = response.get(
"error",
"Unknown error",
)
if response.get("error_type") == "authorization":
raise AuthorizationError(error)
if response.get("error_type") == "database":
raise DatabaseError(error)
raise IPCError(error)
class Database:
"""
Compatibility interface for the GUI.
The GUI keeps using Database.* methods, but all operations
now go through the root parental-control daemon instead of
accessing SQLite directly.
"""
@staticmethod
def get_users():
return IPCClient.call(
"get_users"
)
@staticmethod
def get_user(user_id):
return IPCClient.call(
"get_user",
user_id=user_id,
)
@staticmethod
def get_remaining_time(user_id):
result = IPCClient.call(
"get_remaining_time",
user_id=user_id,
)
return (
result[0],
result[1],
)
@staticmethod
def get_allowances(user_id):
result = IPCClient.call(
"get_allowances",
user_id=user_id,
)
return {
int(key): value
for key, value in result.items()
}
@staticmethod
def get_access_windows(user_id):
result = IPCClient.call(
"get_access_windows",
user_id=user_id,
)
return {
int(key): [
tuple(window)
for window in windows
]
for key, windows in result.items()
}
@staticmethod
def create_user(
username,
enabled,
allowances,
access_windows,
):
return IPCClient.call(
"create_user",
username=username,
enabled=enabled,
allowances={
str(key): value
for key, value in allowances.items()
},
access_windows={
str(key): [
list(window)
for window in windows
]
for key, windows in access_windows.items()
},
)
@staticmethod
def update_user(
user_id,
enabled,
allowances,
access_windows,
):
return IPCClient.call(
"update_user",
user_id=user_id,
enabled=enabled,
allowances={
str(key): value
for key, value in allowances.items()
},
access_windows={
str(key): [
list(window)
for window in windows
]
for key, windows in access_windows.items()
},
)
@staticmethod
def delete_user(user_id):
return IPCClient.call(
"delete_user",
user_id=user_id,
)
@staticmethod
def set_enabled(
user_id,
enabled,
):
return IPCClient.call(
"set_enabled",
user_id=user_id,
enabled=enabled,
)
@staticmethod
def add_temporary_time(
user_id,
seconds,
):
return IPCClient.call(
"add_temporary_time",
user_id=user_id,
seconds=seconds,
)