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.staticfiles import StaticFiles
|
||||
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
|
||||
|
|
@ -64,8 +64,8 @@ async def root():
|
|||
return {"Message": "Welcome to LearnHouse ✨"}
|
||||
|
||||
|
||||
@app.get("/initial_data")
|
||||
async def initial_data(request: Request):
|
||||
# @app.get("/initial_data")
|
||||
# async def initial_data(request: Request):
|
||||
|
||||
await create_initial_data(request)
|
||||
return {"Message": "Initial data created 🤖"}
|
||||
# await create_initial_data(request)
|
||||
# return {"Message": "Initial data created 🤖"}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
"use client";
|
||||
import { useRouter } from "next/navigation";
|
||||
import React from "react";
|
||||
import { Header } from "../../../../../components/UI/Header";
|
||||
import Layout from "../../../../../components/UI/Layout";
|
||||
import { Title } from "../../../../../components/UI/Elements/Styles/Title";
|
||||
import { createNewCourse } from "../../../../../services/courses/courses";
|
||||
import { getOrganizationContextInfo } from "../../../../../services/orgs";
|
||||
import { Header } from "@components/UI/Header";
|
||||
import Layout from "@components/UI/Layout";
|
||||
import { Title } from "@components/UI/Elements/Styles/Title";
|
||||
import { createNewCourse } from "@services/courses/courses";
|
||||
import { getOrganizationContextInfo } from "@services/orgs";
|
||||
|
||||
const NewCoursePage = (params: any) => {
|
||||
const router = useRouter();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
|||
from passlib.context import CryptContext
|
||||
from jose import JWTError, jwt
|
||||
from datetime import datetime, timedelta
|
||||
from src.services.users import *
|
||||
from src.services.users.users import *
|
||||
from fastapi import Cookie, FastAPI
|
||||
from src.services.security import *
|
||||
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.security import OAuth2PasswordRequestForm
|
||||
from src.dependencies.auth import *
|
||||
from src.services.users import *
|
||||
from src.services.users.users import *
|
||||
from datetime import timedelta
|
||||
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.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.users import PublicUser
|
||||
from src.services.users.users import PublicUser
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
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.users import PublicUser
|
||||
from src.services.users.users import PublicUser
|
||||
from src.dependencies.auth import get_current_user
|
||||
|
||||
router = APIRouter()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from fastapi import APIRouter, Depends, Request
|
||||
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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, UploadFile, Form, Request
|
|||
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.users import PublicUser
|
||||
from src.services.users.users import PublicUser
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, Request
|
|||
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.users import PublicUser, User
|
||||
from src.services.users.users import PublicUser, User
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
from fastapi import APIRouter, Depends, Request
|
||||
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.users import PublicUser, User
|
||||
from src.services.users.users import PublicUser, User
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from fastapi import APIRouter, Depends, Request
|
||||
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.users import PublicUser, User
|
||||
from src.services.roles.schemas.roles import Role
|
||||
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()
|
||||
|
|
@ -16,19 +17,11 @@ async def api_create_role(request: Request,role_object: Role, current_user: Publ
|
|||
|
||||
|
||||
@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
|
||||
"""
|
||||
return await get_role(request, role_id)
|
||||
|
||||
|
||||
@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)
|
||||
return await read_role(request, role_id, current_user)
|
||||
|
||||
|
||||
@router.put("/{role_id}")
|
||||
|
|
@ -36,7 +29,7 @@ async def api_update_role(request: Request,role_object: Role, role_id: str, curr
|
|||
"""
|
||||
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}")
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ from fastapi import Depends, FastAPI, APIRouter
|
|||
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
|
||||
from pydantic import BaseModel
|
||||
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())
|
||||
|
||||
|
||||
@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}")
|
||||
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("/")
|
||||
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
|
||||
"""
|
||||
return await create_user(request, user_object)
|
||||
return await create_user(request, None, user_object, org_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
|
||||
"""
|
||||
|
||||
return await delete_user(request, user_id)
|
||||
return await delete_user(request, current_user, 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
|
||||
"""
|
||||
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}")
|
||||
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
|
||||
"""
|
||||
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 src.services.courses.chapters import get_coursechapters_meta
|
||||
|
||||
from src.services.users import PublicUser
|
||||
from src.services.users.users import PublicUser
|
||||
|
||||
#### Classes ####################################################
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from fastapi import HTTPException, status, UploadFile, Request
|
|||
from fastapi.responses import StreamingResponse
|
||||
import os
|
||||
|
||||
from src.services.users import PublicUser
|
||||
from src.services.users.users import PublicUser
|
||||
|
||||
|
||||
class PhotoFile(BaseModel):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from fastapi import HTTPException, status, UploadFile, Request
|
|||
from fastapi.responses import StreamingResponse
|
||||
import os
|
||||
|
||||
from src.services.users import PublicUser
|
||||
from src.services.users.users import PublicUser
|
||||
|
||||
|
||||
class DocumentFile(BaseModel):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from uuid import uuid4
|
|||
from fastapi import Request
|
||||
from pydantic import BaseModel
|
||||
from src.services.blocks.blocks import Block
|
||||
from src.services.users import PublicUser
|
||||
from src.services.users.users import PublicUser
|
||||
|
||||
|
||||
class option(BaseModel):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import os
|
|||
from fastapi import HTTPException, status, UploadFile,Request
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from src.services.users import PublicUser
|
||||
from src.services.users.users import PublicUser
|
||||
|
||||
|
||||
class VideoFile(BaseModel):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from pydantic import BaseModel
|
|||
from src.services.courses.courses import Course, CourseInDB
|
||||
from src.services.courses.lectures.lectures import Lecture, LectureInDB
|
||||
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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import json
|
|||
from typing import List
|
||||
from uuid import uuid4
|
||||
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 fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks
|
||||
from datetime import datetime
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from uuid import uuid4
|
|||
from pydantic import BaseModel
|
||||
from src.services.courses.lectures.lectures import LectureInDB
|
||||
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 fastapi import HTTPException, status, UploadFile
|
||||
from datetime import datetime
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from pydantic import BaseModel
|
||||
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 uuid import uuid4
|
||||
from datetime import datetime
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ async def upload_video(video_file, lecture_id):
|
|||
f.close()
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {"message": "There was an error uploading the file"}
|
||||
finally:
|
||||
video_file.file.close()
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from pydantic import BaseModel
|
||||
from src.services.security import verify_user_rights_with_roles
|
||||
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 fastapi import HTTPException, status, UploadFile, Request
|
||||
from uuid import uuid4
|
||||
|
|
@ -48,9 +48,7 @@ async def create_video_lecture(request: Request,name: str, coursechapter_id: st
|
|||
|
||||
# upload video
|
||||
if video_file:
|
||||
print("uploading video")
|
||||
# get videofile format
|
||||
|
||||
await upload_video(video_file, lecture_id)
|
||||
|
||||
# todo : choose whether to update the chapter or not
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ async def upload_thumbnail(thumbnail_file, name_in_disk):
|
|||
f.close()
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return {"message": "There was an error uploading the file"}
|
||||
finally:
|
||||
thumbnail_file.file.close()
|
||||
|
|
@ -2,7 +2,7 @@ import json
|
|||
from typing import List
|
||||
from uuid import uuid4
|
||||
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 fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks
|
||||
from datetime import datetime
|
||||
|
|
|
|||
|
|
@ -1,186 +1,186 @@
|
|||
import requests
|
||||
from datetime import datetime
|
||||
from fileinput import filename
|
||||
from pprint import pprint
|
||||
from uuid import uuid4
|
||||
from fastapi import File, UploadFile, Request
|
||||
from src.services.courses.chapters import CourseChapter, create_coursechapter
|
||||
from src.services.courses.lectures.lectures import Lecture, create_lecture
|
||||
from src.services.courses.thumbnails import upload_thumbnail
|
||||
from src.services.users import PublicUser, User, UserInDB, UserWithPassword
|
||||
# import requests
|
||||
# from datetime import datetime
|
||||
# from fileinput import filename
|
||||
# from pprint import pprint
|
||||
# from uuid import uuid4
|
||||
# from fastapi import File, UploadFile, Request
|
||||
# from src.services.courses.chapters import CourseChapter, create_coursechapter
|
||||
# from src.services.courses.lectures.lectures import Lecture, create_lecture
|
||||
# from src.services.courses.thumbnails import upload_thumbnail
|
||||
# from src.services.users.users import PublicUser, User, UserInDB, UserWithPassword
|
||||
|
||||
from src.services.orgs import OrganizationInDB, Organization, create_org
|
||||
from src.services.roles import Permission, Elements, RolePolicy, create_role
|
||||
from src.services.users import create_user
|
||||
from src.services.courses.courses import Course, CourseInDB, create_course
|
||||
from src.services.roles import Role
|
||||
from faker import Faker
|
||||
# from src.services.orgs import OrganizationInDB, Organization, create_org
|
||||
# from src.services.roles.schemas.roles import Permission, Elements, RolePolicy, create_role
|
||||
# from src.services.users.users import create_user
|
||||
# from src.services.courses.courses import Course, CourseInDB, create_course
|
||||
# from src.services.roles.roles import Role
|
||||
# from faker import Faker
|
||||
|
||||
|
||||
async def create_initial_data(request: Request):
|
||||
fake = Faker(['en_US'])
|
||||
fake_multilang = Faker(
|
||||
['en_US', 'de_DE', 'ja_JP', 'es_ES', 'it_IT', 'pt_BR', 'ar_PS'])
|
||||
# async def create_initial_data(request: Request):
|
||||
# fake = Faker(['en_US'])
|
||||
# fake_multilang = Faker(
|
||||
# ['en_US', 'de_DE', 'ja_JP', 'es_ES', 'it_IT', 'pt_BR', 'ar_PS'])
|
||||
|
||||
# Create users
|
||||
########################################
|
||||
# # Create users
|
||||
# ########################################
|
||||
|
||||
database_users = request.app.db["users"]
|
||||
await database_users.delete_many({})
|
||||
# database_users = request.app.db["users"]
|
||||
# await database_users.delete_many({})
|
||||
|
||||
users = []
|
||||
admin_user = UserWithPassword(
|
||||
username=f"admin",
|
||||
email=f"admin@admin.admin",
|
||||
password="admin",
|
||||
user_type="isOwner",
|
||||
)
|
||||
# users = []
|
||||
# admin_user = UserWithPassword(
|
||||
# username=f"admin",
|
||||
# email=f"admin@admin.admin",
|
||||
# password="admin",
|
||||
# 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):
|
||||
user = UserWithPassword(
|
||||
username=fake.simple_profile()['username'],
|
||||
email=fake.email(),
|
||||
password=fake.password(),
|
||||
user_type="isOwner",
|
||||
full_name=fake.name(),
|
||||
)
|
||||
users.append(user)
|
||||
# for i in range(0, 20):
|
||||
# user = UserWithPassword(
|
||||
# username=fake.simple_profile()['username'],
|
||||
# email=fake.email(),
|
||||
# password=fake.password(),
|
||||
# user_type="isOwner",
|
||||
# full_name=fake.name(),
|
||||
# )
|
||||
# users.append(user)
|
||||
|
||||
for user in users:
|
||||
await create_user(request, user)
|
||||
# for user in users:
|
||||
# await create_user(request, user)
|
||||
|
||||
# find admin user
|
||||
users = request.app.db["users"]
|
||||
admin_user = await users.find_one({"username": "admin"})
|
||||
# # find admin user
|
||||
# users = request.app.db["users"]
|
||||
# admin_user = await users.find_one({"username": "admin"})
|
||||
|
||||
if admin_user:
|
||||
admin_user = UserInDB(**admin_user)
|
||||
current_user = PublicUser(**admin_user.dict())
|
||||
else:
|
||||
raise Exception("Admin user not found")
|
||||
# if admin_user:
|
||||
# admin_user = UserInDB(**admin_user)
|
||||
# current_user = PublicUser(**admin_user.dict())
|
||||
# else:
|
||||
# raise Exception("Admin user not found")
|
||||
|
||||
# Create organizations
|
||||
########################################
|
||||
# # Create organizations
|
||||
# ########################################
|
||||
|
||||
database_orgs = request.app.db["organizations"]
|
||||
await database_orgs.delete_many({})
|
||||
# database_orgs = request.app.db["organizations"]
|
||||
# await database_orgs.delete_many({})
|
||||
|
||||
organizations = []
|
||||
for i in range(0, 5):
|
||||
company = fake.company()
|
||||
# remove whitespace and special characters and make lowercase
|
||||
slug = ''.join(e for e in company if e.isalnum()).lower()
|
||||
org = Organization(
|
||||
name=company,
|
||||
description=fake.unique.text(),
|
||||
email=fake.unique.email(),
|
||||
slug=slug,
|
||||
)
|
||||
organizations.append(org)
|
||||
await create_org(request, org, current_user)
|
||||
# organizations = []
|
||||
# for i in range(0, 5):
|
||||
# company = fake.company()
|
||||
# # remove whitespace and special characters and make lowercase
|
||||
# slug = ''.join(e for e in company if e.isalnum()).lower()
|
||||
# org = Organization(
|
||||
# name=company,
|
||||
# description=fake.unique.text(),
|
||||
# email=fake.unique.email(),
|
||||
# slug=slug,
|
||||
# )
|
||||
# organizations.append(org)
|
||||
# await create_org(request, org, current_user)
|
||||
|
||||
# Create roles
|
||||
########################################
|
||||
# # Create roles
|
||||
# ########################################
|
||||
|
||||
database_roles = request.app.db["roles"]
|
||||
await database_roles.delete_many({})
|
||||
# database_roles = request.app.db["roles"]
|
||||
# await database_roles.delete_many({})
|
||||
|
||||
|
||||
|
||||
|
||||
roles = []
|
||||
admin_role = Role(
|
||||
name="admin",
|
||||
description="admin",
|
||||
policies=[RolePolicy(permissions=Permission(
|
||||
action_create=True,
|
||||
action_read=True,
|
||||
action_update=True,
|
||||
action_delete=True,
|
||||
),
|
||||
elements=Elements(
|
||||
courses=["*"],
|
||||
users=["*"],
|
||||
houses=["*"],
|
||||
collections=["*"],
|
||||
organizations=["*"],
|
||||
coursechapters=["*"],
|
||||
lectures=["*"],
|
||||
))],
|
||||
linked_users=[admin_user.user_id],
|
||||
)
|
||||
roles.append(admin_role)
|
||||
# roles = []
|
||||
# admin_role = Role(
|
||||
# name="admin",
|
||||
# description="admin",
|
||||
# policies=[RolePolicy(permissions=Permission(
|
||||
# action_create=True,
|
||||
# action_read=True,
|
||||
# action_update=True,
|
||||
# action_delete=True,
|
||||
# ),
|
||||
# elements=Elements(
|
||||
# courses=["*"],
|
||||
# users=["*"],
|
||||
# houses=["*"],
|
||||
# collections=["*"],
|
||||
# organizations=["*"],
|
||||
# coursechapters=["*"],
|
||||
# lectures=["*"],
|
||||
# ))],
|
||||
# linked_users=[admin_user.user_id],
|
||||
# )
|
||||
# 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_chapters = request.app.db["coursechapters"]
|
||||
await database_courses.delete_many({})
|
||||
await database_chapters.delete_many({})
|
||||
# database_courses = request.app.db["courses"]
|
||||
# database_chapters = request.app.db["coursechapters"]
|
||||
# await database_courses.delete_many({})
|
||||
# await database_chapters.delete_many({})
|
||||
|
||||
courses = []
|
||||
orgs = request.app.db["organizations"]
|
||||
# courses = []
|
||||
# orgs = request.app.db["organizations"]
|
||||
|
||||
if await orgs.count_documents({}) > 0:
|
||||
for org in await orgs.find().to_list(length=100):
|
||||
for i in range(0, 5):
|
||||
# if await orgs.count_documents({}) > 0:
|
||||
# for org in await orgs.find().to_list(length=100):
|
||||
# for i in range(0, 5):
|
||||
|
||||
# get image in BinaryIO format from unsplash and save it to disk
|
||||
image = requests.get(
|
||||
"https://source.unsplash.com/random/800x600")
|
||||
with open("thumbnail.jpg", "wb") as f:
|
||||
f.write(image.content)
|
||||
# # get image in BinaryIO format from unsplash and save it to disk
|
||||
# image = requests.get(
|
||||
# "https://source.unsplash.com/random/800x600")
|
||||
# with open("thumbnail.jpg", "wb") as f:
|
||||
# f.write(image.content)
|
||||
|
||||
course_id = f"course_{uuid4()}"
|
||||
course = CourseInDB(
|
||||
name=fake_multilang.unique.sentence(),
|
||||
description=fake_multilang.unique.text(),
|
||||
mini_description=fake_multilang.unique.text(),
|
||||
thumbnail="thumbnail",
|
||||
org_id=org['org_id'],
|
||||
learnings=[fake_multilang.unique.sentence()
|
||||
for i in range(0, 5)],
|
||||
public=True,
|
||||
chapters=[],
|
||||
course_id=course_id,
|
||||
creationDate=str(datetime.now()),
|
||||
updateDate=str(datetime.now()),
|
||||
authors=[current_user.user_id],
|
||||
)
|
||||
# course_id = f"course_{uuid4()}"
|
||||
# course = CourseInDB(
|
||||
# name=fake_multilang.unique.sentence(),
|
||||
# description=fake_multilang.unique.text(),
|
||||
# mini_description=fake_multilang.unique.text(),
|
||||
# thumbnail="thumbnail",
|
||||
# org_id=org['org_id'],
|
||||
# learnings=[fake_multilang.unique.sentence()
|
||||
# for i in range(0, 5)],
|
||||
# public=True,
|
||||
# chapters=[],
|
||||
# course_id=course_id,
|
||||
# creationDate=str(datetime.now()),
|
||||
# updateDate=str(datetime.now()),
|
||||
# authors=[current_user.user_id],
|
||||
# )
|
||||
|
||||
courses = request.app.db["courses"]
|
||||
name_in_disk = f"test_mock{course_id}.jpeg"
|
||||
# courses = request.app.db["courses"]
|
||||
# name_in_disk = f"test_mock{course_id}.jpeg"
|
||||
|
||||
image = requests.get(
|
||||
"https://source.unsplash.com/random/800x600")
|
||||
with open(f"content/uploads/img/{name_in_disk}", "wb") as f:
|
||||
f.write(image.content)
|
||||
# image = requests.get(
|
||||
# "https://source.unsplash.com/random/800x600")
|
||||
# with open(f"content/uploads/img/{name_in_disk}", "wb") as f:
|
||||
# f.write(image.content)
|
||||
|
||||
course.thumbnail = name_in_disk
|
||||
# course.thumbnail = name_in_disk
|
||||
|
||||
course = CourseInDB(**course.dict())
|
||||
course_in_db = await courses.insert_one(course.dict())
|
||||
# course = CourseInDB(**course.dict())
|
||||
# course_in_db = await courses.insert_one(course.dict())
|
||||
|
||||
# create chapters
|
||||
for i in range(0, 5):
|
||||
coursechapter = CourseChapter(
|
||||
name=fake_multilang.unique.sentence(),
|
||||
description=fake_multilang.unique.text(),
|
||||
lectures=[],
|
||||
)
|
||||
coursechapter = await create_coursechapter(request,coursechapter, course_id, current_user)
|
||||
pprint(coursechapter)
|
||||
if coursechapter:
|
||||
# create lectures
|
||||
for i in range(0, 5):
|
||||
lecture = Lecture(
|
||||
name=fake_multilang.unique.sentence(),
|
||||
type="dynamic",
|
||||
content={},
|
||||
)
|
||||
lecture = await create_lecture(request,lecture, coursechapter['coursechapter_id'], current_user)
|
||||
# # create chapters
|
||||
# for i in range(0, 5):
|
||||
# coursechapter = CourseChapter(
|
||||
# name=fake_multilang.unique.sentence(),
|
||||
# description=fake_multilang.unique.text(),
|
||||
# lectures=[],
|
||||
# )
|
||||
# coursechapter = await create_coursechapter(request,coursechapter, course_id, current_user)
|
||||
# pprint(coursechapter)
|
||||
# if coursechapter:
|
||||
# # create lectures
|
||||
# for i in range(0, 5):
|
||||
# lecture = Lecture(
|
||||
# name=fake_multilang.unique.sentence(),
|
||||
# type="dynamic",
|
||||
# content={},
|
||||
# )
|
||||
# lecture = await create_lecture(request,lecture, coursechapter['coursechapter_id'], current_user)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import json
|
|||
from typing import List
|
||||
from uuid import uuid4
|
||||
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 fastapi import FastAPI, HTTPException, status, Request, Response, BackgroundTasks
|
||||
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 passlib.context import CryptContext
|
||||
from passlib.hash import pbkdf2_sha256
|
||||
from src.services.roles.schemas.roles import RoleInDB
|
||||
|
||||
from src.services.users.schemas.users import User, UserInDB
|
||||
|
||||
### 🔒 JWT ##############################################################
|
||||
|
||||
|
|
@ -32,29 +36,36 @@ async def verify_user_rights_with_roles(request: Request,action: str, user_id: s
|
|||
Check if the user has the right to perform the action on the element
|
||||
"""
|
||||
roles = request.app.db["roles"]
|
||||
users = request.app.db["users"]
|
||||
|
||||
# find data where user_id is in linked_users or * is in linked_users
|
||||
user_roles_cursor = roles.find({"$or": [{"linked_users": user_id}, {"linked_users": "*"}]})
|
||||
# Check if the user is an admin
|
||||
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 = []
|
||||
|
||||
# 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):
|
||||
# Check if user is owner or reader of the organization
|
||||
if org.org_role == ("owner" or "editor"):
|
||||
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):
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ class User(BaseModel):
|
|||
verified: bool | None = False
|
||||
user_type: str | None = None
|
||||
bio: str | None = None
|
||||
orgs: list
|
||||
|
||||
|
||||
class UserWithPassword(User):
|
||||
password: str
|
||||
|
|
@ -24,9 +26,11 @@ class UserWithPassword(User):
|
|||
|
||||
class PublicUser(User):
|
||||
user_id: str
|
||||
orgs: list
|
||||
creationDate: str
|
||||
updateDate: str
|
||||
|
||||
|
||||
class PasswordChangeForm(BaseModel):
|
||||
old_password: str
|
||||
new_password: str
|
||||
|
|
@ -86,7 +90,6 @@ async def get_profile_metadata(request: Request, user):
|
|||
|
||||
user_roles_list = []
|
||||
for role in await user_roles.to_list(length=100):
|
||||
print(role)
|
||||
user_roles_list.append(Role(**role))
|
||||
|
||||
return {
|
||||
|
|
@ -142,7 +145,6 @@ async def update_user(request: Request, user_id: str, user_object: User):
|
|||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT, detail="User does not exist")
|
||||
|
||||
|
||||
# TODO : fix this
|
||||
|
||||
# okay if username is not changed
|
||||
|
|
@ -154,7 +156,6 @@ async def update_user(request: Request, user_id: str, user_object: User):
|
|||
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)
|
||||
|
||||
|
|
|
|||
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