From 45e929138b0a779008eb1b76ac58c9252156a66e Mon Sep 17 00:00:00 2001 From: Kyattsukuro Date: Fri, 15 Aug 2025 17:29:34 +0200 Subject: [PATCH] added user delition to webui --- .../__pycache__/db_handler.cpython-312.pyc | Bin 4424 -> 5017 bytes backend_py/db_handler.py | 11 +- .../__pycache__/auth.cpython-312.pyc | Bin 6380 -> 7712 bytes backend_py/endpoints/auth.py | 51 ++++++--- data/db.sqlite | Bin 16384 -> 16384 bytes frontend/src/composable/auth.ts | 39 ++++++- frontend/src/composable/settings.ts | 51 +++++++++ frontend/src/views/User.vue | 106 +++++++++++------- 8 files changed, 197 insertions(+), 61 deletions(-) create mode 100644 frontend/src/composable/settings.ts diff --git a/backend_py/__pycache__/db_handler.cpython-312.pyc b/backend_py/__pycache__/db_handler.cpython-312.pyc index 7e95b6c446532020e95d583ce4ffc2f63b27a02f..4a1534cdb1bc2e5d78c9ade09567c75c14b47959 100644 GIT binary patch delta 858 zcmYjPUr19?7{6!t-tF%GUAxm^Q=7(+rM8xkAxKj$3I=gucx>K8>3 z8L)SwFJ@QVZ`B{@bSyG!-op_az)a)FuxUV$oHflxo*D5zH7+!%nrPo55oR zt8@Z%I0YTFMQ7Mok>YoU&2$q0rirahyzInRGt_M8@g(*0X9h5>DRJo7d7OZ zBkQoqbGIL5$xEq;Ob9+D>o`#n(_oJ{A4!PK%Z7>VVA{+C@xq*)3G>zfbg`lo>bGzj zIE6FViUAX5tsW|PDVzbY=^=u_pydZ&ks7JD=WT2)`TxFWGGkij{}cLY*%g2l=1|ku zyK`%~{8UNne8|D0eDk&*v|TKD`wry3LqT4b*W|JgC<=jm{HxGW7?up=9E4gcM&8IR z=qmXnkE5YNv+@9ST&8>*g&+kwC(Jd@TW7D44{8F95=AqShvc-gJr&NFOJ*7uzCdf} zv8*u)aexY!f)}yqPqA*0k6K6aDQs7ns)OT$dAr7dJC-@Vzbdo*7|Ksp8S1yDw&Q!g zo$JNWrT4yKC|0YpKShCWrzzmJ*}B{7tI??H<{9 delta 412 zcmbQKenN@wG%qg~0}upuO~{Dk*vR*WQP@mBBR@A)zZgj5=cVd9=clA@7Gb*1$k;IX z4YPV&3R@LB149~13VRDn6nlzb3QH<;3MUYAr81|nrEs^fMscL@0QtN?%mEhT1FGaq z5dw?yr*c6|NMTLoPUVpV+6~kt0Mx}>$)+j1xszoHGwU3n==8}GIP4~K3fMDcaZg^z zBP9{cu#(YFldVV#D0z#uur#%(vWOeVp6tdM$Yu;=G%#%5#~H`SXgOJyyMj@B@-*($ z%^ke{jBM6G$s(J{cljh4?IwTc^J6sK?8e{BsAvunmjw|DK;jm2Vor`GOOXy-)OGSJ zfgncP$+m*|jC_;V3MwlWS%M@DK!gj3Z~zjwSaLFpONu}~De{>7M^KTmWwM-*gcKvI z2jh(34-6oBLC6Z1h0&kE?2l}dql9E8HwjH*bet?IY{=+0* 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 2e8db6fcdf1eb7cf5ef640716f5545df9da630fb..7c55c9aae6779b2848024d1a1c2cdd4740b945fd 100644 GIT binary patch delta 2568 zcmah~YiLtf96#qi(wj7Co2HMXO*Lwby*iDpwmzqgZ^3nJ^|5uEG0nZUsc9nT-cT)e ztyq|h4V5#Q!k7-mtaG5FUpDZIV_$}YSt^JZ8Qa*G!3I@fu)!Yx|4Gx@;kE#2R5SNwO?74=)|=h`>tTGFk-Gyzyp5+nj+WMqUzVa~giZM2bJQ8M#{P#a1L9U-p3 z@(>8KC~sD`(nUP4>g{WQc$GR@Dw)Y7yECRPi@ghKYk_IWsCFnKPH;SEbD}4oWCq&+ zjD`GLHA2_$ztlKg#^bI(_N)LZivj40qp)1YWN0Q^iGbCus|8F@fi>)iDq7aJ%SlA9l zhf0%1e>ywu(H3B=w$Vxj1 zrEh>`U{fv3Sq(xJLIr?M9d|O*lVfSSx1W^*g@5R6kFa_aXh4`7Al8hu7GC0O7;eXD z7@-cpahciK%JWz=b*Y!I=aH`%Nju3}QGN%4FuxRP7V0-^n^~JJ1oBMXK&T6WMyg;v zi|l#em>vn|202Y=Hcs*@QH_{PIzQ5+{%wQ;H zm>JVHV;jXkoy#U5oY2~vobrEAQWB^Y2l|WE_xM+UcZosmB8Mch;LtAwT`m)i6|E5E0OC$#WAt!YAQ8k>JtTgPwKZ0p4yd%G>JkXwp7zE-~Fi^NyS zx0X|!uTlXAOJb1ZcqWsY$rh-f=_0FNz~tYu`jnjF!%K=e9ik`bHgZNvNj)Sby#_r$ zB%P4@JeYY|>ht0=JuJN^os-^bP{@!xB-waFh;c$L?$Qam6uu#80K!zr3S5TRc%ry< z?P(zweKsppxP29d!E;n>8Ox!0$DJR>D?O98?78jX_#h|=cosy;EY=Tt!|+*q0iKNh z18?A-H!|Uk-1F)aUVW@_(ibBmdo3wxV{P=V@Hcx5hh~29dn^;F9AVv*0?VnYX{FQaE|?b%pw42U^H5S~ct$w-P=Tc|^KanQDTDw3 delta 1525 zcmZ`&O>7%Q6yCLWU3+7vPVG29ja@?`>P@RU4NVV8)Fil~7*TpC2+0E6O=g@pS?@+~ z*DZ;V8l)V!K^VjZ36&ddkf`J{7gSDgLEPqoR3RjOgs6I}gv5JehY+N$x^|Y{)%JTXwHzBjMsxd^rXkCTU86Si6qXFjHjdB?{)cAx zDM7iTsUsTC9;9YNhw%rt+Q^n^kBZ62d!0{WAL|)8&<;3wPK(!XB% z?G22Fyl@j0eNfy_*o&vocvqM46~}d*WtZDgm986B#KRy;VK<79$A4*2Y4z2%0}RH| zkQF0VQ9mVSttCAz-nIUIC4*MTBG`sBz;`)N<&YzwTenRZqxA^ED8Zpd6SqpDN(Ke- zZQr6kA!5lFr>IUl0;EAD#ySr*DtW5TBC10dIVgVW8x(uVv*N3SDZWWgUmyZ_rprS9 zU?T)HH3!=I9M)=jJ#mwG@T@4MP+X^)ibu;-w2W^$L2%0l_Unf_QjpB;Ox3&TxJ_1k z`TDh2iwz%IP6!#Xlb)DULQCk>ypM(3Q5g-|%QD(@LK)@nY)bQ`>(etAZCxfDch!ex zxYm*}-)VSuz!|-BFY>i+!3;S(k05Ru6W3rGS18zPz#%oc4+S}3ts zCR`@BO9Z36{u8NNNFaj4p%G_I`DSVQ4HaFeoiW4?{ zue=`kp1LrZeBGlKW52gKgBQ@#jwu3&5mk3w#qXJsQR+~D8nvGKVLB&xOBAwKa55U% z2m0t~vZ|}D(duo2I>DT{oGUF*r@9X!1xZ?yl5UnaX{D0VWy-k2SY5sS1RX39 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 d5306e04abe76a5cc65816f271ee0aaee40f5384..9140008f549b33776f425c02cbda1048c339165a 100644 GIT binary patch delta 310 zcmZo@U~Fh$oFL7pJ5k1&QFmj)5`G?LesKo=&HSnS;(TW}3o5Yld-*W4N;k5K=I5pQ z<`<=^7$vC~8mXj(xOoO;Rg|Zfr+WoPRg~u!rj@0onwEza7Dq*7`k9-gCkGWf7n=Gd zMwmJUdjzJYnmPwhPL!84Eer`V^QbcLOUp=jWyZ^<~Tt u{HehTlr$8FNqVH_QzOxMcoB31u#re)|78KaaSMR~XD%{8_npm8o zVw9v}XrvNS5@4E?TvU)`te=!vnvxrtot9pjTAo{366Bj5R+wAtVqR&QQtXu*;a;g% znQ82qYFV0ETAW(s!^|oTGAS>=BqOy5YMN1&zJXb#NoBHMs!6VkkB4uLqjPAfVM;-+ zZ+JvhV6JFYiBV#bc|mAZiG_b@dT~X*Yk*Vk+*7e6D9u&C0gDEHuE58o6wvqXJUFQZ`h%5W2};?&9O9yLlh*9^$#qbA7XjK|1fm3GSN@ I+1fDy09^fNA^-pY 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...

-