Merge pull request #95 from learnhouse/swve/eng-79-collections-should-be-org-scoped

Make Collections org-scoped
This commit is contained in:
Badr B 2023-06-19 18:00:37 +02:00 committed by GitHub
commit 7e680c7dbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 215 additions and 116 deletions

View file

@ -1,4 +1,3 @@
from calendar import c
from typing import Optional
from pydantic import BaseModel
import os
@ -51,7 +50,7 @@ def get_learnhouse_config() -> LearnHouseConfig:
env_site_description = os.environ.get("LEARNHOUSE_SITE_DESCRIPTION")
env_contact_email = os.environ.get("LEARNHOUSE_CONTACT_EMAIL")
env_domain = os.environ.get("LEARNHOUSE_DOMAIN")
env_port = os.environ.get("LEARNHOUSE_PORT")
os.environ.get("LEARNHOUSE_PORT")
env_ssl = os.environ.get("LEARNHOUSE_SSL")
env_use_default_org = os.environ.get("LEARNHOUSE_USE_DEFAULT_ORG")
env_allowed_origins = os.environ.get("LEARNHOUSE_ALLOWED_ORIGINS")

View file

@ -9,10 +9,7 @@ import React from 'react'
const CollectionAdminEditsArea = (props: any) => {
const org_roles_values = ["admin", "owner"];
const user_roles_values = ["role_admin"];
console.log("props: ", props);
const auth: any = React.useContext(AuthContext);
console.log("auth: ", auth);
// this is amazingly terrible code, but gotta release that MVP

View file

@ -22,7 +22,6 @@ export async function generateMetadata(
const access_token_cookie: any = cookieStore.get('access_token_cookie');
// Get Org context information
const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] });
return {
title: `Collections — ${org.name}`,
description: `Collections of courses from ${org.name}`,
@ -38,10 +37,9 @@ const CollectionsPage = async (params: any) => {
const cookieStore = cookies();
const access_token_cookie: any = cookieStore.get('access_token_cookie');
const orgslug = params.params.orgslug;
const collections = await getOrgCollectionsWithAuthHeader(access_token_cookie ? access_token_cookie.value : null);
const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] });
const org_id = org.org_id;
const collections = await getOrgCollectionsWithAuthHeader(org_id, access_token_cookie ? access_token_cookie.value : null);
return (
<div className="max-w-7xl mx-auto px-4 py-10" >

View file

@ -35,7 +35,8 @@ const OrgHomePage = async (params: any) => {
const access_token_cookie: any = cookieStore.get('access_token_cookie');
const courses = await getOrgCoursesWithAuthHeader(orgslug, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null);
const collections = await getOrgCollectionsWithAuthHeader(access_token_cookie ? access_token_cookie.value : null);
const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] });
const collections = await getOrgCollectionsWithAuthHeader(org.org_id, access_token_cookie ? access_token_cookie.value : null);
// function to remove "course_" from the course_id

View file

