diff --git a/src/routers/auth.py b/src/routers/auth.py index b53ffb1f..4eaa4ba8 100644 --- a/src/routers/auth.py +++ b/src/routers/auth.py @@ -1,8 +1,8 @@ from urllib.request import Request from fastapi import Depends, APIRouter, HTTPException, Response, status, Request from fastapi.security import OAuth2PasswordRequestForm -from src.security.auth import * -from src.services.users.users import * +from src.security.auth import AuthJWT, authenticate_user +from src.services.users.users import PublicUser router = APIRouter() diff --git a/src/routers/courses/activities.py b/src/routers/courses/activities.py index 2694c650..f04af1d7 100644 --- a/src/routers/courses/activities.py +++ b/src/routers/courses/activities.py @@ -1,5 +1,12 @@ from fastapi import APIRouter, Depends, UploadFile, Form, Request -from src.services.courses.activities.activities import * +from src.services.courses.activities.activities import ( + Activity, + create_activity, + get_activity, + get_activities, + update_activity, + delete_activity, +) from src.security.auth import get_current_user from src.services.courses.activities.pdf import create_documentpdf_activity from src.services.courses.activities.video import ( @@ -7,6 +14,7 @@ from src.services.courses.activities.video import ( create_external_video_activity, create_video_activity, ) +from src.services.users.schemas.users import PublicUser router = APIRouter() @@ -104,9 +112,7 @@ async def api_create_external_video_activity( """ Create new activity """ - return await create_external_video_activity( - request, current_user, external_video - ) + return await create_external_video_activity(request, current_user, external_video) @router.post("/documentpdf") diff --git a/src/routers/users.py b/src/routers/users.py index 80b90753..3b4edd1a 100644 --- a/src/routers/users.py +++ b/src/routers/users.py @@ -1,5 +1,5 @@ -from fastapi import Depends, APIRouter -from src.security.auth import * +from fastapi import Depends, APIRouter, Request +from src.security.auth import get_current_user from src.services.users.schemas.users import PasswordChangeForm, PublicUser, User, UserWithPassword from src.services.users.users import create_user, delete_user, get_profile_metadata, get_user_by_userid, update_user, update_user_password diff --git a/src/security/auth.py b/src/security/auth.py index a4edaf49..aa811d3f 100644 --- a/src/security/auth.py +++ b/src/security/auth.py @@ -1,13 +1,13 @@ from webbrowser import get from config.config import get_learnhouse_config from pydantic import BaseModel -from fastapi import Depends, HTTPException, status +from fastapi import Depends, HTTPException, Request, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt from datetime import datetime, timedelta -from src.services.users.schemas.users import AnonymousUser -from src.services.users.users import * -from src.security.security import * +from src.services.users.schemas.users import AnonymousUser, PublicUser +from src.services.users.users import security_get_user, security_verify_password +from src.security.security import ALGORITHM, SECRET_KEY, verify_user_rights_with_roles from fastapi_jwt_auth import AuthJWT oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login") diff --git a/src/services/courses/collections.py b/src/services/courses/collections.py index aabd502c..f07c97dc 100644 --- a/src/services/courses/collections.py +++ b/src/services/courses/collections.py @@ -34,7 +34,9 @@ async def get_collection( collection = await collections.find_one({"collection_id": collection_id}) # verify collection rights - await verify_collection_rights(request, collection_id, current_user, "read") + await verify_collection_rights( + request, collection_id, current_user, "read", collection["org_id"] + ) if not collection: raise HTTPException( @@ -99,12 +101,15 @@ async def update_collection( current_user: PublicUser, ): # verify collection rights - await verify_collection_rights(request, collection_id, current_user, "update") collections = request.app.db["collections"] collection = await collections.find_one({"collection_id": collection_id}) + await verify_collection_rights( + request, collection_id, current_user, "update", collection["org_id"] + ) + if not collection: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist" @@ -124,12 +129,14 @@ async def update_collection( async def delete_collection( request: Request, collection_id: str, current_user: PublicUser ): - await verify_collection_rights(request, collection_id, current_user, "delete") - collections = request.app.db["collections"] collection = await collections.find_one({"collection_id": collection_id}) + await verify_collection_rights( + request, collection_id, current_user, "delete", collection["org_id"] + ) + if not collection: raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist" @@ -160,6 +167,8 @@ async def get_collections( ): collections = request.app.db["collections"] + print(org_id) + # get all collections from database without ObjectId all_collections = ( collections.find({"org_id": org_id}) @@ -168,7 +177,7 @@ async def get_collections( .limit(limit) ) - await verify_collection_rights(request, "*", current_user, "read") + await verify_collection_rights(request, "*", current_user, "read", org_id) # create list of collections and include courses in each collection collections_list = [] @@ -195,19 +204,27 @@ async def get_collections( async def verify_collection_rights( - request: Request, collection_id: str, current_user: PublicUser, action: str + request: Request, + collection_id: str, + current_user: PublicUser, + action: str, + org_id: str, ): collections = request.app.db["collections"] collection = await collections.find_one({"collection_id": collection_id}) - if not collection and action != "create": + if not collection and action != "create" and collection_id != "*": raise HTTPException( status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist" ) + # Collections are public by default for now + if current_user.user_id == "anonymous" and action == "read": + return True + hasRoleRights = await verify_user_rights_with_roles( - request, action, current_user.user_id, collection_id, collection["org_id"] + request, action, current_user.user_id, collection_id, org_id ) if not hasRoleRights: diff --git a/src/services/courses/courses.py b/src/services/courses/courses.py index 1be805f4..bd6f2786 100644 --- a/src/services/courses/courses.py +++ b/src/services/courses/courses.py @@ -6,8 +6,8 @@ from src.services.courses.activities.activities import ActivityInDB from src.services.courses.thumbnails import upload_thumbnail from src.services.users.schemas.users import AnonymousUser from src.services.users.users import PublicUser -from src.security.security import * -from fastapi import HTTPException, status, UploadFile +from src.security.security import verify_user_rights_with_roles +from fastapi import HTTPException, Request, status, UploadFile from datetime import datetime #### Classes #################################################### diff --git a/src/services/orgs/orgs.py b/src/services/orgs/orgs.py index 8fc78b83..bb54077f 100644 --- a/src/services/orgs/orgs.py +++ b/src/services/orgs/orgs.py @@ -9,7 +9,7 @@ from src.services.orgs.schemas.orgs import ( ) from src.services.users.schemas.users import UserOrganization from src.services.users.users import PublicUser -from src.security.security import * +from src.security.security import verify_user_rights_with_roles from fastapi import HTTPException, UploadFile, status, Request @@ -103,7 +103,6 @@ async def update_org( # update org await orgs.update_one({"org_id": org_id}, {"$set": updated_org.dict()}) - return updated_org.dict() @@ -117,17 +116,13 @@ async def update_org_logo( org = await orgs.find_one({"org_id": org_id}) - name_in_disk = await upload_org_logo(logo_file) - # update org + # update org org = await orgs.update_one({"org_id": org_id}, {"$set": {"logo": name_in_disk}}) - + return {"detail": "Logo updated"} - - - async def delete_org(request: Request, org_id: str, current_user: PublicUser): await verify_org_rights(request, org_id, current_user, "delete") diff --git a/src/services/orgs/schemas/orgs.py b/src/services/orgs/schemas/orgs.py index 82da07e2..56531357 100644 --- a/src/services/orgs/schemas/orgs.py +++ b/src/services/orgs/schemas/orgs.py @@ -1,6 +1,5 @@ from typing import Optional from pydantic import BaseModel -from src.security.security import * #### Classes #################################################### diff --git a/src/services/roles/roles.py b/src/services/roles/roles.py index 51b1e01b..2ef3ee34 100644 --- a/src/services/roles/roles.py +++ b/src/services/roles/roles.py @@ -2,7 +2,6 @@ from typing import Literal from uuid import uuid4 from src.services.roles.schemas.roles import Role, RoleInDB from src.services.users.schemas.users import PublicUser -from src.security.security import * from fastapi import HTTPException, status, Request from datetime import datetime @@ -12,7 +11,6 @@ async def create_role(request: Request, role_object: Role, current_user: PublicU await verify_user_permissions_on_roles(request, current_user, "create", None) - # create the role object in the database and return the object role_id = "role_" + str(uuid4()) @@ -27,6 +25,7 @@ async def create_role(request: Request, role_object: Role, current_user: PublicU return role + async def read_role(request: Request, role_id: str, current_user: PublicUser): roles = request.app.db["roles"] @@ -36,7 +35,10 @@ async def read_role(request: Request, role_id: str, current_user: PublicUser): return role -async def update_role(request: Request, role_id: str, role_object: Role, current_user: PublicUser): + +async def update_role( + request: Request, role_id: str, role_object: Role, current_user: PublicUser +): roles = request.app.db["roles"] await verify_user_permissions_on_roles(request, current_user, "update", role_id) @@ -44,10 +46,15 @@ async def update_role(request: Request, role_id: str, role_object: Role, current role_object.updated_at = datetime.now() # Update the role object in the database and return the object - updated_role = RoleInDB(**await roles.find_one_and_update({"role_id": role_id}, {"$set": role_object.dict()}, return_document=True)) + updated_role = RoleInDB( + **await roles.find_one_and_update( + {"role_id": role_id}, {"$set": role_object.dict()}, return_document=True + ) + ) return updated_role + async def delete_role(request: Request, role_id: str, current_user: PublicUser): roles = request.app.db["roles"] @@ -58,9 +65,16 @@ async def delete_role(request: Request, role_id: str, current_user: PublicUser): return deleted_role + #### Security #################################################### -async def verify_user_permissions_on_roles(request: Request, current_user: PublicUser, action: Literal["create", "read", "update", "delete"], role_id: str | None): + +async def verify_user_permissions_on_roles( + request: Request, + current_user: PublicUser, + action: Literal["create", "read", "update", "delete"], + role_id: str | None, +): request.app.db["users"] roles = request.app.db["roles"] @@ -68,7 +82,8 @@ async def verify_user_permissions_on_roles(request: Request, current_user: Publi if not current_user: raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, detail="Roles : Not authenticated") + status_code=status.HTTP_401_UNAUTHORIZED, detail="Roles : Not authenticated" + ) if action == "create": if "owner" in [org.org_role for org in current_user.orgs]: