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 fastapi.middleware.gzip import GZipMiddleware
|
||||||
|
|
||||||
|
|
||||||
# from src.services.mocks.initial import create_initial_data
|
|
||||||
|
|
||||||
########################
|
########################
|
||||||
# Pre-Alpha Version 0.1.0
|
# Pre-Alpha Version 0.1.0
|
||||||
# Author: @swve
|
# Author: @swve
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ def install(
|
||||||
email="",
|
email="",
|
||||||
logo_image="",
|
logo_image="",
|
||||||
thumbnail_image="",
|
thumbnail_image="",
|
||||||
|
about="",
|
||||||
|
label="",
|
||||||
)
|
)
|
||||||
install_create_organization(org, db_session)
|
install_create_organization(org, db_session)
|
||||||
print("Default organization created ✅")
|
print("Default organization created ✅")
|
||||||
|
|
@ -91,6 +93,8 @@ def install(
|
||||||
email="",
|
email="",
|
||||||
logo_image="",
|
logo_image="",
|
||||||
thumbnail_image="",
|
thumbnail_image="",
|
||||||
|
about="",
|
||||||
|
label="",
|
||||||
)
|
)
|
||||||
install_create_organization(org, db_session)
|
install_create_organization(org, db_session)
|
||||||
print(orgname + " Organization created ✅")
|
print(orgname + " Organization created ✅")
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,36 @@ class Permission(BaseModel):
|
||||||
return getattr(self, item)
|
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):
|
class Rights(BaseModel):
|
||||||
courses: Permission
|
courses: PermissionsWithOwn
|
||||||
users: Permission
|
users: Permission
|
||||||
usergroups : Permission
|
usergroups : Permission
|
||||||
collections: Permission
|
collections: Permission
|
||||||
organizations: Permission
|
organizations: Permission
|
||||||
coursechapters: Permission
|
coursechapters: Permission
|
||||||
activities: Permission
|
activities: Permission
|
||||||
|
roles: Permission
|
||||||
|
dashboard: DashboardPermission
|
||||||
|
|
||||||
def __getitem__(self, item):
|
def __getitem__(self, item):
|
||||||
return getattr(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.resource_authors import ResourceAuthor, ResourceAuthorshipEnum, ResourceAuthorshipStatusEnum
|
||||||
from src.db.roles import Role
|
from src.db.roles import Role
|
||||||
from src.db.user_organizations import UserOrganization
|
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
|
# 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()
|
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
|
# Check all roles until we find one that grants the permission
|
||||||
for role in user_roles_in_organization_and_standard_roles:
|
for role in user_roles_in_organization_and_standard_roles:
|
||||||
role = Role.model_validate(role)
|
role = Role.model_validate(role)
|
||||||
if role.rights:
|
if role.rights:
|
||||||
rights = role.rights
|
rights = role.rights
|
||||||
element_rights = getattr(rights, element_type, None)
|
element_rights = getattr(rights, element_type, None)
|
||||||
if element_rights and getattr(element_rights, f"action_{action}", False):
|
if element_rights:
|
||||||
return True
|
# 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
|
# If we get here, no role granted the permission
|
||||||
return False
|
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):
|
async def get_singular_form_of_element(element_uuid):
|
||||||
element_type = await check_element_type(element_uuid)
|
element_type = await check_element_type(element_uuid)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ async def create_documentpdf_activity(
|
||||||
pdf_file: UploadFile | None = None,
|
pdf_file: UploadFile | None = None,
|
||||||
):
|
):
|
||||||
# RBAC check
|
# 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
|
# get chapter_id
|
||||||
statement = select(Chapter).where(Chapter.id == chapter_id)
|
statement = select(Chapter).where(Chapter.id == chapter_id)
|
||||||
|
|
@ -94,9 +94,7 @@ async def create_documentpdf_activity(
|
||||||
content={
|
content={
|
||||||
"filename": "documentpdf." + pdf_format,
|
"filename": "documentpdf." + pdf_format,
|
||||||
"activity_uuid": activity_uuid,
|
"activity_uuid": activity_uuid,
|
||||||
},
|
},
|
||||||
published_version=1,
|
|
||||||
version=1,
|
|
||||||
org_id=org_id if org_id else 0,
|
org_id=org_id if org_id else 0,
|
||||||
course_id=coursechapter.course_id,
|
course_id=coursechapter.course_id,
|
||||||
activity_uuid=activity_uuid,
|
activity_uuid=activity_uuid,
|
||||||
|
|
|
||||||
|
|
@ -99,13 +99,11 @@ async def create_video_activity(
|
||||||
activity_uuid=activity_uuid,
|
activity_uuid=activity_uuid,
|
||||||
org_id=coursechapter.org_id,
|
org_id=coursechapter.org_id,
|
||||||
course_id=coursechapter.course_id,
|
course_id=coursechapter.course_id,
|
||||||
published_version=1,
|
|
||||||
content={
|
content={
|
||||||
"filename": "video." + video_format,
|
"filename": "video." + video_format,
|
||||||
"activity_uuid": activity_uuid,
|
"activity_uuid": activity_uuid,
|
||||||
},
|
},
|
||||||
details=details,
|
details=details,
|
||||||
version=1,
|
|
||||||
creation_date=str(datetime.now()),
|
creation_date=str(datetime.now()),
|
||||||
update_date=str(datetime.now()),
|
update_date=str(datetime.now()),
|
||||||
)
|
)
|
||||||
|
|
@ -198,14 +196,12 @@ async def create_external_video_activity(
|
||||||
activity_uuid=activity_uuid,
|
activity_uuid=activity_uuid,
|
||||||
course_id=coursechapter.course_id,
|
course_id=coursechapter.course_id,
|
||||||
org_id=coursechapter.org_id,
|
org_id=coursechapter.org_id,
|
||||||
published_version=1,
|
|
||||||
content={
|
content={
|
||||||
"uri": data.uri,
|
"uri": data.uri,
|
||||||
"type": data.type,
|
"type": data.type,
|
||||||
"activity_uuid": activity_uuid,
|
"activity_uuid": activity_uuid,
|
||||||
},
|
},
|
||||||
details=details,
|
details=details,
|
||||||
version=1,
|
|
||||||
creation_date=str(datetime.now()),
|
creation_date=str(datetime.now()),
|
||||||
update_date=str(datetime.now()),
|
update_date=str(datetime.now()),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ from src.db.organization_config import (
|
||||||
UserGroupOrgConfig,
|
UserGroupOrgConfig,
|
||||||
)
|
)
|
||||||
from src.db.organizations import Organization, OrganizationCreate
|
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.user_organizations import UserOrganization
|
||||||
from src.db.users import User, UserCreate, UserRead
|
from src.db.users import User, UserCreate, UserRead
|
||||||
from config.config import get_learnhouse_config
|
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)
|
statement = select(Role).where(Role.role_type == RoleTypeEnum.TYPE_GLOBAL)
|
||||||
roles = db_session.exec(statement).all()
|
roles = db_session.exec(statement).all()
|
||||||
|
|
||||||
if roles and len(roles) == 3:
|
if roles and len(roles) == 4:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=409,
|
status_code=409,
|
||||||
detail="Default roles already exist",
|
detail="Default roles already exist",
|
||||||
|
|
@ -136,16 +136,19 @@ def install_default_elements(db_session: Session):
|
||||||
# Create default roles
|
# Create default roles
|
||||||
role_global_admin = Role(
|
role_global_admin = Role(
|
||||||
name="Admin",
|
name="Admin",
|
||||||
description="Standard Admin Role",
|
description="Full platform control",
|
||||||
id=1,
|
id=1,
|
||||||
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
||||||
role_uuid="role_global_admin",
|
role_uuid="role_global_admin",
|
||||||
rights=Rights(
|
rights=Rights(
|
||||||
courses=Permission(
|
courses=PermissionsWithOwn(
|
||||||
action_create=True,
|
action_create=True,
|
||||||
action_read=True,
|
action_read=True,
|
||||||
|
action_read_own=True,
|
||||||
action_update=True,
|
action_update=True,
|
||||||
|
action_update_own=True,
|
||||||
action_delete=True,
|
action_delete=True,
|
||||||
|
action_delete_own=True,
|
||||||
),
|
),
|
||||||
users=Permission(
|
users=Permission(
|
||||||
action_create=True,
|
action_create=True,
|
||||||
|
|
@ -183,6 +186,15 @@ def install_default_elements(db_session: Session):
|
||||||
action_update=True,
|
action_update=True,
|
||||||
action_delete=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()),
|
creation_date=str(datetime.now()),
|
||||||
update_date=str(datetime.now()),
|
update_date=str(datetime.now()),
|
||||||
|
|
@ -190,22 +202,25 @@ def install_default_elements(db_session: Session):
|
||||||
|
|
||||||
role_global_maintainer = Role(
|
role_global_maintainer = Role(
|
||||||
name="Maintainer",
|
name="Maintainer",
|
||||||
description="Standard Maintainer Role",
|
description="Mid-level manager, wide permissions but no platform control",
|
||||||
id=2,
|
id=2,
|
||||||
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
||||||
role_uuid="role_global_maintainer",
|
role_uuid="role_global_maintainer",
|
||||||
rights=Rights(
|
rights=Rights(
|
||||||
courses=Permission(
|
courses=PermissionsWithOwn(
|
||||||
action_create=True,
|
action_create=True,
|
||||||
action_read=True,
|
action_read=True,
|
||||||
|
action_read_own=True,
|
||||||
action_update=True,
|
action_update=True,
|
||||||
|
action_update_own=True,
|
||||||
action_delete=True,
|
action_delete=True,
|
||||||
|
action_delete_own=True,
|
||||||
),
|
),
|
||||||
users=Permission(
|
users=Permission(
|
||||||
action_create=True,
|
action_create=True,
|
||||||
action_read=True,
|
action_read=True,
|
||||||
action_update=True,
|
action_update=True,
|
||||||
action_delete=True,
|
action_delete=False,
|
||||||
),
|
),
|
||||||
usergroups=Permission(
|
usergroups=Permission(
|
||||||
action_create=True,
|
action_create=True,
|
||||||
|
|
@ -220,10 +235,10 @@ def install_default_elements(db_session: Session):
|
||||||
action_delete=True,
|
action_delete=True,
|
||||||
),
|
),
|
||||||
organizations=Permission(
|
organizations=Permission(
|
||||||
action_create=True,
|
action_create=False,
|
||||||
action_read=True,
|
action_read=True,
|
||||||
action_update=True,
|
action_update=False,
|
||||||
action_delete=True,
|
action_delete=False,
|
||||||
),
|
),
|
||||||
coursechapters=Permission(
|
coursechapters=Permission(
|
||||||
action_create=True,
|
action_create=True,
|
||||||
|
|
@ -237,6 +252,81 @@ def install_default_elements(db_session: Session):
|
||||||
action_update=True,
|
action_update=True,
|
||||||
action_delete=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()),
|
creation_date=str(datetime.now()),
|
||||||
update_date=str(datetime.now()),
|
update_date=str(datetime.now()),
|
||||||
|
|
@ -244,20 +334,23 @@ def install_default_elements(db_session: Session):
|
||||||
|
|
||||||
role_global_user = Role(
|
role_global_user = Role(
|
||||||
name="User",
|
name="User",
|
||||||
description="Standard User Role",
|
description="Read-Only Learner",
|
||||||
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
role_type=RoleTypeEnum.TYPE_GLOBAL,
|
||||||
role_uuid="role_global_user",
|
role_uuid="role_global_user",
|
||||||
id=3,
|
id=4,
|
||||||
rights=Rights(
|
rights=Rights(
|
||||||
courses=Permission(
|
courses=PermissionsWithOwn(
|
||||||
action_create=False,
|
action_create=False,
|
||||||
action_read=True,
|
action_read=True,
|
||||||
|
action_read_own=True,
|
||||||
action_update=False,
|
action_update=False,
|
||||||
action_delete=False,
|
action_update_own=False,
|
||||||
|
action_delete=True,
|
||||||
|
action_delete_own=True,
|
||||||
),
|
),
|
||||||
users=Permission(
|
users=Permission(
|
||||||
action_create=True,
|
action_create=False,
|
||||||
action_read=True,
|
action_read=False,
|
||||||
action_update=False,
|
action_update=False,
|
||||||
action_delete=False,
|
action_delete=False,
|
||||||
),
|
),
|
||||||
|
|
@ -275,7 +368,7 @@ def install_default_elements(db_session: Session):
|
||||||
),
|
),
|
||||||
organizations=Permission(
|
organizations=Permission(
|
||||||
action_create=False,
|
action_create=False,
|
||||||
action_read=True,
|
action_read=False,
|
||||||
action_update=False,
|
action_update=False,
|
||||||
action_delete=False,
|
action_delete=False,
|
||||||
),
|
),
|
||||||
|
|
@ -291,6 +384,15 @@ def install_default_elements(db_session: Session):
|
||||||
action_update=False,
|
action_update=False,
|
||||||
action_delete=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()),
|
creation_date=str(datetime.now()),
|
||||||
update_date=str(datetime.now()),
|
update_date=str(datetime.now()),
|
||||||
|
|
@ -299,11 +401,13 @@ def install_default_elements(db_session: Session):
|
||||||
# Serialize rights to JSON
|
# Serialize rights to JSON
|
||||||
role_global_admin.rights = role_global_admin.rights.dict() # type: ignore
|
role_global_admin.rights = role_global_admin.rights.dict() # type: ignore
|
||||||
role_global_maintainer.rights = role_global_maintainer.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
|
role_global_user.rights = role_global_user.rights.dict() # type: ignore
|
||||||
|
|
||||||
# Insert roles in DB
|
# Insert roles in DB
|
||||||
db_session.add(role_global_admin)
|
db_session.add(role_global_admin)
|
||||||
db_session.add(role_global_maintainer)
|
db_session.add(role_global_maintainer)
|
||||||
|
db_session.add(role_global_instructor)
|
||||||
db_session.add(role_global_user)
|
db_session.add(role_global_user)
|
||||||
|
|
||||||
# commit changes
|
# commit changes
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue