mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
Merge pull request #59 from learnhouse/feat/roles-remake
feat: init roles & user reengineering
This commit is contained in:
commit
e1d4c4c594
41 changed files with 856 additions and 738 deletions
10
app.py
10
app.py
|
|
@ -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 🤖"}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 ####################################################
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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 ####################################################
|
|
||||||
0
src/services/roles/__init__.py
Normal file
0
src/services/roles/__init__.py
Normal file
112
src/services/roles/roles.py
Normal file
112
src/services/roles/roles.py
Normal 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 ####################################################
|
||||||
0
src/services/roles/schemas/__init__.py
Normal file
0
src/services/roles/schemas/__init__.py
Normal file
41
src/services/roles/schemas/roles.py
Normal file
41
src/services/roles/schemas/roles.py
Normal 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
|
||||||
|
|
||||||
|
|
@ -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 ##############################################################
|
||||||
|
|
|
||||||
|
|
@ -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())
|
|
||||||
0
src/services/users/__init__.py
Normal file
0
src/services/users/__init__.py
Normal file
0
src/services/users/schemas/__init__.py
Normal file
0
src/services/users/schemas/__init__.py
Normal file
51
src/services/users/schemas/users.py
Normal file
51
src/services/users/schemas/users.py
Normal 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
243
src/services/users/users.py
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue