diff --git a/.gitignore b/.gitignore index 9994306..676baac 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,9 @@ venv/ # .env .env +# data +data/ + # testing test.py test.sql diff --git a/requirements.txt b/requirements.txt index b748448..0737363 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/src/api/general.py b/src/api/general.py index 904bb28..6eba691 100644 --- a/src/api/general.py +++ b/src/api/general.py @@ -7,7 +7,7 @@ from api.models import User from api.utils import get_current_user from settings.settings import load_settings, reset_settings, save_settings -general_router = APIRouter(prefix="/api", tags=["status"]) +general_router = APIRouter(prefix="/api", tags=["general"]) @general_router.get('/ping') diff --git a/src/api/groups.py b/src/api/groups.py new file mode 100644 index 0000000..5157cc6 --- /dev/null +++ b/src/api/groups.py @@ -0,0 +1,72 @@ +from typing import Annotated + +from fastapi import APIRouter, Depends, HTTPException, status +from psycopg2._psycopg import connection + +import db.users as db +import settings.settings as settings +from api.models import User +from api.utils import get_current_user +from db.internal import get_db_connection + +groups_router = APIRouter(prefix="/api/groups", tags=["groups"]) + + +@groups_router.get("/my") +async def read_users_groups(current_user: Annotated[User, Depends(get_current_user)]): + return current_user + +@groups_router.post("/user") +async def read_users_any_groups( + username: str, + conn: Annotated[connection, Depends(get_db_connection)], + current_user: Annotated[User, Depends(get_current_user)] +): + if current_user.role not in settings.settings.admin_roles: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Not allowed", + ) + user = User() + user_data = db.get_user(conn, username) + if user_data is None: + return HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="No such user", + ) + user.fill(user_data) + return user + + +@groups_router.post("/add") +async def add_group( + groupname: str, + conn: Annotated[connection, Depends(get_db_connection)], + current_user: Annotated[User, Depends(get_current_user)] +): + # TODO + pass + # if not settings.settings.allow_create_admins_by_admins: + # if current_user.role not in settings.settings.admin_roles: + # raise HTTPException( + # status_code=status.HTTP_403_FORBIDDEN, + # detail="Not allowed", + # ) + # return db.create_user(conn, username, hashed_password, "admin") + + +@groups_router.post("/delete") +async def delete_user( + groupname: str, + conn: Annotated[connection, Depends(get_db_connection)], + current_user: Annotated[User, Depends(get_current_user)] +): + # TODO + pass + # if current_user.username == username or current_user.role in settings.settings.admin_roles: + # return db.delete_user(conn, groupname) + # else: + # raise HTTPException( + # status_code=status.HTTP_403_FORBIDDEN, + # detail="Not allowed", + # ) diff --git a/src/api/users.py b/src/api/users.py index 164abb2..30f48a5 100644 --- a/src/api/users.py +++ b/src/api/users.py @@ -100,6 +100,21 @@ async def update_disabled( detail="Not allowed", ) +@users_router.post("/update/role") +async def update_role( + username: str, + role: str, + conn: Annotated[connection, Depends(get_db_connection)], + current_user: Annotated[User, Depends(get_current_user)] +): + if current_user.role in settings.settings.admin_roles: + return db.update_user_role(conn, username, role) + else: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Not allowed", + ) + @users_router.post("/update/username") async def update_username( diff --git a/src/create_app.py b/src/create_app.py index 806c671..431e9ba 100644 --- a/src/create_app.py +++ b/src/create_app.py @@ -1,8 +1,12 @@ +import sys + from fastapi import FastAPI +from loguru import logger from api.anon import anon_router from api.auth import auth_router from api.general import general_router +from api.groups import groups_router from api.users import users_router from db.internal import connect_db, disconnect_db from settings import startup_settings @@ -19,13 +23,24 @@ app = FastAPI( def create_app(): + logger.configure( + handlers=[ + { + "sink": sys.stdout, + "level": startup_settings.log_level, + "format": "{level}: {message}", + } + ] + ) + app.add_event_handler("startup", connect_db) app.add_event_handler("startup", settings_up) app.include_router(general_router) app.include_router(auth_router) - app.include_router(users_router) app.include_router(anon_router) + app.include_router(users_router) + app.include_router(groups_router) app.add_event_handler("shutdown", disconnect_db) app.add_event_handler("shutdown", settings_down) diff --git a/src/db/users.py b/src/db/users.py index 33e317b..a295a00 100644 --- a/src/db/users.py +++ b/src/db/users.py @@ -75,6 +75,40 @@ def check_user_disabled( # user updates +def update_user_disabled( + conn: connection, + username: str, + disabled: bool +): + # if disabled = True => user is disabled + with conn.cursor() as cur: + cur.execute( + """ + update picrinth.users + set disabled = %s + where username = %s + """, + (disabled, username), + ) + conn.commit() + +def update_user_role( + conn: connection, + username: str, + role: str +): + with conn.cursor() as cur: + cur.execute( + """ + update picrinth.users + set role = %s + where username = %s + """, + (role, username), + ) + conn.commit() + + def update_user_username( conn: connection, username: str, @@ -108,24 +142,6 @@ def update_user_password( conn.commit() -def update_user_disabled( - conn: connection, - username: str, - disabled: bool -): - # if disabled = True -> user is disabled - with conn.cursor() as cur: - cur.execute( - """ - update picrinth.users - set disabled = %s - where username = %s - """, - (disabled, username), - ) - conn.commit() - - def update_user_last_seen( conn: connection, username: str diff --git a/src/settings/startup_settings.py b/src/settings/startup_settings.py index fed0c83..ff5f6b5 100644 --- a/src/settings/startup_settings.py +++ b/src/settings/startup_settings.py @@ -20,3 +20,4 @@ access_token_expiration_time = int(config('access_token_expiration_time', defau # other settings swagger_enabled = str_to_bool(str(config('swagger_enabled', 'false'))) +log_level = str(config('log_level', default='INFO')) diff --git a/tables.sql b/tables.sql index 687b26c..0698691 100644 --- a/tables.sql +++ b/tables.sql @@ -5,17 +5,24 @@ CREATE TABLE picrinth.users ( "role" text not null default "user", "disabled" bool not null, groups_ids integer[] NULL, - last_seen_at timestamp with time zone NULL, - created_at timestamp with time zone NULL, - CONSTRAINT userid_pk PRIMARY KEY (id), + last_seen_at timestamp with time zone null, + created_at timestamp with time zone null, CONSTRAINT username_unique UNIQUE (username) ); CREATE TABLE picrinth.groups ( id serial not null, groupname text not null, - join_code text not null, - users_ids integer[] null, + invite_code text not null, created_at timestamp with time zone null, - CONSTRAINT groupname_unique UNIQUE (username) + CONSTRAINT groupname_unique UNIQUE (groupname) +); + +create table picrinth.group_members ( + username text, + groupname text, + joined_at timestamp with time zone null, + PRIMARY KEY (username, groupname) + FOREIGN KEY (username) REFERENCES users (username) on delete cascade on update cascade + FOREIGN KEY (groupname) REFERENCES groups (groupname) on delete cascade on update cascade );