feat(P2.2): add default mgr/dev role seeds with preset permissions for milestone/task/propose actions
This commit is contained in:
@@ -151,61 +151,93 @@ def init_default_permissions(db: Session) -> list[Permission]:
|
|||||||
return db.query(Permission).all()
|
return db.query(Permission).all()
|
||||||
|
|
||||||
|
|
||||||
def init_admin_role(db: Session, admin_user: models.User) -> None:
|
# ---------------------------------------------------------------------------
|
||||||
"""Create admin role with all permissions and guest role with minimal permissions."""
|
# Default role → permission mapping
|
||||||
# Check if admin role already exists
|
# ---------------------------------------------------------------------------
|
||||||
admin_role = db.query(Role).filter(Role.name == "admin").first()
|
|
||||||
if not admin_role:
|
# mgr: project management + all milestone/task/propose actions
|
||||||
admin_role = Role(
|
_MGR_PERMISSIONS = {
|
||||||
name="admin",
|
"project.read", "project.write", "project.manage_members",
|
||||||
description="Administrator - full access to all features",
|
"task.create", "task.read", "task.write", "task.delete",
|
||||||
is_global=True
|
"milestone.create", "milestone.read", "milestone.write", "milestone.delete",
|
||||||
)
|
"milestone.freeze", "milestone.start", "milestone.close",
|
||||||
db.add(admin_role)
|
"task.close", "task.reopen_closed", "task.reopen_completed",
|
||||||
|
"propose.accept", "propose.reject", "propose.reopen",
|
||||||
|
"monitor.read",
|
||||||
|
}
|
||||||
|
|
||||||
|
# dev: day-to-day development work — no freeze/start/close milestone, no accept/reject propose
|
||||||
|
_DEV_PERMISSIONS = {
|
||||||
|
"project.read",
|
||||||
|
"task.create", "task.read", "task.write",
|
||||||
|
"milestone.read",
|
||||||
|
"task.close", "task.reopen_closed", "task.reopen_completed",
|
||||||
|
"monitor.read",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Role definitions: (name, description, permission_set)
|
||||||
|
_DEFAULT_ROLES = [
|
||||||
|
("admin", "Administrator - full access to all features", None), # None ⇒ all perms
|
||||||
|
("mgr", "Manager - project & milestone management", _MGR_PERMISSIONS),
|
||||||
|
("dev", "Developer - task execution & daily work", _DEV_PERMISSIONS),
|
||||||
|
("guest", "Guest - read-only access", None), # special: *.read only
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _ensure_role(db: Session, name: str, description: str, is_global: bool = True) -> Role:
|
||||||
|
"""Get or create a role by name."""
|
||||||
|
role = db.query(Role).filter(Role.name == name).first()
|
||||||
|
if not role:
|
||||||
|
role = Role(name=name, description=description, is_global=is_global)
|
||||||
|
db.add(role)
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(admin_role)
|
db.refresh(role)
|
||||||
logger.info("Created admin role (id=%d)", admin_role.id)
|
logger.info("Created role '%s' (id=%d)", name, role.id)
|
||||||
|
return role
|
||||||
# Check if guest role already exists
|
|
||||||
guest_role = db.query(Role).filter(Role.name == "guest").first()
|
|
||||||
if not guest_role:
|
def _sync_role_permissions(db: Session, role: Role, target_perm_names: set[str] | None) -> None:
|
||||||
guest_role = Role(
|
"""Ensure *role* has exactly the permissions in *target_perm_names*.
|
||||||
name="guest",
|
|
||||||
description="Guest - read-only access",
|
* ``None`` means **all** permissions (admin).
|
||||||
is_global=True
|
* The special sentinel ``"__read_only__"`` is handled by the caller passing
|
||||||
)
|
just the ``*.read`` names.
|
||||||
db.add(guest_role)
|
Only adds missing permissions; never removes manually-granted ones (additive).
|
||||||
db.commit()
|
"""
|
||||||
db.refresh(guest_role)
|
|
||||||
logger.info("Created guest role (id=%d)", guest_role.id)
|
|
||||||
|
|
||||||
# Get all permissions
|
|
||||||
all_perms = db.query(Permission).all()
|
all_perms = db.query(Permission).all()
|
||||||
|
perm_by_name = {p.name: p for p in all_perms}
|
||||||
# Assign all permissions to admin role
|
|
||||||
existing_admin_perm_ids = {rp.permission_id for rp in admin_role.permissions}
|
if target_perm_names is None:
|
||||||
for perm in all_perms:
|
wanted_ids = {p.id for p in all_perms}
|
||||||
if perm.id not in existing_admin_perm_ids:
|
else:
|
||||||
rp = RolePermission(role_id=admin_role.id, permission_id=perm.id)
|
wanted_ids = {perm_by_name[n].id for n in target_perm_names if n in perm_by_name}
|
||||||
db.add(rp)
|
|
||||||
|
existing_ids = {rp.permission_id for rp in role.permissions}
|
||||||
if all_perms:
|
added = 0
|
||||||
|
for pid in wanted_ids - existing_ids:
|
||||||
|
db.add(RolePermission(role_id=role.id, permission_id=pid))
|
||||||
|
added += 1
|
||||||
|
|
||||||
|
if added:
|
||||||
db.commit()
|
db.commit()
|
||||||
logger.info("Assigned %d permissions to admin role", len(all_perms))
|
logger.info("Assigned %d new permissions to role '%s'", added, role.name)
|
||||||
|
|
||||||
# Assign only read permissions to guest role
|
|
||||||
read_perms = db.query(Permission).filter(Permission.name.like("%.read")).all()
|
def init_admin_role(db: Session, admin_user: models.User) -> None:
|
||||||
existing_guest_perm_ids = {rp.permission_id for rp in guest_role.permissions}
|
"""Create default roles (admin / mgr / dev / guest) with preset permissions."""
|
||||||
for perm in read_perms:
|
|
||||||
if perm.id not in existing_guest_perm_ids:
|
all_perms = db.query(Permission).all()
|
||||||
rp = RolePermission(role_id=guest_role.id, permission_id=perm.id)
|
read_perm_names = {p.name for p in all_perms if p.name.endswith(".read")}
|
||||||
db.add(rp)
|
|
||||||
|
for name, description, perm_set in _DEFAULT_ROLES:
|
||||||
if read_perms:
|
role = _ensure_role(db, name, description)
|
||||||
db.commit()
|
|
||||||
logger.info("Assigned %d read permissions to guest role", len(read_perms))
|
if name == "guest":
|
||||||
|
_sync_role_permissions(db, role, read_perm_names)
|
||||||
logger.info("Admin and guest roles setup complete")
|
else:
|
||||||
|
_sync_role_permissions(db, role, perm_set)
|
||||||
|
|
||||||
|
logger.info("Default roles setup complete (admin, mgr, dev, guest)")
|
||||||
|
|
||||||
|
|
||||||
def run_init(db: Session) -> None:
|
def run_init(db: Session) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user