mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: init roles & user reengineering
This commit is contained in:
parent
40404cc852
commit
9384cbe85d
38 changed files with 671 additions and 409 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 🤖"}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
"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";
|
||||||
|
|
||||||
const NewCoursePage = (params: any) => {
|
const NewCoursePage = (params: any) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
|
||||||
|
|
@ -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,7 +2,7 @@ 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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,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,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,41 @@ 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):
|
||||||
"""
|
"""
|
||||||
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)
|
||||||
|
|
||||||
|
# Check if user is owner or reader of the organization
|
||||||
user_roles = []
|
if org.org_role == ("owner" or "editor"):
|
||||||
|
|
||||||
# Info: permission actions are: read, create, delete, update
|
|
||||||
|
|
||||||
for role in await user_roles_cursor.to_list(length=100):
|
|
||||||
user_roles.append(role)
|
|
||||||
|
|
||||||
for role in user_roles:
|
|
||||||
for policy in role['policies']:
|
|
||||||
element = policy["elements"][await check_element_type(element_id)]
|
|
||||||
permission_state = policy["permissions"][f'action_{action}']
|
|
||||||
|
|
||||||
##
|
|
||||||
if ("*" in element or element_id in element) and (permission_state is True):
|
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
return False
|
# If the user is not an owner or a editor, check if he has a role
|
||||||
|
# Get user roles
|
||||||
|
user_roles = user.roles
|
||||||
|
|
||||||
|
# TODO: Check if the org_id of the role is the same as the org_id of the element using find
|
||||||
|
|
||||||
|
# Check if user has the right role
|
||||||
|
|
||||||
|
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):
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ class User(BaseModel):
|
||||||
verified: bool | None = False
|
verified: bool | None = False
|
||||||
user_type: str | None = None
|
user_type: str | None = None
|
||||||
bio: str | None = None
|
bio: str | None = None
|
||||||
|
orgs: list
|
||||||
|
|
||||||
|
|
||||||
class UserWithPassword(User):
|
class UserWithPassword(User):
|
||||||
password: str
|
password: str
|
||||||
|
|
@ -24,9 +26,11 @@ class UserWithPassword(User):
|
||||||
|
|
||||||
class PublicUser(User):
|
class PublicUser(User):
|
||||||
user_id: str
|
user_id: str
|
||||||
|
orgs: list
|
||||||
creationDate: str
|
creationDate: str
|
||||||
updateDate: str
|
updateDate: str
|
||||||
|
|
||||||
|
|
||||||
class PasswordChangeForm(BaseModel):
|
class PasswordChangeForm(BaseModel):
|
||||||
old_password: str
|
old_password: str
|
||||||
new_password: str
|
new_password: str
|
||||||
|
|
@ -86,7 +90,6 @@ async def get_profile_metadata(request: Request, user):
|
||||||
|
|
||||||
user_roles_list = []
|
user_roles_list = []
|
||||||
for role in await user_roles.to_list(length=100):
|
for role in await user_roles.to_list(length=100):
|
||||||
print(role)
|
|
||||||
user_roles_list.append(Role(**role))
|
user_roles_list.append(Role(**role))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -142,7 +145,6 @@ async def update_user(request: Request, user_id: str, user_object: User):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
|
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
|
||||||
|
|
||||||
|
|
||||||
# TODO : fix this
|
# TODO : fix this
|
||||||
|
|
||||||
# okay if username is not changed
|
# okay if username is not changed
|
||||||
|
|
@ -154,7 +156,6 @@ async def update_user(request: Request, user_id: str, user_object: User):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_409_CONFLICT, detail="Username already used")
|
status_code=status.HTTP_409_CONFLICT, detail="Username already used")
|
||||||
|
|
||||||
|
|
||||||
updated_user = {"$set": user_object.dict()}
|
updated_user = {"$set": user_object.dict()}
|
||||||
users.update_one({"user_id": user_id}, updated_user)
|
users.update_one({"user_id": user_id}, updated_user)
|
||||||
|
|
||||||
|
|
|
||||||
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
48
src/services/users/schemas/users.py
Normal file
48
src/services/users/schemas/users.py
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
from typing import Literal
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class UserOrganization(BaseModel):
|
||||||
|
org_id: str
|
||||||
|
org_role: Literal['owner', 'editor', 'member']
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
232
src/services/users/users.py
Normal file
232
src/services/users/users.py
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
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"]
|
||||||
|
|
||||||
|
isUserAvailable = await users.find_one({"username": user_object.username})
|
||||||
|
|
||||||
|
if isUserAvailable:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_409_CONFLICT, detail="Username 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})
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
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