mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
refactor: update role permissions and enhance RBAC logic to include own permissions for courses and dashboard access
This commit is contained in:
parent
a1976c5423
commit
9f13884c08
8 changed files with 201 additions and 31 deletions
|
|
@ -11,8 +11,6 @@ from fastapi_jwt_auth.exceptions import AuthJWTException
|
|||
from fastapi.middleware.gzip import GZipMiddleware
|
||||
|
||||
|
||||
# from src.services.mocks.initial import create_initial_data
|
||||
|
||||
########################
|
||||
# Pre-Alpha Version 0.1.0
|
||||
# Author: @swve
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ def install(
|
|||
email="",
|
||||
logo_image="",
|
||||
thumbnail_image="",
|
||||
about="",
|
||||
label="",
|
||||
)
|
||||
install_create_organization(org, db_session)
|
||||
print("Default organization created ✅")
|
||||
|
|
@ -91,6 +93,8 @@ def install(
|
|||
email="",
|
||||
logo_image="",
|
||||
thumbnail_image="",
|
||||
about="",
|
||||
label="",
|
||||
)
|
||||
install_create_organization(org, db_session)
|
||||
print(orgname + " Organization created ✅")
|
||||
|
|
|
|||
|
|
@ -16,14 +16,36 @@ class Permission(BaseModel):
|
|||
return getattr(self, item)
|
||||
|
||||
|
||||
class PermissionsWithOwn(BaseModel):
|
||||
action_create: bool
|
||||
action_read: bool
|
||||
action_read_own: bool
|
||||
action_update: bool
|
||||
action_update_own: bool
|
||||
action_delete: bool
|
||||
action_delete_own: bool
|
||||
|
||||
def __getitem__(self, item):
|
||||
return getattr(self, item)
|
||||
|
||||
|
||||
class DashboardPermission(BaseModel):
|
||||
action_access: bool
|
||||
|
||||
def __getitem__(self, item):
|
||||
return getattr(self, item)
|
||||
|
||||
|
||||
class Rights(BaseModel):
|
||||
courses: Permission
|
||||
courses: PermissionsWithOwn
|
||||
users: Permission
|
||||
usergroups : Permission
|
||||
collections: Permission
|
||||
organizations: Permission
|
||||
coursechapters: Permission
|
||||
activities: Permission
|
||||
roles: Permission
|
||||
dashboard: DashboardPermission
|
||||
|
||||
def __getitem__(self, item):
|
||||
return getattr(self, item)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from src.db.courses.courses import Course
|
|||
from src.db.resource_authors import ResourceAuthor, ResourceAuthorshipEnum, ResourceAuthorshipStatusEnum
|
||||
from src.db.roles import Role
|
||||
from src.db.user_organizations import UserOrganization
|
||||
from src.security.rbac.utils import check_element_type
|
||||
from src.security.rbac.utils import check_element_type, check_course_permissions_with_own
|
||||
|
||||
|
||||
# Tested and working
|
||||
|
|
@ -106,14 +106,30 @@ async def authorization_verify_based_on_roles(
|
|||
|
||||
user_roles_in_organization_and_standard_roles = db_session.exec(statement).all()
|
||||
|
||||
|
||||
# Check if user is the author of the resource for "own" permissions
|
||||
is_author = False
|
||||
if action in ["update", "delete", "read"]:
|
||||
is_author = await authorization_verify_if_user_is_author(
|
||||
request, user_id, action, element_uuid, db_session
|
||||
)
|
||||
|
||||
# Check all roles until we find one that grants the permission
|
||||
for role in user_roles_in_organization_and_standard_roles:
|
||||
role = Role.model_validate(role)
|
||||
if role.rights:
|
||||
rights = role.rights
|
||||
element_rights = getattr(rights, element_type, None)
|
||||
if element_rights and getattr(element_rights, f"action_{action}", False):
|
||||
return True
|
||||
if element_rights:
|
||||
# Special handling for courses with PermissionsWithOwn
|
||||
if element_type == "courses":
|
||||
if await check_course_permissions_with_own(element_rights, action, is_author):
|
||||
return True
|
||||
else:
|
||||
# For non-course resources, only check general permissions
|
||||
# (regular Permission class no longer has "own" permissions)
|
||||
if getattr(element_rights, f"action_{action}", False):
|
||||
return True
|
||||
|
||||
# If we get here, no role granted the permission
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -30,6 +30,38 @@ async def check_element_type(element_uuid):
|
|||
)
|
||||
|
||||
|
||||
async def check_course_permissions_with_own(
|
||||
element_rights,
|
||||
action: str,
|
||||
is_author: bool = False
|
||||
) -> bool:
|
||||
"""
|
||||
Check course-specific permissions including "own" permissions.
|
||||
|
||||
Args:
|
||||
element_rights: The rights object for courses (PermissionsWithOwn)
|
||||
action: The action to check ("read", "update", "delete", "create")
|
||||
is_author: Whether the user is the author of the course
|
||||
|
||||
Returns:
|
||||
bool: True if permission is granted, False otherwise
|
||||
"""
|
||||
if not element_rights:
|
||||
return False
|
||||
|
||||
# Check for general permission first
|
||||
if getattr(element_rights, f"action_{action}", False):
|
||||
return True
|
||||
|
||||
# Check for "own" permission if user is the author
|
||||
if is_author:
|
||||
own_action = f"action_{action}_own"
|
||||
if getattr(element_rights, own_action, False):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
async def get_singular_form_of_element(element_uuid):
|
||||
element_type = await check_element_type(element_uuid)
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ async def create_documentpdf_activity(
|
|||
pdf_file: UploadFile | None = None,
|
||||
):
|
||||
# RBAC check
|
||||
await rbac_check(request, "activity_x", current_user, "create", db_session)
|
||||
await rbac_check(request, "course_uuid", current_user, "create", db_session)
|
||||
|
||||
# get chapter_id
|
||||
statement = select(Chapter).where(Chapter.id == chapter_id)
|
||||
|
|
@ -94,9 +94,7 @@ async def create_documentpdf_activity(
|
|||
content={
|
||||
"filename": "documentpdf." + pdf_format,
|
||||
"activity_uuid": activity_uuid,
|
||||
},
|
||||
published_version=1,
|
||||
version=1,
|
||||
},
|
||||
org_id=org_id if org_id else 0,
|
||||
course_id=coursechapter.course_id,
|
||||
activity_uuid=activity_uuid,
|
||||
|
|
|
|||
|
|
@ -99,13 +99,11 @@ async def create_video_activity(
|
|||
activity_uuid=activity_uuid,
|
||||
org_id=coursechapter.org_id,
|
||||
course_id=coursechapter.course_id,
|
||||
published_version=1,
|
||||
content={
|
||||
"filename": "video." + video_format,
|
||||
"activity_uuid": activity_uuid,
|
||||
},
|
||||
details=details,
|
||||
version=1,
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
)
|
||||
|
|
@ -198,14 +196,12 @@ async def create_external_video_activity(
|
|||
activity_uuid=activity_uuid,
|
||||
course_id=coursechapter.course_id,
|
||||
org_id=coursechapter.org_id,
|
||||
published_version=1,
|
||||
content={
|
||||
"uri": data.uri,
|
||||
"type": data.type,
|
||||
"activity_uuid": activity_uuid,
|
||||
},
|
||||
details=details,
|
||||
version=1,
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ from src.db.organization_config import (
|
|||
UserGroupOrgConfig,
|
||||
)
|
||||
from src.db.organizations import Organization, OrganizationCreate
|
||||
from src.db.roles import Permission, Rights, Role, RoleTypeEnum
|
||||
from src.db.roles import DashboardPermission, Permission, PermissionsWithOwn, Rights, Role, RoleTypeEnum
|
||||
from src.db.user_organizations import UserOrganization
|
||||
from src.db.users import User, UserCreate, UserRead
|
||||
from config.config import get_learnhouse_config
|
||||
|
|
@ -127,7 +127,7 @@ def install_default_elements(db_session: Session):
|
|||
statement = select(Role).where(Role.role_type == RoleTypeEnum.TYPE_GLOBAL)
|
||||
roles = db_session.exec(statement).all()
|
||||
|
||||
if roles and len(roles) == 3:
|
||||
if roles and len(roles) == 4:
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="Default roles already exist",
|
||||
|
|
@ -136,16 +136,19 @@ def install_default_elements(db_session: Session):
|
|||
# Create default roles
|
||||
role_global_admin = Role(
|
||||
name="Admin",
|
||||
description="Standard Admin Role",
|
||||
description="Full platform control",
|
||||
id=1,
|
||||
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
||||
role_uuid="role_global_admin",
|
||||
rights=Rights(
|
||||
courses=Permission(
|
||||
courses=PermissionsWithOwn(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_read_own=True,
|
||||
action_update=True,
|
||||
action_update_own=True,
|
||||
action_delete=True,
|
||||
action_delete_own=True,
|
||||
),
|
||||
users=Permission(
|
||||
action_create=True,
|
||||
|
|
@ -183,6 +186,15 @@ def install_default_elements(db_session: Session):
|
|||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
roles=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
dashboard=DashboardPermission(
|
||||
action_access=True,
|
||||
),
|
||||
),
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
|
|
@ -190,22 +202,25 @@ def install_default_elements(db_session: Session):
|
|||
|
||||
role_global_maintainer = Role(
|
||||
name="Maintainer",
|
||||
description="Standard Maintainer Role",
|
||||
description="Mid-level manager, wide permissions but no platform control",
|
||||
id=2,
|
||||
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
||||
role_uuid="role_global_maintainer",
|
||||
rights=Rights(
|
||||
courses=Permission(
|
||||
courses=PermissionsWithOwn(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_read_own=True,
|
||||
action_update=True,
|
||||
action_update_own=True,
|
||||
action_delete=True,
|
||||
action_delete_own=True,
|
||||
),
|
||||
users=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
action_delete=False,
|
||||
),
|
||||
usergroups=Permission(
|
||||
action_create=True,
|
||||
|
|
@ -220,10 +235,10 @@ def install_default_elements(db_session: Session):
|
|||
action_delete=True,
|
||||
),
|
||||
organizations=Permission(
|
||||
action_create=True,
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
coursechapters=Permission(
|
||||
action_create=True,
|
||||
|
|
@ -237,6 +252,81 @@ def install_default_elements(db_session: Session):
|
|||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
roles=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
dashboard=DashboardPermission(
|
||||
action_access=True,
|
||||
),
|
||||
),
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
)
|
||||
|
||||
role_global_instructor = Role(
|
||||
name="Instructor",
|
||||
description="Can manage their own content",
|
||||
id=3,
|
||||
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
||||
role_uuid="role_global_instructor",
|
||||
rights=Rights(
|
||||
courses=PermissionsWithOwn(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_read_own=True,
|
||||
action_update=False,
|
||||
action_update_own=True,
|
||||
action_delete=False,
|
||||
action_delete_own=True,
|
||||
),
|
||||
users=Permission(
|
||||
action_create=False,
|
||||
action_read=False,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
usergroups=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
collections=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
organizations=Permission(
|
||||
action_create=False,
|
||||
action_read=False,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
coursechapters=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
activities=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
roles=Permission(
|
||||
action_create=False,
|
||||
action_read=False,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
dashboard=DashboardPermission(
|
||||
action_access=True,
|
||||
),
|
||||
),
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
|
|
@ -244,20 +334,23 @@ def install_default_elements(db_session: Session):
|
|||
|
||||
role_global_user = Role(
|
||||
name="User",
|
||||
description="Standard User Role",
|
||||
description="Read-Only Learner",
|
||||
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
||||
role_uuid="role_global_user",
|
||||
id=3,
|
||||
id=4,
|
||||
rights=Rights(
|
||||
courses=Permission(
|
||||
courses=PermissionsWithOwn(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_read_own=True,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
action_update_own=False,
|
||||
action_delete=True,
|
||||
action_delete_own=True,
|
||||
),
|
||||
users=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_create=False,
|
||||
action_read=False,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
|
|
@ -275,7 +368,7 @@ def install_default_elements(db_session: Session):
|
|||
),
|
||||
organizations=Permission(
|
||||
action_create=False,
|
||||
action_read=True,
|
||||
action_read=False,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
|
|
@ -291,6 +384,15 @@ def install_default_elements(db_session: Session):
|
|||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
roles=Permission(
|
||||
action_create=False,
|
||||
action_read=False,
|
||||
action_update=False,
|
||||
action_delete=False,
|
||||
),
|
||||
dashboard=DashboardPermission(
|
||||
action_access=False,
|
||||
),
|
||||
),
|
||||
creation_date=str(datetime.now()),
|
||||
update_date=str(datetime.now()),
|
||||
|
|
@ -299,11 +401,13 @@ def install_default_elements(db_session: Session):
|
|||
# Serialize rights to JSON
|
||||
role_global_admin.rights = role_global_admin.rights.dict() # type: ignore
|
||||
role_global_maintainer.rights = role_global_maintainer.rights.dict() # type: ignore
|
||||
role_global_instructor.rights = role_global_instructor.rights.dict() # type: ignore
|
||||
role_global_user.rights = role_global_user.rights.dict() # type: ignore
|
||||
|
||||
# Insert roles in DB
|
||||
db_session.add(role_global_admin)
|
||||
db_session.add(role_global_maintainer)
|
||||
db_session.add(role_global_instructor)
|
||||
db_session.add(role_global_user)
|
||||
|
||||
# commit changes
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue