Refactor to manager-centric model, add team calendar for all users

Key changes:
- Removed office-centric model (deleted offices.py, office-rules)
- Renamed to team-rules, managers are part of their own team
- Team calendar visible to all (read-only for employees)
- Admins can have a manager assigned
This commit is contained in:
Stefano Manfredi
2025-12-02 13:30:04 +00:00
parent 2ad8ba3424
commit 7168fa4b72
30 changed files with 1016 additions and 910 deletions

View File

@@ -16,13 +16,9 @@ from app import config
security = HTTPBearer(auto_error=False)
def get_role_from_groups(groups: list[str]) -> str:
"""Map Authelia groups to application roles"""
if config.AUTHELIA_ADMIN_GROUP in groups:
return "admin"
if config.AUTHELIA_MANAGER_GROUP in groups:
return "manager"
return "employee"
def is_admin_from_groups(groups: list[str]) -> bool:
"""Check if user is admin based on Authelia groups"""
return config.AUTHELIA_ADMIN_GROUP in groups
def get_or_create_authelia_user(
@@ -31,14 +27,28 @@ def get_or_create_authelia_user(
groups: list[str],
db: Session
) -> User:
"""Get existing user or create from Authelia headers"""
"""
Get existing user or create from Authelia headers.
Role logic:
- If user is in parking_admins group -> admin role (always synced from LLDAP)
- Otherwise -> keep existing role, or 'employee' for new users
Admin assigns manager role and user assignments via the UI.
"""
user = get_user_by_email(db, email)
role = get_role_from_groups(groups)
is_admin = is_admin_from_groups(groups)
if user:
# Update role if changed in LLDAP
if user.role != role:
user.role = role
# Only sync admin status from LLDAP, other roles managed by app admin
if is_admin and user.role != "admin":
user.role = "admin"
user.updated_at = datetime.utcnow().isoformat()
db.commit()
db.refresh(user)
elif not is_admin and user.role == "admin":
# Removed from parking_admins group -> demote to employee
user.role = "employee"
user.updated_at = datetime.utcnow().isoformat()
db.commit()
db.refresh(user)
@@ -55,7 +65,7 @@ def get_or_create_authelia_user(
id=str(uuid.uuid4()),
email=email,
name=name or email.split("@")[0],
role=role,
role="admin" if is_admin else "employee",
password_hash=None, # No password for Authelia users
created_at=datetime.utcnow().isoformat(),
updated_at=datetime.utcnow().isoformat()
@@ -82,6 +92,8 @@ def get_current_user(
remote_name = request.headers.get(config.AUTHELIA_HEADER_NAME, "")
remote_groups = request.headers.get(config.AUTHELIA_HEADER_GROUPS, "")
print(f"[Authelia] Headers: user={remote_user}, email={remote_email}, name={remote_name}, groups={remote_groups}")
if not remote_user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
@@ -149,18 +161,17 @@ def require_manager_or_admin(user=Depends(get_current_user)):
def check_manager_access_to_user(current_user, target_user, db: Session) -> bool:
"""
Check if current_user (manager) has access to target_user.
Admins always have access. Managers can only access users in their managed offices.
Admins always have access. Managers can only access users they manage.
Returns True if access granted, raises HTTPException if not.
"""
if current_user.role == "admin":
return True
if current_user.role == "manager":
managed_office_ids = [m.office_id for m in current_user.managed_offices]
if target_user.office_id not in managed_office_ids:
if target_user.manager_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="User not in your managed offices"
detail="User is not managed by you"
)
return True