diff --git a/backend_py/__pycache__/db_handler.cpython-312.pyc b/backend_py/__pycache__/db_handler.cpython-312.pyc index 7e95b6c..4a1534c 100644 Binary files a/backend_py/__pycache__/db_handler.cpython-312.pyc and b/backend_py/__pycache__/db_handler.cpython-312.pyc differ diff --git a/backend_py/db_handler.py b/backend_py/db_handler.py index 1b5f502..ad9c9bd 100644 --- a/backend_py/db_handler.py +++ b/backend_py/db_handler.py @@ -37,7 +37,9 @@ class DbConnector: except ValueError: print("Default admin user already exists") - def get_user(self, name: str) -> User | None: + def get_user(self, name: str) -> User | dict[User] | None: + if not name: + return self.session.query(User).all() return self.session.query(User).filter(User.name==name).first() def add_user(self, name: str, hash: str, role: str = "user"): @@ -50,6 +52,13 @@ class DbConnector: self.session.add(new_user) self.session.commit() + def delete_user(self, name: str): + user = self.get_user(name) + if not user: + raise ValueError("User does not exist") + self.session.delete(user) + self.session.commit() + def add_msg_to_room(self, room: str, msg: str, user: str): new_msg = Message(room=room, content=msg, user=user, timestamp=int(time.time())) self.session.add(new_msg) diff --git a/backend_py/endpoints/__pycache__/auth.cpython-312.pyc b/backend_py/endpoints/__pycache__/auth.cpython-312.pyc index 2e8db6f..7c55c9a 100644 Binary files a/backend_py/endpoints/__pycache__/auth.cpython-312.pyc and b/backend_py/endpoints/__pycache__/auth.cpython-312.pyc differ diff --git a/backend_py/endpoints/auth.py b/backend_py/endpoints/auth.py index f04eb99..1186e6d 100644 --- a/backend_py/endpoints/auth.py +++ b/backend_py/endpoints/auth.py @@ -16,6 +16,20 @@ from utils import read_keys_from_request app = Bottle() +def username_by_token(request) -> str | None: + token = request.get_cookie("oauth2") + if not token: + return None + + try: + decoded = jwt.decode(token, JWT_SECRET, algorithms=["HS256"], options={"verify_sub": False}) + curent_time = time.time() + if decoded.get("exp", float("inf")) + decoded.get("iat", float("inf")) < curent_time: + return None + return decoded["sub"]["user"] + except (jwt.ExpiredSignatureError, jwt.InvalidTokenError) as e: + print(f"Token error: {e}") + return None def user_guard(reyection_msg: str = "Requires authentication", allow_anonymous: bool = False): def user_guard_decorator(fn: callable): @@ -74,22 +88,6 @@ def token(): return dumps(jwt_content) -def username_by_token(request) -> str | None: - token = request.get_cookie("oauth2") - if not token: - return None - - try: - decoded = jwt.decode(token, JWT_SECRET, algorithms=["HS256"], options={"verify_sub": False}) - curent_time = time.time() - if decoded.get("exp", float("inf")) + decoded.get("iat", float("inf")) < curent_time: - return None - return decoded["sub"]["user"] - except (jwt.ExpiredSignatureError, jwt.InvalidTokenError) as e: - print(f"Token error: {e}") - return None - - @app.route("/", method=["GET"]) def get_user(): username = username_by_token(request) @@ -113,5 +111,24 @@ def add_user(user): except ValueError as e: response.status = 400 return dumps({"error": str(e)}) - + +@app.route("/delete/", method=["POST"]) +@admin_guard() +def delete_user(_, deletion_target: str): + response.content_type = 'application/json' + try: + request.db_connector.delete_user(deletion_target) + response.status = 200 + return dumps({"message": "User deleted successfully"}) + except ValueError as e: + response.status = 400 + return dumps({"error": str(e)}) + +@app.route("/getAll", method=["GET"]) +@admin_guard() +def get_all_users(_): + response.content_type = 'application/json' + users = request.db_connector.get_user(None) + user_list = [{"name": u.name, "role": u.role} for u in users] + return dumps(user_list) diff --git a/data/db.sqlite b/data/db.sqlite index d5306e0..9140008 100644 Binary files a/data/db.sqlite and b/data/db.sqlite differ diff --git a/frontend/src/composable/auth.ts b/frontend/src/composable/auth.ts index 224d80c..40ed8db 100644 --- a/frontend/src/composable/auth.ts +++ b/frontend/src/composable/auth.ts @@ -1,6 +1,7 @@ import { API_URL } from '@/main' import { getJsonOrError } from '@/composable/utils' import { computed, ref, type Ref } from 'vue' +import router from '@/router' export interface User { user: string @@ -14,7 +15,7 @@ const readToken = () => { } return null } - +export let allUserStorage: Ref = ref(null) const userHandler = () => { let curentUser: Ref = ref(null) @@ -60,6 +61,24 @@ const userHandler = () => { }) } + const getAllUsers = async (): Promise => { + return fetch(`${API_URL}/user/getAll`, { + method: 'GET', + credentials: 'include', // set coockies from responce + }).then(async (response) => { + if (response.ok) { + let data = await getJsonOrError(response) + return data.map((user: any) => ({ + user: user.name as string, + role: user.role as string, + })) as User[] + } else { + console.error('Error fetching all users:', await response.text()) + return null + } + }) + } + const currentUser = (): User | null => { if (curentUser.value === null) { try { @@ -67,15 +86,33 @@ const userHandler = () => { } catch (e) { console.error('Error getting session from JWT:', e) curentUser.value = null + router.push('/login') } } return curentUser.value } + const allUsers = computed((): User[] | null => { + if (curentUser.value?.role !== 'admin') { + return null + } + if (allUserStorage.value === null) { + getAllUsers() + .then((users) => { + allUserStorage.value = users + }) + .catch((error) => { + console.error('Error fetching all users:', error) + }) + } + return allUserStorage.value + }) + return { getSessionFromJWT, requestToken, removeToken, + allUsers, currentUser: computed(() => currentUser()), } } diff --git a/frontend/src/composable/settings.ts b/frontend/src/composable/settings.ts new file mode 100644 index 0000000..2bae06f --- /dev/null +++ b/frontend/src/composable/settings.ts @@ -0,0 +1,51 @@ +import { type User, allUserStorage } from '@/composable/auth' +import { API_URL } from '@/main' + +export const deleteUser = async (user: User): Promise => { + return fetch(`${API_URL}/user/delete/${user.user}`, { + method: 'POST', + credentials: 'include', // set cookies from response + headers: { + 'Content-Type': 'application/json', + }, + }).then(async (response) => { + if (response.ok) { + // Remove user from allUserStorage + const index = allUserStorage.value?.findIndex((u) => u.user === user.user) + if (index !== undefined && index >= 0) { + allUserStorage.value?.splice(index, 1) + } + } else { + console.error('Error deleting user:', await response.text()) + } + }) +} + +export const addUser = async ( + username: string, + password: string, + new_admin: boolean, +): Promise => { + return fetch(`${API_URL}/user/add`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + credentials: 'include', + }, + body: JSON.stringify({ + new_user: username, + new_password: password, + new_admin: new_admin, + }), + }).then(async (response) => { + if (response.ok) { + let data = await response.json() + allUserStorage.value?.push({ + user: username, + role: new_admin ? 'admin' : 'user', + } as User) + return data.message + } + throw new Error('Failed to create user. Try another username.') + }) +} diff --git a/frontend/src/views/User.vue b/frontend/src/views/User.vue index 6b24767..cdc3471 100644 --- a/frontend/src/views/User.vue +++ b/frontend/src/views/User.vue @@ -2,40 +2,28 @@ import { ref } from 'vue' import { API_URL } from '@/main.ts' import { type User, primaryUser } from '@/composable/auth.ts' +import { deleteUser, addUser } from '@/composable/settings' import UserInfo from '@/components/UserInfo.vue' const new_user_name = ref('') const new_user_passwd = ref('') const new_admin = ref(false) -const msg = ref({ message: '', type: 'info' }) +const userCreationMsg = ref({ message: '', type: 'info' }) +const userDeletionMsg = ref({ message: '', type: 'info' }) const onNewUserCreation = async () => { - try { - const response = await fetch(`${API_URL}/user/add`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - credentials: 'include', - }, - body: JSON.stringify({ - new_user: new_user_name.value, - new_password: new_user_passwd.value, - new_admin: new_admin.value, - }), + addUser(new_user_name.value, new_user_passwd.value, new_admin.value) + .then(() => { + userCreationMsg.value = { message: 'User created successfully', type: 'success' } + new_user_name.value = '' + new_user_passwd.value = '' + new_admin.value = false + }) + .catch((error) => { + userCreationMsg.value = { message: `${error}`, type: 'error' } + console.error(error) }) - - const data = await response.json() - if (response.ok) { - msg.value = { message: data.message, type: 'success' } - } else { - throw new Error(data.error || 'Failed to create user') - } - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : 'Unknown error' - msg.value = { message: `Error creating user: ${errorMessage}`, type: 'error' } - console.error('Error creating user:', error) - } } @@ -47,24 +35,58 @@ const onNewUserCreation = async () => {

Name: {{ primaryUser.currentUser.value.user }}

Role: {{ primaryUser.currentUser.value.role }}

-
-

New user

- - - - - - - - - - + +
+

You need Admin rights to see the rest...

-
-

No user information...

-