Merge pull request #59 from learnhouse/feat/roles-remake

feat: init roles & user reengineering
This commit is contained in:
Badr B 2023-03-23 21:20:10 +01:00 committed by GitHub
commit e1d4c4c594
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 856 additions and 738 deletions

10
app.py
View file

@ -8,7 +8,7 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi_jwt_auth.exceptions import AuthJWTException from fastapi_jwt_auth.exceptions import AuthJWTException
from src.services.mocks.initial import create_initial_data # from src.services.mocks.initial import create_initial_data
######################## ########################
# Pre-Alpha Version 0.1.0 # Pre-Alpha Version 0.1.0
@ -64,8 +64,8 @@ async def root():
return {"Message": "Welcome to LearnHouse ✨"} return {"Message": "Welcome to LearnHouse ✨"}
@app.get("/initial_data") # @app.get("/initial_data")
async def initial_data(request: Request): # async def initial_data(request: Request):
await create_initial_data(request) # await create_initial_data(request)
return {"Message": "Initial data created 🤖"} # return {"Message": "Initial data created 🤖"}

View file

@ -13,6 +13,7 @@ import { useRouter } from "next/navigation";
import NewChapterModal from "@components/Modals/CourseEdit/NewChapter"; import NewChapterModal from "@components/Modals/CourseEdit/NewChapter";
import NewLectureModal from "@components/Modals/CourseEdit/NewLecture"; import NewLectureModal from "@components/Modals/CourseEdit/NewLecture";
import { createLecture, createFileLecture } from "@services/courses/lectures"; import { createLecture, createFileLecture } from "@services/courses/lectures";
import { getOrganizationContextInfo } from "@services/orgs";
function CourseEdit(params: any) { function CourseEdit(params: any) {
const router = useRouter(); const router = useRouter();
@ -31,6 +32,8 @@ function CourseEdit(params: any) {
const courseid = params.params.courseid; const courseid = params.params.courseid;
const orgslug = params.params.orgslug; const orgslug = params.params.orgslug;
async function getCourseChapters() { async function getCourseChapters() {
const courseChapters = await getCourseChaptersMetadata(courseid); const courseChapters = await getCourseChaptersMetadata(courseid);
setData(courseChapters); setData(courseChapters);
@ -75,8 +78,9 @@ function CourseEdit(params: any) {
// Submit new lecture // Submit new lecture
const submitLecture = async (lecture: any) => { const submitLecture = async (lecture: any) => {
console.log("submitLecture", lecture); console.log("submitLecture", lecture);
let org = await getOrganizationContextInfo(orgslug);
await updateChaptersMetadata(courseid, data); await updateChaptersMetadata(courseid, data);
await createLecture(lecture, lecture.chapterId); await createLecture(lecture, lecture.chapterId, org.org_id);
await getCourseChapters(); await getCourseChapters();
setNewLectureModal(false); setNewLectureModal(false);
}; };
@ -226,63 +230,63 @@ function CourseEdit(params: any) {
return ( return (
<> <>
<Page> <Page>
<Title> <Title>
Edit Course {" "} Edit Course {" "}
<button <button
onClick={() => { onClick={() => {
setNewChapterModal(true); setNewChapterModal(true);
}} }}
> >
Add chapter + Add chapter +
</button> </button>
<button <button
onClick={() => { onClick={() => {
updateChapters(); updateChapters();
}} }}
> >
Save Save
</button> </button>
</Title> </Title>
{newChapterModal && <NewChapterModal closeModal={closeNewChapterModal} submitChapter={submitChapter}></NewChapterModal>} {newChapterModal && <NewChapterModal closeModal={closeNewChapterModal} submitChapter={submitChapter}></NewChapterModal>}
{newLectureModal && ( {newLectureModal && (
<NewLectureModal <NewLectureModal
closeModal={closeNewLectureModal} closeModal={closeNewLectureModal}
submitFileLecture={submitFileLecture} submitFileLecture={submitFileLecture}
submitLecture={submitLecture} submitLecture={submitLecture}
chapterId={newLectureModalData} chapterId={newLectureModalData}
></NewLectureModal> ></NewLectureModal>
)} )}
<br /> <br />
{winReady && ( {winReady && (
<ChapterlistWrapper> <ChapterlistWrapper>
<DragDropContext onDragEnd={onDragEnd}> <DragDropContext onDragEnd={onDragEnd}>
<Droppable key="chapters" droppableId="chapters" type="chapter"> <Droppable key="chapters" droppableId="chapters" type="chapter">
{(provided) => ( {(provided) => (
<> <>
<div key={"chapters"} {...provided.droppableProps} ref={provided.innerRef}> <div key={"chapters"} {...provided.droppableProps} ref={provided.innerRef}>
{getChapters().map((info: any, index: any) => ( {getChapters().map((info: any, index: any) => (
<> <>
<Chapter <Chapter
orgslug={orgslug} orgslug={orgslug}
courseid={courseid} courseid={courseid}
openNewLectureModal={openNewLectureModal} openNewLectureModal={openNewLectureModal}
deleteChapter={deleteChapterUI} deleteChapter={deleteChapterUI}
key={index} key={index}
info={info} info={info}
index={index} index={index}
></Chapter> ></Chapter>
</> </>
))} ))}
{provided.placeholder} {provided.placeholder}
</div> </div>
</> </>
)} )}
</Droppable> </Droppable>
</DragDropContext> </DragDropContext>
</ChapterlistWrapper> </ChapterlistWrapper>
)} )}
</Page> </Page>
</> </>
); );

View file

@ -1,11 +1,12 @@
"use client"; "use client";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import React from "react"; import React from "react";
import { Header } from "../../../../../components/UI/Header"; import { Header } from "@components/UI/Header";
import Layout from "../../../../../components/UI/Layout"; import Layout from "@components/UI/Layout";
import { Title } from "../../../../../components/UI/Elements/Styles/Title"; import { Title } from "@components/UI/Elements/Styles/Title";
import { createNewCourse } from "../../../../../services/courses/courses"; import { createNewCourse } from "@services/courses/courses";
import { getOrganizationContextInfo } from "../../../../../services/orgs"; import { getOrganizationContextInfo } from "@services/orgs";
import { getUriWithOrg } from "@services/config";
const NewCoursePage = (params: any) => { const NewCoursePage = (params: any) => {
const router = useRouter(); const router = useRouter();
@ -40,7 +41,7 @@ const NewCoursePage = (params: any) => {
// TODO : wow this is terrible - fix this // TODO : wow this is terrible - fix this
if (status.org_id == orgId) { if (status.org_id == orgId) {
router.push(`/org/${orgslug}/courses`); router.push(getUriWithOrg(orgslug, `/courses`));
} else { } else {
alert("Error creating course, please see console logs"); alert("Error creating course, please see console logs");
console.log(status); console.log(status);

View file

@ -1,13 +1,13 @@
import { getAPIUrl } from "@services/config"; import { getAPIUrl } from "@services/config";
import { RequestBody, RequestBodyForm } from "@services/utils/requests"; import { RequestBody, RequestBodyForm } from "@services/utils/requests";
export async function createLecture(data: any, chapter_id: any) { export async function createLecture(data: any, chapter_id: any, org_id: any) {
data.content = {}; data.content = {};
// remove chapter_id from data // remove chapter_id from data
delete data.chapterId; delete data.chapterId;
const result: any = await fetch(`${getAPIUrl()}lectures/?coursechapter_id=${chapter_id}`, RequestBody("POST", data)) const result: any = await fetch(`${getAPIUrl()}lectures/?coursechapter_id=${chapter_id}&org_id=${org_id}`, RequestBody("POST", data))
.then((result) => result.json()) .then((result) => result.json())
.catch((error) => console.log("error", error)); .catch((error) => console.log("error", error));

View file

@ -4,7 +4,7 @@ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from passlib.context import CryptContext from passlib.context import CryptContext
from jose import JWTError, jwt from jose import JWTError, jwt
from datetime import datetime, timedelta from datetime import datetime, timedelta
from src.services.users import * from src.services.users.users import *
from fastapi import Cookie, FastAPI from fastapi import Cookie, FastAPI
from src.services.security import * from src.services.security import *
from fastapi_jwt_auth import AuthJWT from fastapi_jwt_auth import AuthJWT

View file

@ -2,7 +2,7 @@ from urllib.request import Request
from fastapi import Depends, APIRouter, HTTPException, status, Request from fastapi import Depends, APIRouter, HTTPException, status, Request
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
from src.dependencies.auth import * from src.dependencies.auth import *
from src.services.users import * from src.services.users.users import *
from datetime import timedelta from datetime import timedelta
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse

View file

@ -5,7 +5,7 @@ from src.services.blocks.imageBlock.images import create_image_file, get_image_f
from src.services.blocks.videoBlock.videos import create_video_file, get_video_file from src.services.blocks.videoBlock.videos import create_video_file, get_video_file
from src.services.blocks.pdfBlock.documents import create_document_file, get_document_file from src.services.blocks.pdfBlock.documents import create_document_file, get_document_file
from src.services.blocks.quizBlock.quizBlock import create_quiz_block, get_quiz_block_answers, get_quiz_block_options, quizBlock from src.services.blocks.quizBlock.quizBlock import create_quiz_block, get_quiz_block_answers, get_quiz_block_options, quizBlock
from src.services.users import PublicUser from src.services.users.users import PublicUser
router = APIRouter() router = APIRouter()

View file

@ -1,7 +1,7 @@
from fastapi import APIRouter, Depends, Request, UploadFile, Form from fastapi import APIRouter, Depends, Request, UploadFile, Form
from src.services.courses.chapters import CourseChapter, CourseChapterMetaData, create_coursechapter, delete_coursechapter, get_coursechapter, get_coursechapters, get_coursechapters_meta, update_coursechapter, update_coursechapters_meta from src.services.courses.chapters import CourseChapter, CourseChapterMetaData, create_coursechapter, delete_coursechapter, get_coursechapter, get_coursechapters, get_coursechapters_meta, update_coursechapter, update_coursechapters_meta
from src.services.users import PublicUser from src.services.users.users import PublicUser
from src.dependencies.auth import get_current_user from src.dependencies.auth import get_current_user
router = APIRouter() router = APIRouter()

View file

@ -1,6 +1,6 @@
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Request
from src.dependencies.auth import get_current_user from src.dependencies.auth import get_current_user
from src.services.users import PublicUser, User from src.services.users.users import PublicUser, User
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

View file

@ -2,14 +2,14 @@ from fastapi import APIRouter, Depends, UploadFile, Form, Request
from src.dependencies.auth import get_current_user from src.dependencies.auth import get_current_user
from src.services.courses.courses import Course, create_course, get_course, get_course_meta, get_courses, get_courses_orgslug, update_course, delete_course, update_course_thumbnail from src.services.courses.courses import Course, create_course, get_course, get_course_meta, get_courses, get_courses_orgslug, update_course, delete_course, update_course_thumbnail
from src.services.users import PublicUser from src.services.users.users import PublicUser
router = APIRouter() router = APIRouter()
@router.post("/") @router.post("/")
async def api_create_course(request: Request,org_id: str, name: str = Form(), mini_description: str = Form(), description: str = Form(), public: bool = Form(), current_user: PublicUser = Depends(get_current_user), thumbnail: UploadFile | None = None): async def api_create_course(request: Request, org_id: str, name: str = Form(), mini_description: str = Form(), description: str = Form(), public: bool = Form(), current_user: PublicUser = Depends(get_current_user), thumbnail: UploadFile | None = None):
""" """
Create new Course Create new Course
""" """
@ -19,7 +19,7 @@ async def api_create_course(request: Request,org_id: str, name: str = Form(), mi
@router.put("/thumbnail/{course_id}") @router.put("/thumbnail/{course_id}")
async def api_create_course_thumbnail(request: Request,course_id: str, thumbnail: UploadFile | None = None, current_user: PublicUser = Depends(get_current_user)): async def api_create_course_thumbnail(request: Request, course_id: str, thumbnail: UploadFile | None = None, current_user: PublicUser = Depends(get_current_user)):
""" """
Update new Course Thumbnail Update new Course Thumbnail
""" """
@ -27,7 +27,7 @@ async def api_create_course_thumbnail(request: Request,course_id: str, thumbnail
@router.get("/{course_id}") @router.get("/{course_id}")
async def api_get_course(request: Request,course_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_get_course(request: Request, course_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Get single Course by course_id Get single Course by course_id
""" """
@ -35,7 +35,7 @@ async def api_get_course(request: Request,course_id: str, current_user: PublicU
@router.get("/meta/{course_id}") @router.get("/meta/{course_id}")
async def api_get_course_meta(request: Request,course_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_get_course_meta(request: Request, course_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Get single Course Metadata (chapters, lectures) by course_id Get single Course Metadata (chapters, lectures) by course_id
""" """
@ -43,30 +43,31 @@ async def api_get_course_meta(request: Request,course_id: str, current_user: Pu
@router.get("/org_id/{org_id}/page/{page}/limit/{limit}") @router.get("/org_id/{org_id}/page/{page}/limit/{limit}")
async def api_get_course_by(request: Request,page: int, limit: int, org_id: str): async def api_get_course_by(request: Request, page: int, limit: int, org_id: str):
""" """
Get houses by page and limit Get houses by page and limit
""" """
return await get_courses(request,page, limit, org_id) return await get_courses(request, page, limit, org_id)
@router.get("/org_slug/{org_slug}/page/{page}/limit/{limit}") @router.get("/org_slug/{org_slug}/page/{page}/limit/{limit}")
async def api_get_course_by_orgslug(request: Request,page: int, limit: int, org_slug: str): async def api_get_course_by_orgslug(request: Request, page: int, limit: int, org_slug: str):
""" """
Get houses by page and limit Get houses by page and limit
""" """
return await get_courses_orgslug(request,page, limit, org_slug) return await get_courses_orgslug(request, page, limit, org_slug)
@router.put("/{course_id}") @router.put("/{course_id}")
async def api_update_course(request: Request,course_object: Course, course_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_update_course(request: Request, course_object: Course, course_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Update Course by course_id Update Course by course_id
""" """
return await update_course(request,course_object, course_id, current_user) return await update_course(request, course_object, course_id, current_user)
@router.delete("/{course_id}") @router.delete("/{course_id}")
async def api_delete_course(request: Request,course_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_delete_course(request: Request, course_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Delete Course by ID Delete Course by ID
""" """

View file

@ -7,15 +7,15 @@ router = APIRouter()
@router.post("/") @router.post("/")
async def api_create_lecture(request: Request,lecture_object: Lecture, coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_create_lecture(request: Request, lecture_object: Lecture, org_id: str, coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Create new lecture Create new lecture
""" """
return await create_lecture(request,lecture_object, coursechapter_id, current_user) return await create_lecture(request, lecture_object, org_id, coursechapter_id, current_user)
@router.get("/{lecture_id}") @router.get("/{lecture_id}")
async def api_get_lecture(request: Request,lecture_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_get_lecture(request: Request, lecture_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Get single lecture by lecture_id Get single lecture by lecture_id
""" """
@ -23,32 +23,34 @@ async def api_get_lecture(request: Request,lecture_id: str, current_user: Public
@router.get("/coursechapter/{coursechapter_id}") @router.get("/coursechapter/{coursechapter_id}")
async def api_get_lectures(request: Request,coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_get_lectures(request: Request, coursechapter_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Get CourseChapter lectures Get CourseChapter lectures
""" """
return await get_lectures(request,coursechapter_id, current_user) return await get_lectures(request, coursechapter_id, current_user)
@router.put("/{lecture_id}") @router.put("/{lecture_id}")
async def api_update_lecture(request: Request,lecture_object: Lecture, lecture_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_update_lecture(request: Request, lecture_object: Lecture, lecture_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Update lecture by lecture_id Update lecture by lecture_id
""" """
return await update_lecture(request,lecture_object, lecture_id, current_user) return await update_lecture(request, lecture_object, lecture_id, current_user)
@router.delete("/{lecture_id}") @router.delete("/{lecture_id}")
async def api_delete_lecture(request: Request,lecture_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_delete_lecture(request: Request, lecture_id: str, org_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Delete lecture by lecture_id Delete lecture by lecture_id
""" """
return await delete_lecture(request,lecture_id, current_user) return await delete_lecture(request, lecture_id, current_user)
# Video play # Video play
@router.post("/video") @router.post("/video")
async def api_create_video_lecture(request: Request,name: str = Form(), coursechapter_id: str = Form(), current_user: PublicUser = Depends(get_current_user), video_file: UploadFile | None = None): async def api_create_video_lecture(request: Request, org_id: str, name: str = Form(), coursechapter_id: str = Form(), current_user: PublicUser = Depends(get_current_user), video_file: UploadFile | None = None):
""" """
Create new lecture Create new lecture
""" """
return await create_video_lecture(request,name, coursechapter_id, current_user, video_file) return await create_video_lecture(request, name, coursechapter_id, current_user, video_file)

View file

@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, Request
from src.dependencies.auth import get_current_user from src.dependencies.auth import get_current_user
from src.services.houses import House, HouseInDB, create_house, get_house, get_houses, update_house, delete_house from src.services.houses import House, HouseInDB, create_house, get_house, get_houses, update_house, delete_house
from src.services.users import PublicUser, User from src.services.users.users import PublicUser, User
router = APIRouter() router = APIRouter()

View file

@ -2,7 +2,7 @@
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Request
from src.dependencies.auth import get_current_user from src.dependencies.auth import get_current_user
from src.services.orgs import Organization, create_org, delete_org, get_organization, get_organization_by_slug, get_orgs_by_user, update_org from src.services.orgs import Organization, create_org, delete_org, get_organization, get_organization_by_slug, get_orgs_by_user, update_org
from src.services.users import PublicUser, User from src.services.users.users import PublicUser, User
router = APIRouter() router = APIRouter()

View file

@ -1,14 +1,15 @@
from fastapi import APIRouter, Depends, Request from fastapi import APIRouter, Depends, Request
from src.dependencies.auth import get_current_user from src.dependencies.auth import get_current_user
from src.services.roles import Role, create_role, delete_role, get_role, get_roles, update_role from src.services.roles.schemas.roles import Role
from src.services.users import PublicUser, User from src.services.roles.roles import create_role, delete_role, read_role, update_role
from src.services.users.schemas.users import PublicUser, User
router = APIRouter() router = APIRouter()
@router.post("/") @router.post("/")
async def api_create_role(request: Request,role_object: Role, current_user: PublicUser = Depends(get_current_user)): async def api_create_role(request: Request, role_object: Role, current_user: PublicUser = Depends(get_current_user)):
""" """
Create new role Create new role
""" """
@ -16,31 +17,23 @@ async def api_create_role(request: Request,role_object: Role, current_user: Publ
@router.get("/{role_id}") @router.get("/{role_id}")
async def api_get_role(request: Request,role_id: str): async def api_get_role(request: Request, role_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Get single role by role_id Get single role by role_id
""" """
return await get_role(request, role_id) return await read_role(request, role_id, current_user)
@router.get("/page/{page}/limit/{limit}")
async def api_get_role_by(request: Request,page: int, limit: int):
"""
Get roles by page and limit
"""
return await get_roles(request, page, limit)
@router.put("/{role_id}") @router.put("/{role_id}")
async def api_update_role(request: Request,role_object: Role, role_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_update_role(request: Request, role_object: Role, role_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Update role by role_id Update role by role_id
""" """
return await update_role(request, role_object, role_id, current_user) return await update_role(request, role_id, role_object, current_user)
@router.delete("/{role_id}") @router.delete("/{role_id}")
async def api_delete_role(request: Request,role_id: str, current_user: PublicUser = Depends(get_current_user)): async def api_delete_role(request: Request, role_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Delete role by ID Delete role by ID
""" """

View file

@ -2,7 +2,9 @@ from fastapi import Depends, FastAPI, APIRouter
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel from pydantic import BaseModel
from src.dependencies.auth import * from src.dependencies.auth import *
from src.services.users import * 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, read_user, update_user, update_user_password
@ -25,13 +27,6 @@ async def api_get_current_user_metadata(request: Request,current_user: User = De
return await get_profile_metadata(request , current_user.dict()) return await get_profile_metadata(request , current_user.dict())
@router.get("/username/{username}")
async def api_get_user_by_username(request: Request, username: str):
"""
Get single user by username
"""
return await get_user(request, username)
@router.get("/user_id/{user_id}") @router.get("/user_id/{user_id}")
async def api_get_user_by_userid(request: Request,user_id: str): async def api_get_user_by_userid(request: Request,user_id: str):
@ -42,32 +37,32 @@ async def api_get_user_by_userid(request: Request,user_id: str):
@router.post("/") @router.post("/")
async def api_create_user(request: Request,user_object: UserWithPassword): async def api_create_user(request: Request,user_object: UserWithPassword, org_id: str ):
""" """
Create new user Create new user
""" """
return await create_user(request, user_object) return await create_user(request, None, user_object, org_id)
@router.delete("/user_id/{user_id}") @router.delete("/user_id/{user_id}")
async def api_delete_user(request: Request, user_id: str): async def api_delete_user(request: Request, user_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Delete user by ID Delete user by ID
""" """
return await delete_user(request, user_id) return await delete_user(request, current_user, user_id)
@router.put("/user_id/{user_id}") @router.put("/user_id/{user_id}")
async def api_update_user(request: Request, user_object: User, user_id: str): async def api_update_user(request: Request, user_object: User, user_id: str, current_user: PublicUser = Depends(get_current_user)):
""" """
Update user by ID Update user by ID
""" """
return await update_user(request, user_id, user_object) return await update_user(request, user_id, user_object, current_user)
@router.put("/password/user_id/{user_id}") @router.put("/password/user_id/{user_id}")
async def api_update_user_password(request: Request, user_id: str , passwordChangeForm : PasswordChangeForm): async def api_update_user_password(request: Request, user_id: str , passwordChangeForm : PasswordChangeForm, current_user: PublicUser = Depends(get_current_user)):
""" """
Update user password by ID Update user password by ID
""" """
return await update_user_password(request, user_id, passwordChangeForm) return await update_user_password(request,current_user, user_id, passwordChangeForm)

View file

@ -7,7 +7,7 @@ from fastapi import HTTPException, Request, status
from pydantic import BaseModel from pydantic import BaseModel
from src.services.courses.chapters import get_coursechapters_meta from src.services.courses.chapters import get_coursechapters_meta
from src.services.users import PublicUser from src.services.users.users import PublicUser
#### Classes #################################################### #### Classes ####################################################

View file

@ -4,7 +4,7 @@ from fastapi import HTTPException, status, UploadFile, Request
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
import os import os
from src.services.users import PublicUser from src.services.users.users import PublicUser
class PhotoFile(BaseModel): class PhotoFile(BaseModel):

View file

@ -4,7 +4,7 @@ from fastapi import HTTPException, status, UploadFile, Request
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
import os import os
from src.services.users import PublicUser from src.services.users.users import PublicUser
class DocumentFile(BaseModel): class DocumentFile(BaseModel):

View file

@ -3,7 +3,7 @@ from uuid import uuid4
from fastapi import Request from fastapi import Request
from pydantic import BaseModel from pydantic import BaseModel
from src.services.blocks.blocks import Block from src.services.blocks.blocks import Block
from src.services.users import PublicUser from src.services.users.users import PublicUser
class option(BaseModel): class option(BaseModel):

View file

@ -4,7 +4,7 @@ import os
from fastapi import HTTPException, status, UploadFile,Request from fastapi import HTTPException, status, UploadFile,Request
from fastapi.responses import StreamingResponse from fastapi.responses import StreamingResponse
from src.services.users import PublicUser from src.services.users.users import PublicUser
class VideoFile(BaseModel): class VideoFile(BaseModel):

View file

@ -6,7 +6,7 @@ from pydantic import BaseModel
from src.services.courses.courses import Course, CourseInDB from src.services.courses.courses import Course, CourseInDB
from src.services.courses.lectures.lectures import Lecture, LectureInDB from src.services.courses.lectures.lectures import Lecture, LectureInDB
from src.services.security import verify_user_rights_with_roles from src.services.security import verify_user_rights_with_roles
from src.services.users import PublicUser from src.services.users.users import PublicUser
from fastapi import HTTPException, status, Request, Response, BackgroundTasks, UploadFile, File from fastapi import HTTPException, status, Request, Response, BackgroundTasks, UploadFile, File
@ -40,10 +40,13 @@ async def create_coursechapter(request: Request,coursechapter_object: CourseChap
coursechapters = request.app.db["coursechapters"] coursechapters = request.app.db["coursechapters"]
courses = request.app.db["courses"] courses = request.app.db["courses"]
# get course org_id and verify rights
course = await courses.find_one({"course_id": course_id})
# generate coursechapter_id with uuid4 # generate coursechapter_id with uuid4
coursechapter_id = str(f"coursechapter_{uuid4()}") coursechapter_id = str(f"coursechapter_{uuid4()}")
hasRoleRights = await verify_user_rights_with_roles(request, "create", current_user.user_id, coursechapter_id) hasRoleRights = await verify_user_rights_with_roles(request, "create", current_user.user_id, coursechapter_id, course["org_id"])
if not hasRoleRights: if not hasRoleRights:
raise HTTPException( raise HTTPException(
@ -229,7 +232,7 @@ async def verify_rights(request: Request,course_id: str, current_user: PublicUse
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail=f"Course does not exist") status_code=status.HTTP_409_CONFLICT, detail=f"Course does not exist")
hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, course_id) hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, course_id, course["org_id"])
isAuthor = current_user.user_id in course["authors"] isAuthor = current_user.user_id in course["authors"]
if not hasRoleRights and not isAuthor: if not hasRoleRights and not isAuthor:

View file

@ -2,7 +2,7 @@ import json
from typing import List from typing import List
from uuid import uuid4 from uuid import uuid4
from pydantic import BaseModel from pydantic import BaseModel
from src.services.users import PublicUser, User from src.services.users.users import PublicUser, User
from src.services.security import * from src.services.security import *
from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks
from datetime import datetime from datetime import datetime

View file

@ -4,7 +4,7 @@ from uuid import uuid4
from pydantic import BaseModel from pydantic import BaseModel
from src.services.courses.lectures.lectures import LectureInDB from src.services.courses.lectures.lectures import LectureInDB
from src.services.courses.thumbnails import upload_thumbnail from src.services.courses.thumbnails import upload_thumbnail
from src.services.users import PublicUser from src.services.users.users import PublicUser
from src.services.security import * from src.services.security import *
from fastapi import HTTPException, status, UploadFile from fastapi import HTTPException, status, UploadFile
from datetime import datetime from datetime import datetime
@ -141,12 +141,9 @@ async def create_course(request: Request, course_object: Course, org_id: str, cu
# TODO(fix) : the implementation here is clearly not the best one (this entire function) # TODO(fix) : the implementation here is clearly not the best one (this entire function)
course_object.org_id = org_id course_object.org_id = org_id
hasRoleRights = await verify_user_rights_with_roles(request, "create", current_user.user_id, course_id) await verify_user_rights_with_roles(request, "create", current_user.user_id, course_id,org_id)
if not hasRoleRights:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Roles : Insufficient rights to perform this action")
if thumbnail_file: if thumbnail_file:
name_in_disk = f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}" name_in_disk = f"{course_id}_thumbnail_{uuid4()}.{thumbnail_file.filename.split('.')[-1]}"
await upload_thumbnail(thumbnail_file, name_in_disk) await upload_thumbnail(thumbnail_file, name_in_disk)
@ -290,7 +287,7 @@ async def verify_rights(request: Request, course_id: str, current_user: PublicUs
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail=f"Course/CourseChapter does not exist") status_code=status.HTTP_409_CONFLICT, detail=f"Course/CourseChapter does not exist")
hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, course_id) hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, course_id, course["org_id"])
isAuthor = current_user.user_id in course["authors"] isAuthor = current_user.user_id in course["authors"]
if not hasRoleRights and not isAuthor: if not hasRoleRights and not isAuthor:

View file

@ -1,6 +1,6 @@
from pydantic import BaseModel from pydantic import BaseModel
from src.services.security import verify_user_rights_with_roles from src.services.security import verify_user_rights_with_roles
from src.services.users import PublicUser, User from src.services.users.schemas.users import PublicUser, User
from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks, UploadFile, File from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks, UploadFile, File
from uuid import uuid4 from uuid import uuid4
from datetime import datetime from datetime import datetime
@ -17,6 +17,7 @@ class Lecture(BaseModel):
class LectureInDB(Lecture): class LectureInDB(Lecture):
lecture_id: str lecture_id: str
coursechapter_id: str coursechapter_id: str
org_id: str
creationDate: str creationDate: str
updateDate: str updateDate: str
@ -28,14 +29,14 @@ class LectureInDB(Lecture):
#################################################### ####################################################
async def create_lecture(request: Request,lecture_object: Lecture, coursechapter_id: str, current_user: PublicUser): async def create_lecture(request: Request, lecture_object: Lecture, org_id: str, coursechapter_id: str, current_user: PublicUser):
lectures = request.app.db["lectures"] lectures = request.app.db["lectures"]
coursechapters = request.app.db["coursechapters"] coursechapters = request.app.db["coursechapters"]
# generate lecture_id # generate lecture_id
lecture_id = str(f"lecture_{uuid4()}") lecture_id = str(f"lecture_{uuid4()}")
hasRoleRights = await verify_user_rights_with_roles(request, "create", current_user.user_id, lecture_id) hasRoleRights = await verify_user_rights_with_roles(request, "create", current_user.user_id, lecture_id, org_id)
if not hasRoleRights: if not hasRoleRights:
raise HTTPException( raise HTTPException(
@ -43,23 +44,23 @@ async def create_lecture(request: Request,lecture_object: Lecture, coursechapter
# create lecture # create lecture
lecture = LectureInDB(**lecture_object.dict(), creationDate=str( lecture = LectureInDB(**lecture_object.dict(), creationDate=str(
datetime.now()), coursechapter_id=coursechapter_id, updateDate=str(datetime.now()), lecture_id=lecture_id) datetime.now()), coursechapter_id=coursechapter_id, updateDate=str(datetime.now()), lecture_id=lecture_id, org_id=org_id)
await lectures.insert_one(lecture.dict()) await lectures.insert_one(lecture.dict())
# update chapter # update chapter
await coursechapters.update_one({"coursechapter_id": coursechapter_id}, { await coursechapters.update_one({"coursechapter_id": coursechapter_id}, {
"$addToSet": {"lectures": lecture_id}}) "$addToSet": {"lectures": lecture_id}})
return lecture return lecture
async def get_lecture(request: Request,lecture_id: str, current_user: PublicUser): async def get_lecture(request: Request, lecture_id: str, current_user: PublicUser):
lectures = request.app.db["lectures"] lectures = request.app.db["lectures"]
lecture = await lectures.find_one({"lecture_id": lecture_id}) lecture = await lectures.find_one({"lecture_id": lecture_id})
# verify course rights # verify course rights
hasRoleRights = await verify_user_rights_with_roles(request,"read", current_user.user_id, lecture_id) hasRoleRights = await verify_user_rights_with_roles(request, "read", current_user.user_id, lecture_id, element_org_id=lecture["org_id"])
if not hasRoleRights: if not hasRoleRights:
raise HTTPException( raise HTTPException(
@ -73,14 +74,13 @@ async def get_lecture(request: Request,lecture_id: str, current_user: PublicUser
return lecture return lecture
async def update_lecture(request: Request,lecture_object: Lecture, lecture_id: str, current_user: PublicUser): async def update_lecture(request: Request, lecture_object: Lecture, lecture_id: str, current_user: PublicUser):
# verify course rights
await verify_user_rights_with_roles(request, "update", current_user.user_id, lecture_id)
lectures = request.app.db["lectures"] lectures = request.app.db["lectures"]
lecture = await lectures.find_one({"lecture_id": lecture_id}) lecture = await lectures.find_one({"lecture_id": lecture_id})
# verify course rights
await verify_user_rights_with_roles(request, "update", current_user.user_id, lecture_id, element_org_id=lecture["org_id"])
if lecture: if lecture:
creationDate = lecture["creationDate"] creationDate = lecture["creationDate"]
@ -89,7 +89,7 @@ async def update_lecture(request: Request,lecture_object: Lecture, lecture_id: s
datetime_object = datetime.now() datetime_object = datetime.now()
updated_course = LectureInDB( updated_course = LectureInDB(
lecture_id=lecture_id, coursechapter_id=lecture["coursechapter_id"], creationDate=creationDate, updateDate=str(datetime_object), **lecture_object.dict()) lecture_id=lecture_id, coursechapter_id=lecture["coursechapter_id"], creationDate=creationDate, updateDate=str(datetime_object), org_id=lecture["org_id"], **lecture_object.dict())
await lectures.update_one({"lecture_id": lecture_id}, { await lectures.update_one({"lecture_id": lecture_id}, {
"$set": updated_course.dict()}) "$set": updated_course.dict()})
@ -101,15 +101,15 @@ async def update_lecture(request: Request,lecture_object: Lecture, lecture_id: s
status_code=status.HTTP_409_CONFLICT, detail="lecture does not exist") status_code=status.HTTP_409_CONFLICT, detail="lecture does not exist")
async def delete_lecture(request: Request,lecture_id: str, current_user: PublicUser): async def delete_lecture(request: Request, lecture_id: str, current_user: PublicUser):
# verify course rights
await verify_user_rights_with_roles(request,"delete", current_user.user_id, lecture_id)
lectures = request.app.db["lectures"] lectures = request.app.db["lectures"]
lecture = await lectures.find_one({"lecture_id": lecture_id}) lecture = await lectures.find_one({"lecture_id": lecture_id})
# verify course rights
await verify_user_rights_with_roles(request, "delete", current_user.user_id, lecture_id, element_org_id=lecture["org_id"])
if not lecture: if not lecture:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="lecture does not exist") status_code=status.HTTP_409_CONFLICT, detail="lecture does not exist")
@ -127,18 +127,19 @@ async def delete_lecture(request: Request,lecture_id: str, current_user: PublicU
#################################################### ####################################################
async def get_lectures(request: Request,coursechapter_id: str, current_user: PublicUser): async def get_lectures(request: Request, coursechapter_id: str, current_user: PublicUser):
lectures = request.app.db["lectures"] lectures = request.app.db["lectures"]
# verify course rights # TODO : TERRIBLE SECURITY ISSUE HERE, NEED TO FIX ASAP
await verify_user_rights_with_roles(request,"read", current_user.user_id, coursechapter_id) # TODO : TERRIBLE SECURITY ISSUE HERE, NEED TO FIX ASAP
# TODO : TERRIBLE SECURITY ISSUE HERE, NEED TO FIX ASAP
lectures = lectures.find({"coursechapter_id": coursechapter_id}) lectures = lectures.find({"coursechapter_id": coursechapter_id})
if not lectures: if not lectures:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Course does not exist") status_code=status.HTTP_409_CONFLICT, detail="Course does not exist")
lectures = [LectureInDB(**lecture) for lecture in await lectures.to_list(length=100)] lectures = [LectureInDB(**lecture) for lecture in await lectures.to_list(length=100)]
return lectures return lectures

View file

@ -13,7 +13,6 @@ async def upload_video(video_file, lecture_id):
f.close() f.close()
except Exception as e: except Exception as e:
print(e)
return {"message": "There was an error uploading the file"} return {"message": "There was an error uploading the file"}
finally: finally:
video_file.file.close() video_file.file.close()

View file

@ -1,7 +1,7 @@
from pydantic import BaseModel from pydantic import BaseModel
from src.services.security import verify_user_rights_with_roles from src.services.security import verify_user_rights_with_roles
from src.services.courses.lectures.uploads.videos import upload_video from src.services.courses.lectures.uploads.videos import upload_video
from src.services.users import PublicUser from src.services.users.users import PublicUser
from src.services.courses.lectures.lectures import LectureInDB from src.services.courses.lectures.lectures import LectureInDB
from fastapi import HTTPException, status, UploadFile, Request from fastapi import HTTPException, status, UploadFile, Request
from uuid import uuid4 from uuid import uuid4
@ -48,9 +48,7 @@ async def create_video_lecture(request: Request,name: str, coursechapter_id: st
# upload video # upload video
if video_file: if video_file:
print("uploading video")
# get videofile format # get videofile format
await upload_video(video_file, lecture_id) await upload_video(video_file, lecture_id)
# todo : choose whether to update the chapter or not # todo : choose whether to update the chapter or not

View file

@ -9,7 +9,6 @@ async def upload_thumbnail(thumbnail_file, name_in_disk):
f.close() f.close()
except Exception as e: except Exception as e:
print(e)
return {"message": "There was an error uploading the file"} return {"message": "There was an error uploading the file"}
finally: finally:
thumbnail_file.file.close() thumbnail_file.file.close()

View file

@ -2,7 +2,7 @@ import json
from typing import List from typing import List
from uuid import uuid4 from uuid import uuid4
from pydantic import BaseModel from pydantic import BaseModel
from src.services.users import PublicUser, User from src.services.users.users import PublicUser, User
from src.services.security import * from src.services.security import *
from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks
from datetime import datetime from datetime import datetime

View file

@ -1,186 +1,186 @@
import requests # import requests
from datetime import datetime # from datetime import datetime
from fileinput import filename # from fileinput import filename
from pprint import pprint # from pprint import pprint
from uuid import uuid4 # from uuid import uuid4
from fastapi import File, UploadFile, Request # from fastapi import File, UploadFile, Request
from src.services.courses.chapters import CourseChapter, create_coursechapter # from src.services.courses.chapters import CourseChapter, create_coursechapter
from src.services.courses.lectures.lectures import Lecture, create_lecture # from src.services.courses.lectures.lectures import Lecture, create_lecture
from src.services.courses.thumbnails import upload_thumbnail # from src.services.courses.thumbnails import upload_thumbnail
from src.services.users import PublicUser, User, UserInDB, UserWithPassword # from src.services.users.users import PublicUser, User, UserInDB, UserWithPassword
from src.services.orgs import OrganizationInDB, Organization, create_org # from src.services.orgs import OrganizationInDB, Organization, create_org
from src.services.roles import Permission, Elements, RolePolicy, create_role # from src.services.roles.schemas.roles import Permission, Elements, RolePolicy, create_role
from src.services.users import create_user # from src.services.users.users import create_user
from src.services.courses.courses import Course, CourseInDB, create_course # from src.services.courses.courses import Course, CourseInDB, create_course
from src.services.roles import Role # from src.services.roles.roles import Role
from faker import Faker # from faker import Faker
async def create_initial_data(request: Request): # async def create_initial_data(request: Request):
fake = Faker(['en_US']) # fake = Faker(['en_US'])
fake_multilang = Faker( # fake_multilang = Faker(
['en_US', 'de_DE', 'ja_JP', 'es_ES', 'it_IT', 'pt_BR', 'ar_PS']) # ['en_US', 'de_DE', 'ja_JP', 'es_ES', 'it_IT', 'pt_BR', 'ar_PS'])
# Create users # # Create users
######################################## # ########################################
database_users = request.app.db["users"] # database_users = request.app.db["users"]
await database_users.delete_many({}) # await database_users.delete_many({})
users = [] # users = []
admin_user = UserWithPassword( # admin_user = UserWithPassword(
username=f"admin", # username=f"admin",
email=f"admin@admin.admin", # email=f"admin@admin.admin",
password="admin", # password="admin",
user_type="isOwner", # user_type="isOwner",
) # )
admin_user = await create_user(request, admin_user) # admin_user = await create_user(request, admin_user)
for i in range(0, 20): # for i in range(0, 20):
user = UserWithPassword( # user = UserWithPassword(
username=fake.simple_profile()['username'], # username=fake.simple_profile()['username'],
email=fake.email(), # email=fake.email(),
password=fake.password(), # password=fake.password(),
user_type="isOwner", # user_type="isOwner",
full_name=fake.name(), # full_name=fake.name(),
) # )
users.append(user) # users.append(user)
for user in users: # for user in users:
await create_user(request, user) # await create_user(request, user)
# find admin user # # find admin user
users = request.app.db["users"] # users = request.app.db["users"]
admin_user = await users.find_one({"username": "admin"}) # admin_user = await users.find_one({"username": "admin"})
if admin_user: # if admin_user:
admin_user = UserInDB(**admin_user) # admin_user = UserInDB(**admin_user)
current_user = PublicUser(**admin_user.dict()) # current_user = PublicUser(**admin_user.dict())
else: # else:
raise Exception("Admin user not found") # raise Exception("Admin user not found")
# Create organizations # # Create organizations
######################################## # ########################################
database_orgs = request.app.db["organizations"] # database_orgs = request.app.db["organizations"]
await database_orgs.delete_many({}) # await database_orgs.delete_many({})
organizations = [] # organizations = []
for i in range(0, 5): # for i in range(0, 5):
company = fake.company() # company = fake.company()
# remove whitespace and special characters and make lowercase # # remove whitespace and special characters and make lowercase
slug = ''.join(e for e in company if e.isalnum()).lower() # slug = ''.join(e for e in company if e.isalnum()).lower()
org = Organization( # org = Organization(
name=company, # name=company,
description=fake.unique.text(), # description=fake.unique.text(),
email=fake.unique.email(), # email=fake.unique.email(),
slug=slug, # slug=slug,
) # )
organizations.append(org) # organizations.append(org)
await create_org(request, org, current_user) # await create_org(request, org, current_user)
# Create roles # # Create roles
######################################## # ########################################
database_roles = request.app.db["roles"] # database_roles = request.app.db["roles"]
await database_roles.delete_many({}) # await database_roles.delete_many({})
roles = [] # roles = []
admin_role = Role( # admin_role = Role(
name="admin", # name="admin",
description="admin", # description="admin",
policies=[RolePolicy(permissions=Permission( # policies=[RolePolicy(permissions=Permission(
action_create=True, # action_create=True,
action_read=True, # action_read=True,
action_update=True, # action_update=True,
action_delete=True, # action_delete=True,
), # ),
elements=Elements( # elements=Elements(
courses=["*"], # courses=["*"],
users=["*"], # users=["*"],
houses=["*"], # houses=["*"],
collections=["*"], # collections=["*"],
organizations=["*"], # organizations=["*"],
coursechapters=["*"], # coursechapters=["*"],
lectures=["*"], # lectures=["*"],
))], # ))],
linked_users=[admin_user.user_id], # linked_users=[admin_user.user_id],
) # )
roles.append(admin_role) # roles.append(admin_role)
await create_role(request, admin_role, current_user) # await create_role(request, admin_role, current_user)
# Generate Courses and CourseChapters # # Generate Courses and CourseChapters
######################################## # ########################################
database_courses = request.app.db["courses"] # database_courses = request.app.db["courses"]
database_chapters = request.app.db["coursechapters"] # database_chapters = request.app.db["coursechapters"]
await database_courses.delete_many({}) # await database_courses.delete_many({})
await database_chapters.delete_many({}) # await database_chapters.delete_many({})
courses = [] # courses = []
orgs = request.app.db["organizations"] # orgs = request.app.db["organizations"]
if await orgs.count_documents({}) > 0: # if await orgs.count_documents({}) > 0:
for org in await orgs.find().to_list(length=100): # for org in await orgs.find().to_list(length=100):
for i in range(0, 5): # for i in range(0, 5):
# get image in BinaryIO format from unsplash and save it to disk # # get image in BinaryIO format from unsplash and save it to disk
image = requests.get( # image = requests.get(
"https://source.unsplash.com/random/800x600") # "https://source.unsplash.com/random/800x600")
with open("thumbnail.jpg", "wb") as f: # with open("thumbnail.jpg", "wb") as f:
f.write(image.content) # f.write(image.content)
course_id = f"course_{uuid4()}" # course_id = f"course_{uuid4()}"
course = CourseInDB( # course = CourseInDB(
name=fake_multilang.unique.sentence(), # name=fake_multilang.unique.sentence(),
description=fake_multilang.unique.text(), # description=fake_multilang.unique.text(),
mini_description=fake_multilang.unique.text(), # mini_description=fake_multilang.unique.text(),
thumbnail="thumbnail", # thumbnail="thumbnail",
org_id=org['org_id'], # org_id=org['org_id'],
learnings=[fake_multilang.unique.sentence() # learnings=[fake_multilang.unique.sentence()
for i in range(0, 5)], # for i in range(0, 5)],
public=True, # public=True,
chapters=[], # chapters=[],
course_id=course_id, # course_id=course_id,
creationDate=str(datetime.now()), # creationDate=str(datetime.now()),
updateDate=str(datetime.now()), # updateDate=str(datetime.now()),
authors=[current_user.user_id], # authors=[current_user.user_id],
) # )
courses = request.app.db["courses"] # courses = request.app.db["courses"]
name_in_disk = f"test_mock{course_id}.jpeg" # name_in_disk = f"test_mock{course_id}.jpeg"
image = requests.get( # image = requests.get(
"https://source.unsplash.com/random/800x600") # "https://source.unsplash.com/random/800x600")
with open(f"content/uploads/img/{name_in_disk}", "wb") as f: # with open(f"content/uploads/img/{name_in_disk}", "wb") as f:
f.write(image.content) # f.write(image.content)
course.thumbnail = name_in_disk # course.thumbnail = name_in_disk
course = CourseInDB(**course.dict()) # course = CourseInDB(**course.dict())
course_in_db = await courses.insert_one(course.dict()) # course_in_db = await courses.insert_one(course.dict())
# create chapters # # create chapters
for i in range(0, 5): # for i in range(0, 5):
coursechapter = CourseChapter( # coursechapter = CourseChapter(
name=fake_multilang.unique.sentence(), # name=fake_multilang.unique.sentence(),
description=fake_multilang.unique.text(), # description=fake_multilang.unique.text(),
lectures=[], # lectures=[],
) # )
coursechapter = await create_coursechapter(request,coursechapter, course_id, current_user) # coursechapter = await create_coursechapter(request,coursechapter, course_id, current_user)
pprint(coursechapter) # pprint(coursechapter)
if coursechapter: # if coursechapter:
# create lectures # # create lectures
for i in range(0, 5): # for i in range(0, 5):
lecture = Lecture( # lecture = Lecture(
name=fake_multilang.unique.sentence(), # name=fake_multilang.unique.sentence(),
type="dynamic", # type="dynamic",
content={}, # content={},
) # )
lecture = await create_lecture(request,lecture, coursechapter['coursechapter_id'], current_user) # lecture = await create_lecture(request,lecture, coursechapter['coursechapter_id'], current_user)

View file

@ -2,7 +2,8 @@ import json
from typing import List from typing import List
from uuid import uuid4 from uuid import uuid4
from pydantic import BaseModel from pydantic import BaseModel
from src.services.users import PublicUser, User from src.services.users.schemas.users import UserOrganization
from src.services.users.users import PublicUser, User
from src.services.security import * from src.services.security import *
from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks from fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks
from datetime import datetime from datetime import datetime
@ -62,6 +63,7 @@ async def get_organization_by_slug(request: Request, org_slug: str):
async def create_org(request: Request, org_object: Organization, current_user: PublicUser): async def create_org(request: Request, org_object: Organization, current_user: PublicUser):
orgs = request.app.db["organizations"] orgs = request.app.db["organizations"]
user = request.app.db["users"]
# find if org already exists using name # find if org already exists using name
isOrgAvailable = await orgs.find_one({"slug": org_object.slug}) isOrgAvailable = await orgs.find_one({"slug": org_object.slug})
@ -79,6 +81,13 @@ async def create_org(request: Request, org_object: Organization, current_user: P
org_in_db = await orgs.insert_one(org.dict()) org_in_db = await orgs.insert_one(org.dict())
user_organization: UserOrganization = UserOrganization(
org_id=org_id, org_role="owner")
# add org to user
await user.update_one({"user_id": current_user.user_id}, {
"$addToSet": {"orgs": user_organization.dict()}})
if not org_in_db: if not org_in_db:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database") status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
@ -125,6 +134,10 @@ async def delete_org(request: Request, org_id: str, current_user: PublicUser):
isDeleted = await orgs.delete_one({"org_id": org_id}) isDeleted = await orgs.delete_one({"org_id": org_id})
# remove org from all users
users = request.app.db["users"]
await users.update_many({}, {"$pull": {"orgs": {"org_id": org_id}}})
if isDeleted: if isDeleted:
return {"detail": "Org deleted"} return {"detail": "Org deleted"}
else: else:
@ -134,9 +147,20 @@ async def delete_org(request: Request, org_id: str, current_user: PublicUser):
async def get_orgs_by_user(request: Request, user_id: str, page: int = 1, limit: int = 10): async def get_orgs_by_user(request: Request, user_id: str, page: int = 1, limit: int = 10):
orgs = request.app.db["organizations"] orgs = request.app.db["organizations"]
user = request.app.db["users"]
# find all orgs where user_id is in owners or admins arrays # get user orgs
all_orgs = orgs.find({"$or": [{"owners": user_id}, {"admins": user_id}]}).sort( user_orgs = await user.find_one({"user_id": user_id})
org_ids : list[UserOrganization] = []
for org in user_orgs["orgs"]:
if org["org_role"] == "owner" or org["org_role"] == "editor" or org["org_role"] == "member":
org_ids.append(org["org_id"])
# find all orgs where org_id is in org_ids array
all_orgs = orgs.find({"org_id": {"$in": org_ids}}).sort(
"name", 1).skip(10 * (page - 1)).limit(100) "name", 1).skip(10 * (page - 1)).limit(100)
return [json.loads(json.dumps(org, default=str)) for org in await all_orgs.to_list(length=100)] return [json.loads(json.dumps(org, default=str)) for org in await all_orgs.to_list(length=100)]
@ -154,7 +178,7 @@ async def verify_org_rights(request: Request, org_id: str, current_user: Public
status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist") status_code=status.HTTP_409_CONFLICT, detail="Organization does not exist")
isOwner = current_user.user_id in org["owners"] isOwner = current_user.user_id in org["owners"]
hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, org_id) hasRoleRights = await verify_user_rights_with_roles(request, action, current_user.user_id, org_id, org_id)
if not hasRoleRights and not isOwner: if not hasRoleRights and not isOwner:
raise HTTPException( raise HTTPException(

View file

@ -1,167 +0,0 @@
import json
from typing import List
from uuid import uuid4
from pydantic import BaseModel
from src.services.users import PublicUser, User
from src.services.security import *
from src.services.houses import House
from fastapi import HTTPException, status, Request
from datetime import datetime
#### Classes ####################################################
class Permission(BaseModel):
action_create: bool
action_read: bool
action_update: bool
action_delete: bool
class Elements(BaseModel):
courses: List[str]
users: List[str]
houses: List[str]
collections: List[str]
organizations: List[str]
coursechapters: List[str]
lectures : List[str]
class RolePolicy(BaseModel):
permissions: Permission
elements: Elements
class Role(BaseModel):
name: str
description: str
policies: List[RolePolicy]
linked_users: List[str]
class RoleInDB(Role):
role_id: str
creationDate: str
updateDate: str
#### Classes ####################################################
async def get_role(request: Request,role_id: str):
roles = request.app.db["roles"]
role = await roles.find_one({"role_id": role_id})
if not role:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Role does not exist")
role = Role(**role)
return role
async def create_role(request: Request,role_object: Role, current_user: PublicUser):
roles = request.app.db["roles"]
# find if house already exists using name
isRoleAvailable = await roles.find_one({"name": role_object.name})
if isRoleAvailable:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Role name already exists")
await verify_user_permissions_on_roles(request, "create", current_user)
# generate house_id with uuid4
role_id = str(f"role_{uuid4()}")
role = RoleInDB(role_id=role_id, creationDate=str(datetime.now()),
updateDate=str(datetime.now()), **role_object.dict())
role_in_db = await roles.insert_one(role.dict())
if not role_in_db:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
return role.dict()
async def update_role(request: Request,role_object: Role, role_id: str, current_user: PublicUser):
# verify house rights
await verify_user_permissions_on_roles(request, "update", current_user)
roles = request.app.db["roles"]
role = await roles.find_one({"role_id": role_id})
if not role:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Role does not exist")
updated_role = RoleInDB(
role_id=role_id, updateDate=str(datetime.now()), creationDate=role["creationDate"], **role_object.dict())
await roles.update_one({"role_id": role_id}, {"$set": updated_role.dict()})
return RoleInDB(**updated_role.dict())
async def delete_role(request: Request,role_id: str, current_user: PublicUser):
# verify house rights
await verify_user_permissions_on_roles(request, "delete", current_user)
roles = request.app.db["roles"]
role = await roles.find_one({"role_id": role_id})
if not role:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Role does not exist")
isDeleted = await roles.delete_one({"role_id": role_id})
if isDeleted:
return {"detail": "Role deleted"}
else:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Unavailable database")
async def get_roles(request: Request,page: int = 1, limit: int = 10):
roles = request.app.db["roles"]
# get all roles from database
all_roles = roles.find().sort("name", 1).skip(10 * (page - 1)).limit(limit)
return [json.loads(json.dumps(role, default=str)) for role in await all_roles.to_list(length=limit)]
#### Security ####################################################
async def verify_user_permissions_on_roles(request: Request,action: str, current_user: PublicUser):
users = request.app.db["users"]
user = await users.find_one({"user_id": current_user.user_id})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
isOwner = "owner" in user["user_type"]
isEditor = "editor" in user["user_type"]
# TODO: verify for all actions.
if action == "delete":
if isEditor:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="You do not have rights to this Role")
if not isOwner and not isEditor:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="You do not have rights to this Role")
return True
#### Security ####################################################

View file

112
src/services/roles/roles.py Normal file
View file

@ -0,0 +1,112 @@
import json
from typing import List, Literal
from uuid import uuid4
from pydantic import BaseModel
from src.services.roles.schemas.roles import Role, RoleInDB
from src.services.users.schemas.users import PublicUser, User
from src.services.security import *
from src.services.houses import House
from fastapi import HTTPException, status, Request
from datetime import datetime
async def create_role(request: Request, role_object: Role, current_user: PublicUser):
roles = request.app.db["roles"]
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())
role = RoleInDB(
role_id=role_id,
created_at=str(datetime.now()),
updated_at=str(datetime.now()),
**role_object.dict()
)
await roles.insert_one(role.dict())
return role
async def read_role(request: Request, role_id: str, current_user: PublicUser):
roles = request.app.db["roles"]
await verify_user_permissions_on_roles(request, current_user, "read", role_id)
role = RoleInDB(**await roles.find_one({"role_id": role_id}))
return role
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)
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))
return updated_role
async def delete_role(request: Request, role_id: str, current_user: PublicUser):
roles = request.app.db["roles"]
await verify_user_permissions_on_roles(request, current_user, "delete", role_id)
# Delete the role object in the database and return the object
deleted_role = RoleInDB(**await roles.find_one_and_delete({"role_id": role_id}))
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):
users = request.app.db["users"]
roles = request.app.db["roles"]
# If current user is not authenticated
if not current_user:
raise HTTPException(
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]:
return True
if role_id is not None:
role = RoleInDB(**await roles.find_one({"role_id": role_id}))
if action == "read":
if "owner" in [org.org_role for org in current_user.orgs]:
return True
for org in current_user.orgs:
if org.org_id == role.org_id:
return True
if action == "update":
for org in current_user.orgs:
# If the user is an owner of the organization
if org.org_id == role.org_id:
if org.org_role == "owner" or org.org_role == "editor":
return True
# Can't update a global role
if role.org_id == "*":
return False
if action == "delete":
for org in current_user.orgs:
# If the user is an owner of the organization
if org.org_id == role.org_id:
if org.org_role == "owner":
return True
# Can't delete a global role
if role.org_id == "*":
return False
#### Security ####################################################

View file

View file

@ -0,0 +1,41 @@
from typing import List, Literal
from pydantic import BaseModel
# Database Models
class Permission(BaseModel):
action_create: bool
action_read: bool
action_update: bool
action_delete: bool
def __getitem__(self, item):
return getattr(self, item)
class Elements(BaseModel):
courses: Permission
users: Permission
houses: Permission
collections: Permission
organizations: Permission
coursechapters: Permission
lectures: Permission
def __getitem__(self, item):
return getattr(self, item)
class Role(BaseModel):
name: str
description: str
elements : Elements
org_id: str | Literal["*"]
class RoleInDB(Role):
role_id: str
created_at: str
updated_at: str

View file

@ -1,6 +1,10 @@
from pprint import pprint
from fastapi import HTTPException, status, Request from fastapi import HTTPException, status, Request
from passlib.context import CryptContext from passlib.context import CryptContext
from passlib.hash import pbkdf2_sha256 from passlib.hash import pbkdf2_sha256
from src.services.roles.schemas.roles import RoleInDB
from src.services.users.schemas.users import User, UserInDB
### 🔒 JWT ############################################################## ### 🔒 JWT ##############################################################
@ -27,34 +31,47 @@ async def security_verify_password(plain_password: str, hashed_password: str):
### 🔒 Roles checking ############################################################## ### 🔒 Roles checking ##############################################################
async def verify_user_rights_with_roles(request: Request,action: str, user_id: str, element_id: str): async def verify_user_rights_with_roles(request: Request, action: str, user_id: str, element_id: str, element_org_id: str):
""" """
Check if the user has the right to perform the action on the element Check if the user has the right to perform the action on the element
""" """
roles = request.app.db["roles"] roles = request.app.db["roles"]
users = request.app.db["users"]
# find data where user_id is in linked_users or * is in linked_users # Check if the user is an admin
user_roles_cursor = roles.find({"$or": [{"linked_users": user_id}, {"linked_users": "*"}]}) user: UserInDB = UserInDB(**await users.find_one({"user_id": user_id}))
# Organization roles verification
for org in user.orgs:
# TODO: Check if the org_id (user) is the same as the org_id (element)
user_roles = [] if org.org_id == element_org_id:
return True
# Info: permission actions are: read, create, delete, update # Check if user is owner or reader of the organization
if org.org_role == ("owner" or "editor"):
return True
for role in await user_roles_cursor.to_list(length=100): # If the user is not an owner or a editor, check if he has a role
user_roles.append(role) # Get user roles
user_roles = user.roles
for role in user_roles: # TODO: Check if the org_id of the role is the same as the org_id of the element using find
for policy in role['policies']:
element = policy["elements"][await check_element_type(element_id)]
permission_state = policy["permissions"][f'action_{action}']
## if action != "create":
if ("*" in element or element_id in element) and (permission_state is True): await check_user_role_org_with_element_org(request, element_id, user_roles)
return True
else: # Check if user has the right role
return False
element_type = await check_element_type(element_id)
for role_id in user_roles:
role = RoleInDB(**await roles.find_one({"role_id": role_id}))
if role.elements[element_type][f"action_{action}"]:
return True
# If no role is found, raise an error
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="You don't have the right to perform this action")
async def check_element_type(element_id): async def check_element_type(element_id):
@ -80,4 +97,29 @@ async def check_element_type(element_id):
status_code=status.HTTP_409_CONFLICT, detail="Issue verifying element nature") status_code=status.HTTP_409_CONFLICT, detail="Issue verifying element nature")
async def check_user_role_org_with_element_org(request: Request, element_id: str, roles_list: list[str]):
element_type = await check_element_type(element_id)
element = request.app.db[element_type]
roles = request.app.db["roles"]
# get singular element type
singular_form_element = element_type[:-1]
element_type_id = singular_form_element + "_id"
element_org = await element.find_one({element_type_id: element_id})
for role_id in roles_list:
role = RoleInDB(**await roles.find_one({"role_id": role_id}))
if role.org_id == element_org["org_id"]:
return True
if role.org_id == "*":
return True
else:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="You don't have the right to perform this action")
### 🔒 Roles checking ############################################################## ### 🔒 Roles checking ##############################################################

View file

@ -1,221 +0,0 @@
from typing import Optional
from uuid import uuid4
from pydantic import BaseModel
from src.services.security import *
from fastapi import HTTPException, status, Request
from datetime import datetime
#### Classes ####################################################
class User(BaseModel):
username: str
email: str
full_name: str | None = None
disabled: bool | None = False
avatar_url: str | None = None
verified: bool | None = False
user_type: str | None = None
bio: str | None = None
class UserWithPassword(User):
password: str
class PublicUser(User):
user_id: str
creationDate: str
updateDate: str
class PasswordChangeForm(BaseModel):
old_password: str
new_password: str
class UserInDB(UserWithPassword):
user_id: str
password: str
creationDate: str
updateDate: str
class UserProfileMetadata(BaseModel):
user_object: PublicUser
roles = list
# TODO : terrible, export role classes from one single source of truth
class Role(BaseModel):
name: str
description: str
permissions: object
elements: object
#### Classes ####################################################
# TODO : user actions security
# TODO : avatar upload and update
async def get_user(request: Request, username: str):
users = request.app.db["users"]
user = await users.find_one({"username": username})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
user = User(**user)
return user
async def get_profile_metadata(request: Request, user):
users = request.app.db["users"]
roles = request.app.db["roles"]
user = await users.find_one({"user_id": user['user_id']})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
# get roles
user_roles = roles.find({"linked_users": user['user_id']})
user_roles_list = []
for role in await user_roles.to_list(length=100):
print(role)
user_roles_list.append(Role(**role))
return {
"user_object": PublicUser(**user),
"roles": user_roles_list
}
async def get_user_by_userid(request: Request, user_id: str):
users = request.app.db["users"]
user = await users.find_one({"user_id": user_id})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
user = User(**user)
return user
async def security_get_user(request: Request, email: str):
users = request.app.db["users"]
user = await users.find_one({"email": email})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User with Email does not exist")
return UserInDB(**user)
async def get_userid_by_username(request: Request, username: str):
users = request.app.db["users"]
user = await users.find_one({"username": username})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
return user["user_id"]
async def update_user(request: Request, user_id: str, user_object: User):
users = request.app.db["users"]
isUserExists = await users.find_one({"user_id": user_id})
isUsernameAvailable = await users.find_one({"username": user_object.username})
if not isUserExists:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
# TODO : fix this
# okay if username is not changed
if isUserExists["username"] == user_object.username:
user_object.username = user_object.username.lower()
else:
if isUsernameAvailable:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Username already used")
updated_user = {"$set": user_object.dict()}
users.update_one({"user_id": user_id}, updated_user)
return User(**user_object.dict())
async def update_user_password(request: Request, user_id: str, password_change_form: PasswordChangeForm):
users = request.app.db["users"]
isUserExists = await users.find_one({"user_id": user_id})
if not isUserExists:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
if not await security_verify_password(password_change_form.old_password, isUserExists["password"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Wrong password")
new_password = await security_hash_password(password_change_form.new_password)
updated_user = {"$set": {"password": new_password}}
users.update_one({"user_id": user_id}, updated_user)
return {"detail": "Password updated"}
async def delete_user(request: Request, user_id: str):
users = request.app.db["users"]
isUserAvailable = await users.find_one({"user_id": user_id})
if not isUserAvailable:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
users.delete_one({"user_id": user_id})
return {"detail": "User deleted"}
async def create_user(request: Request, user_object: UserWithPassword):
users = request.app.db["users"]
isUserAvailable = await users.find_one({"username": user_object.username})
if isUserAvailable:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Username already exists")
# generate house_id with uuid4
user_id = str(f"user_{uuid4()}")
# lowercase username
user_object.username = user_object.username.lower()
user_object.password = await security_hash_password(user_object.password)
user = UserInDB(user_id=user_id, creationDate=str(datetime.now()),
updateDate=str(datetime.now()), **user_object.dict())
user_in_db = await users.insert_one(user.dict())
return User(**user.dict())

View file

View file

View file

@ -0,0 +1,51 @@
from typing import Literal
from pydantic import BaseModel
class UserOrganization(BaseModel):
org_id: str
org_role: Literal['owner', 'editor', 'member']
def __getitem__(self, item):
return getattr(self, item)
class User(BaseModel):
username: str
email: str
full_name: str | None = None
avatar_url: str | None = None
bio: str | None = None
class UserWithPassword(User):
password: str
class UserInDB(User):
user_id: str
password: str
verified: bool | None = False
disabled: bool | None = False
orgs: list[UserOrganization] = []
roles: list[str] = []
creation_date: str
update_date: str
class PublicUser(User):
user_id: str
orgs: list[UserOrganization] = []
roles: list[str] = []
creation_date: str
update_date: str
# Forms ####################################################
class PasswordChangeForm(BaseModel):
old_password: str
new_password: str

243
src/services/users/users.py Normal file
View file

@ -0,0 +1,243 @@
from datetime import datetime
from typing import Literal
from uuid import uuid4
from fastapi import HTTPException, Request, status
from src.services.roles.schemas.roles import Role
from src.services.security import security_hash_password, security_verify_password
from src.services.users.schemas.users import PasswordChangeForm, PublicUser, User, UserOrganization, UserWithPassword, UserInDB
async def create_user(request: Request, current_user: PublicUser | None, user_object: UserWithPassword, org_id: str):
users = request.app.db["users"]
isUsernameAvailable = await users.find_one({"username": user_object.username})
isEmailAvailable = await users.find_one({"email": user_object.email})
if isUsernameAvailable:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Username already exists")
if isEmailAvailable:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Email already exists")
# Generate user_id with uuid4
user_id = str(f"user_{uuid4()}")
# Check if the requesting user is authenticated
if current_user is not None:
# Verify rights
await verify_user_rights_on_user(request, current_user, "create", user_id)
# Set the username & hash the password
user_object.username = user_object.username.lower()
user_object.password = await security_hash_password(user_object.password)
# Create initial orgs list with the org_id passed in
orgs = [UserOrganization(org_id=org_id, org_role="member")]
# Give role
roles = ["role_1"]
# Create the user
user = UserInDB(user_id=user_id, creation_date=str(datetime.now()),
update_date=str(datetime.now()), orgs=orgs, roles=roles, **user_object.dict())
# Insert the user into the database
await users.insert_one(user.dict())
return User(**user.dict())
async def read_user(request: Request, current_user: PublicUser, user_id: str):
users = request.app.db["users"]
# Check if the user exists
isUserExists = await users.find_one({"user_id": user_id})
# Verify rights
await verify_user_rights_on_user(request, current_user, "read", user_id)
# If the user does not exist, raise an error
if not isUserExists:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
return User(**isUserExists)
async def update_user(request: Request, user_id: str, user_object: User,current_user: PublicUser):
users = request.app.db["users"]
# Verify rights
await verify_user_rights_on_user(request, current_user, "update", user_id)
isUserExists = await users.find_one({"user_id": user_id})
isUsernameAvailable = await users.find_one({"username": user_object.username})
isEmailAvailable = await users.find_one({"email": user_object.email})
if not isUserExists:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
# okay if username is not changed
if isUserExists["username"] == user_object.username:
user_object.username = user_object.username.lower()
else:
if isUsernameAvailable:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Username already used")
if isEmailAvailable:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="Email already used")
updated_user = {"$set": user_object.dict()}
users.update_one({"user_id": user_id}, updated_user)
return User(**user_object.dict())
async def update_user_password(request: Request, current_user: PublicUser, user_id: str, password_change_form: PasswordChangeForm):
users = request.app.db["users"]
isUserExists = await users.find_one({"user_id": user_id})
# Verify rights
await verify_user_rights_on_user(request, current_user, "update", user_id)
if not isUserExists:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
if not await security_verify_password(password_change_form.old_password, isUserExists["password"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Wrong password")
new_password = await security_hash_password(password_change_form.new_password)
updated_user = {"$set": {"password": new_password}}
await users.update_one({"user_id": user_id}, updated_user)
return {"detail": "Password updated"}
async def delete_user(request: Request, current_user: PublicUser, user_id: str):
users = request.app.db["users"]
isUserExists = await users.find_one({"user_id": user_id})
# Verify is user has permission to delete the user
await verify_user_rights_on_user(request, current_user, "delete", user_id)
if not isUserExists:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
await users.delete_one({"user_id": user_id})
return {"detail": "User deleted"}
# Utils & Security functions
async def security_get_user(request: Request, email: str):
users = request.app.db["users"]
user = await users.find_one({"email": email})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User with Email does not exist")
return UserInDB(**user)
async def get_userid_by_username(request: Request, username: str):
users = request.app.db["users"]
user = await users.find_one({"username": username})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
return user["user_id"]
async def get_user_by_userid(request: Request, user_id: str):
users = request.app.db["users"]
user = await users.find_one({"user_id": user_id})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
user = User(**user)
return user
async def get_profile_metadata(request: Request, user):
users = request.app.db["users"]
roles = request.app.db["roles"]
user = await users.find_one({"user_id": user['user_id']})
if not user:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
return {
"user_object": PublicUser(**user),
"roles": "random"
}
# Verification of the user's permissions on the roles
async def verify_user_rights_on_user(request: Request, current_user: PublicUser, action: Literal["create", "read", "update", "delete"], user_id: str):
users = request.app.db["users"]
user = UserInDB(**await users.find_one({"user_id": user_id}))
if action == "create":
return True
if action == "read":
if current_user.user_id == user_id:
return True
for org in current_user.orgs:
if org.org_id in [org.org_id for org in user.orgs]:
return True
return False
if action == "update":
if current_user.user_id == user_id:
return True
for org in current_user.orgs:
if org.org_id in [org.org_id for org in user.orgs]:
if org.org_role == "owner":
return True
# TODO: Verify user roles on the org
return False
if action == "delete":
if current_user.user_id == user_id:
return True
for org in current_user.orgs:
if org.org_id in [org.org_id for org in user.orgs]:
if org.org_role == "owner":
return True
# TODO: Verify user roles on the org