@ -44,7 +44,7 @@
},
"devDependencies": {
"@types/node": "18.7.18",
"@types/react": "18.0.20",
"@types/react": "18.2.8",
"@types/react-beautiful-dnd": "^13.1.2",
"@types/react-dom": "18.0.6",
"@types/react-katex": "^3.0.0",
@ -55,7 +55,7 @@
"eslint-config-next": "^13.0.6",
"postcss": "^8.4.23",
"tailwindcss": "^3.3.2",
"typescript": "4.8.3"
"typescript": "5.1.3"
}
},
"node_modules/@alloc/quick-lru": {
@ -3599,9 +3599,9 @@
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
},
"node_modules/@types/react": {
"version": "18.0.20",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.20.tgz",
"integrity": "sha512-MWul1teSPxujEHVwZl4a5HxQ9vVNsjTchVA+xRqv/VYGCuKGAU6UhfrTdF5aBefwD1BHUD8i/zq+O/vyCm/FrA==",
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.8.tgz",
"integrity": "sha512-lTyWUNrd8ntVkqycEEplasWy2OxNlShj3zqS0LuB1ENUGis5HodmhM7DtCoUGbxj3VW/WsGA0DUhpG6XrM7gPA==",
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@ -8181,16 +8181,16 @@
}
},
"node_modules/typescript": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
"integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==",
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz",
"integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=4.2.0"
"node": ">=14.17"
}
},
"node_modules/unbox-primitive": {
@ -11084,9 +11084,9 @@
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w=="
},
"@types/react": {
"version": "18.0.20",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.20.tgz",
"integrity": "sha512-MWul1teSPxujEHVwZl4a5HxQ9vVNsjTchVA+xRqv/VYGCuKGAU6UhfrTdF5aBefwD1BHUD8i/zq+O/vyCm/FrA==",
"version": "18.2.8",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.8.tgz",
"integrity": "sha512-lTyWUNrd8ntVkqycEEplasWy2OxNlShj3zqS0LuB1ENUGis5HodmhM7DtCoUGbxj3VW/WsGA0DUhpG6XrM7gPA==",
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@ -14341,9 +14341,9 @@
"dev": true
},
"typescript": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz",
"integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==",
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz",
"integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==",
"dev": true
},
"unbox-primitive": {

View file

@ -45,7 +45,7 @@
},
"devDependencies": {
"@types/node": "18.7.18",
"@types/react": "18.0.20",
"@types/react": "18.2.8",
"@types/react-beautiful-dnd": "^13.1.2",
"@types/react-dom": "18.0.6",
"@types/react-katex": "^3.0.0",
@ -56,6 +56,6 @@
"eslint-config-next": "^13.0.6",
"postcss": "^8.4.23",
"tailwindcss": "^3.3.2",
"typescript": "4.8.3"
"typescript": "5.1.3"
}
}

View file

@ -19,7 +19,6 @@ export async function createCollection(collection: any) {
return res;
}
// Get a colletion by id
export async function getCollectionById(collection_id: any) {
const result: any = await fetch(`${getAPIUrl()}collections/${collection_id}`, { next: { revalidate: 10 } });
@ -41,8 +40,8 @@ export async function getOrgCollections() {
return res;
}
export async function getOrgCollectionsWithAuthHeader(access_token: string) {
const result: any = await fetch(`${getAPIUrl()}collections/page/1/limit/10`, RequestBodyWithAuthHeader("GET", null, { revalidate: 3 }, access_token));
export async function getOrgCollectionsWithAuthHeader(org_id: string, access_token: string) {
const result: any = await fetch(`${getAPIUrl()}collections/org_id/${org_id}/page/1/limit/10`, RequestBodyWithAuthHeader("GET", null, { revalidate: 3 }, access_token));
const res = await errorHandling(result);
return res;
}

View file

@ -1,8 +1,7 @@
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()

View file

@ -1,6 +1,5 @@
from fastapi import APIRouter, Depends, UploadFile, Form, Request
from src.security.auth import get_current_user
from fastapi import UploadFile
from src.services.blocks.block_types.imageBlock.images import create_image_block, get_image_block
from src.services.blocks.block_types.videoBlock.videoBlock import create_video_block, get_video_block
from src.services.blocks.block_types.pdfBlock.pdfBlock import create_pdf_block, get_pdf_block

View file

@ -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")

View file

@ -1,14 +1,25 @@
from fastapi import APIRouter, Depends, Request
from src.security.auth import get_current_user
from src.services.users.users import PublicUser
from src.services.courses.collections import Collection, create_collection, get_collection, get_collections, update_collection, delete_collection
from src.services.courses.collections import (
Collection,
create_collection,
get_collection,
get_collections,
update_collection,
delete_collection,
)
router = APIRouter()
@router.post("/")
async def api_create_collection(request: Request,collection_object: Collection, current_user: PublicUser = Depends(get_current_user)):
async def api_create_collection(
request: Request,
collection_object: Collection,
current_user: PublicUser = Depends(get_current_user),
):
"""
Create new Collection
"""
@ -16,31 +27,52 @@ async def api_create_collection(request: Request,collection_object: Collection,
@router.get("/{collection_id}")
async def api_get_collection(request: Request,collection_id: str, current_user: PublicUser = Depends(get_current_user)):
async def api_get_collection(
request: Request,
collection_id: str,
current_user: PublicUser = Depends(get_current_user),
):
"""
Get single collection by ID
"""
return await get_collection(request, collection_id, current_user)
@router.get("/page/{page}/limit/{limit}")
async def api_get_collections_by(request: Request,page: int, limit: int, current_user: PublicUser = Depends(get_current_user)):
@router.get("/org_id/{org_id}/page/{page}/limit/{limit}")
async def api_get_collections_by(
request: Request,
page: int,
limit: int,
org_id: str,
current_user: PublicUser = Depends(get_current_user),
):
"""
Get collections by page and limit
"""
return await get_collections(request, page, limit)
return await get_collections(request, org_id, current_user, page, limit)
@router.put("/{collection_id}")
async def api_update_collection(request: Request,collection_object: Collection, collection_id: str, current_user: PublicUser = Depends(get_current_user)):
async def api_update_collection(
request: Request,
collection_object: Collection,
collection_id: str,
current_user: PublicUser = Depends(get_current_user),
):
"""
Update collection by ID
"""
return await update_collection(request, collection_object, collection_id, current_user)
return await update_collection(
request, collection_object, collection_id, current_user
)
@router.delete("/{collection_id}")
async def api_delete_collection(request: Request,collection_id: str, current_user: PublicUser = Depends(get_current_user)):
async def api_delete_collection(
request: Request,
collection_id: str,
current_user: PublicUser = Depends(get_current_user),
):
"""
Delete collection by ID
"""

View file

@ -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

View file

@ -1,13 +1,12 @@
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
from fastapi_jwt_auth import AuthJWT
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login")

View file

@ -2,7 +2,7 @@ from typing import List
from uuid import uuid4
from pydantic import BaseModel
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, status, Request
#### Classes ####################################################
@ -25,17 +25,23 @@ class CollectionInDB(Collection):
# CRUD
####################################################
async def get_collection(request: Request,collection_id: str, current_user: PublicUser):
async def get_collection(
request: Request, collection_id: str, current_user: PublicUser
):
collections = request.app.db["collections"]
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(
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist")
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist"
)
collection = Collection(**collection)
@ -44,77 +50,97 @@ async def get_collection(request: Request,collection_id: str, current_user: Publ
courseids = [course for course in collection.courses]
collection.courses = []
collection.courses = courses.find(
{"course_id": {"$in": courseids}}, {'_id': 0})
collection.courses = [course for course in await collection.courses.to_list(length=100)]
collection.courses = courses.find({"course_id": {"$in": courseids}}, {"_id": 0})
collection.courses = [
course for course in await collection.courses.to_list(length=100)
]
return collection
async def create_collection(request: Request,collection_object: Collection, current_user: PublicUser):
async def create_collection(
request: Request, collection_object: Collection, current_user: PublicUser
):
collections = request.app.db["collections"]
# find if collection already exists using name
isCollectionNameAvailable = await collections.find_one(
{"name": collection_object.name})
{"name": collection_object.name}
)
# TODO
# await verify_collection_rights("*", current_user, "create")
if isCollectionNameAvailable:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Collection name already exists")
status_code=status.HTTP_409_CONFLICT,
detail="Collection name already exists",
)
# generate collection_id with uuid4
collection_id = str(f"collection_{uuid4()}")
collection = CollectionInDB(
collection_id=collection_id, **collection_object.dict())
collection = CollectionInDB(collection_id=collection_id, **collection_object.dict())
collection_in_db = await collections.insert_one(collection.dict())
if not collection_in_db:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Unavailable database",
)
return collection.dict()
async def update_collection(request: Request,collection_object: Collection, collection_id: str, current_user: PublicUser):
async def update_collection(
request: Request,
collection_object: Collection,
collection_id: str,
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")
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist"
)
updated_collection = CollectionInDB(
collection_id=collection_id, **collection_object.dict())
collection_id=collection_id, **collection_object.dict()
)
await collections.update_one({"collection_id": collection_id}, {
"$set": updated_collection.dict()})
await collections.update_one(
{"collection_id": collection_id}, {"$set": updated_collection.dict()}
)
return Collection(**updated_collection.dict())
async def delete_collection(request: Request,collection_id: str, current_user: PublicUser):
await verify_collection_rights(request, collection_id, current_user, "delete")
async def delete_collection(
request: Request, collection_id: str, current_user: PublicUser
):
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")
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist"
)
isDeleted = await collections.delete_one({"collection_id": collection_id})
@ -122,20 +148,36 @@ async def delete_collection(request: Request,collection_id: str, current_user: P
return {"detail": "collection deleted"}
else:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Unavailable database",
)
####################################################
# Misc
####################################################
async def get_collections(request: Request,page: int = 1, limit: int = 10):
## TODO : auth
async def get_collections(
request: Request,
org_id: str,
current_user: PublicUser,
page: int = 1,
limit: int = 10,
):
collections = request.app.db["collections"]
print(org_id)
# get all collections from database without ObjectId
all_collections = collections.find({}).sort(
"name", 1).skip(10 * (page - 1)).limit(limit)
all_collections = (
collections.find({"org_id": org_id})
.sort("name", 1)
.skip(10 * (page - 1))
.limit(limit)
)
await verify_collection_rights(request, "*", current_user, "read", org_id)
# create list of collections and include courses in each collection
collections_list = []
@ -148,30 +190,50 @@ async def get_collections(request: Request,page: int = 1, limit: int = 10):
courses = request.app.db["courses"]
collection.courses = []
collection.courses = courses.find(
{"course_id": {"$in": collection_courses}}, {'_id': 0})
{"course_id": {"$in": collection_courses}}, {"_id": 0}
)
collection.courses = [course for course in await collection.courses.to_list(length=100)]
collection.courses = [
course for course in await collection.courses.to_list(length=100)
]
return collections_list
#### Security ####################################################
async def verify_collection_rights(request: Request,collection_id: str, current_user: PublicUser, action: str):
async def verify_collection_rights(
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")
status_code=status.HTTP_409_CONFLICT, detail="Collection does not exist"
)
hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, collection_id, collection["org_id"])
# 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, org_id
)
if not hasRoleRights:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="You do not have rights to this Collection")
status_code=status.HTTP_403_FORBIDDEN,
detail="You do not have rights to this Collection",
)
return True
#### Security ####################################################

View file

@ -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 ####################################################

View file

@ -1,5 +1,4 @@
import json
from typing import Optional
from uuid import uuid4
from src.services.orgs.logos import upload_org_logo
from src.services.orgs.schemas.orgs import (
@ -9,7 +8,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
@ -96,14 +95,13 @@ async def update_org(
orgs = request.app.db["organizations"]
org = await orgs.find_one({"org_id": org_id})
await orgs.find_one({"org_id": org_id})
updated_org = OrganizationInDB(org_id=org_id, **org_object.dict())
# update org
await orgs.update_one({"org_id": org_id}, {"$set": updated_org.dict()})
return updated_org.dict()
@ -115,20 +113,16 @@ async def update_org_logo(
orgs = request.app.db["organizations"]
org = await orgs.find_one({"org_id": org_id})
await orgs.find_one({"org_id": org_id})
name_in_disk = await upload_org_logo(logo_file)
# update org
org = await orgs.update_one({"org_id": org_id}, {"$set": {"logo": name_in_disk}})
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")

View file

@ -1,6 +1,5 @@
from typing import Optional
from pydantic import BaseModel
from src.security.security import *
#### Classes ####################################################

View file

@ -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]: