hardening server
This commit is contained in:
		| @ -6,12 +6,14 @@ from user_manager import UserManager | ||||
| from match_manager import MatchManager | ||||
| from match import Match | ||||
|  | ||||
| from tools import debug | ||||
|  | ||||
|  | ||||
| def parse_message(msg: str): | ||||
|     # TODO: make it more robust by validating each part of a message | ||||
|     msg_obj = json.loads(msg) | ||||
|     if "type" not in msg_obj or "data" not in msg_obj: | ||||
|         print("got strange message") | ||||
|         debug("got strange message") | ||||
|         return None | ||||
|  | ||||
|     return msg_obj | ||||
| @ -33,7 +35,7 @@ class Connection(object): | ||||
|         try: | ||||
|             await self.websocket.send(msg) | ||||
|         except Exception as e: | ||||
|             print("error sending message to user " + | ||||
|             debug("error sending message to user " + | ||||
|                   self.user_name + ". Reason: " + str(e)) | ||||
|  | ||||
|     async def close(self): | ||||
| @ -44,7 +46,7 @@ class Connection(object): | ||||
|             self.is_closed = True | ||||
|  | ||||
|         except Exception as e: | ||||
|             print("error closing session to user " + | ||||
|             debug("error closing session to user " + | ||||
|                   self.user_name + ". Reason: " + str(e)) | ||||
|  | ||||
|  | ||||
| @ -114,7 +116,7 @@ class ConnectionHandler(object): | ||||
|                              socket: websockets.WebSocketServerProtocol, | ||||
|                              login_msg: str): | ||||
|         msg = parse_message(login_msg) | ||||
|         print("new incomming connection...") | ||||
|         debug("new incomming connection...") | ||||
|         if msg is None: | ||||
|             return None | ||||
|  | ||||
| @ -395,7 +397,7 @@ class ConnectionHandler(object): | ||||
|                         ) | ||||
|  | ||||
|             except Exception as e: | ||||
|                 print("error processing match request: " + str(data) + str(e)) | ||||
|                 debug("error processing match request: " + str(data) + str(e)) | ||||
|  | ||||
|     async def _on_match_state_req(self, conn, data): | ||||
|         db_matches = self.match_manager.get_matches_for_user(conn.user_name) | ||||
| @ -432,7 +434,7 @@ class ConnectionHandler(object): | ||||
|             if match is not None: | ||||
|                 match_state = match.to_json_state() | ||||
|  | ||||
|                 print(match_state) | ||||
|                 debug(match_state) | ||||
|  | ||||
|                 await conn.send(json.dumps({ | ||||
|                     'type': 'match_update', | ||||
| @ -580,7 +582,7 @@ class ConnectionHandler(object): | ||||
|     async def handle_message(self, conn, msg_str): | ||||
|         msg = parse_message(msg_str) | ||||
|  | ||||
|         print("incoming message" + str(msg)) | ||||
|         debug("incoming message" + str(msg)) | ||||
|  | ||||
|         if msg is None: | ||||
|             return None | ||||
| @ -607,4 +609,4 @@ class ConnectionHandler(object): | ||||
|             await self._on_unfriend_request(conn, msg['data']) | ||||
|  | ||||
|         else: | ||||
|             print("could not interpret message: " + msg_str) | ||||
|             debug("could not interpret message: " + msg_str) | ||||
|  | ||||
| @ -36,23 +36,30 @@ class DatabaseConnection(object): | ||||
|         DatabaseConnection.instance.commit() | ||||
|  | ||||
|     @staticmethod | ||||
|     def global_single_query(query): | ||||
|     def global_single_query(query, params=None): | ||||
|         if ';' in query: | ||||
|             # Possible injection! | ||||
|             raise SQLInjectionError() | ||||
|  | ||||
|         with DatabaseConnection.global_cursor() as c: | ||||
|             c.execute(query) | ||||
|             if params is None: | ||||
|                 c.execute(query) | ||||
|             else: | ||||
|                 c.execute(query, params) | ||||
|  | ||||
|             return c.fetchall() | ||||
|  | ||||
|     @staticmethod | ||||
|     def global_single_execution(sql_statement): | ||||
|     def global_single_execution(sql_statement, params=None): | ||||
|         if ';' in sql_statement: | ||||
|             # Possible injection detected! | ||||
|             raise SQLInjectionError() | ||||
|  | ||||
|         with DatabaseConnection.global_cursor() as c: | ||||
|             c.execute(sql_statement) | ||||
|             if params is None: | ||||
|                 c.execute(sql_statement) | ||||
|             else: | ||||
|                 c.execute(sql_statement, params) | ||||
|             DatabaseConnection.global_commit() | ||||
|  | ||||
|     def __init__(self, | ||||
| @ -84,7 +91,7 @@ class DatabaseConnection(object): | ||||
|                              ":" + | ||||
|                              str(port) + | ||||
|                              "\nCheck the configuration in settings.py!\n") | ||||
|             raise Exception('could not connec to database') | ||||
|             raise Exception('could not connect to database') | ||||
|  | ||||
|     def get_cursor(self): | ||||
|         return self.connection.cursor() | ||||
| @ -96,6 +103,7 @@ class DatabaseConnection(object): | ||||
|     def commit(self): | ||||
|         self.connection.commit() | ||||
|  | ||||
|  | ||||
| def test_connection(): | ||||
|     import settings | ||||
|     DatabaseConnection(settings.db_host, | ||||
| @ -103,4 +111,4 @@ def test_connection(): | ||||
|                        settings.db_user, | ||||
|                        settings.db_pw, | ||||
|                        settings.db_db, | ||||
|                        settings.db_charset) | ||||
|                        settings.db_charset) | ||||
|  | ||||
							
								
								
									
										23
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								main.py
									
									
									
									
									
								
							| @ -12,6 +12,8 @@ from user_manager import UserManager | ||||
| from database_connection import DatabaseConnection | ||||
| import datetime | ||||
|  | ||||
| from tools import debug | ||||
|  | ||||
| um = UserManager() | ||||
| sm = SessionManager(datetime.timedelta(hours=12)) | ||||
| mm = MatchManager() | ||||
| @ -29,7 +31,6 @@ DatabaseConnection(db_host, | ||||
| async def socket_worker(websocket, path): | ||||
|     connection = None | ||||
|  | ||||
|  | ||||
|     try: | ||||
|         raw_msg = await websocket.recv() | ||||
|  | ||||
| @ -37,21 +38,25 @@ async def socket_worker(websocket, path): | ||||
|  | ||||
|         if connection is None: | ||||
|             return | ||||
|          | ||||
|         print("successfull logged in user: " + connection.user_name) | ||||
|          | ||||
|  | ||||
|         debug("successfull logged in user: " + connection.user_name) | ||||
|  | ||||
|         async for m in websocket: | ||||
|             await ch.handle_message(connection, m) | ||||
|  | ||||
|     except websockets.ConnectionClosed as e: | ||||
|         # nothing suspicious here | ||||
|         pass | ||||
|  | ||||
|     except Exception as e: | ||||
|         # TODO: each disconnect is an exception so far | ||||
|         if connection is not None: | ||||
|             print("catched exception in worker for user: " + | ||||
|             debug("catched exception in worker for user: " + | ||||
|                   connection.user_name + ": " + str(e)) | ||||
|         else: | ||||
|             print("catched exception in worker for unknown user: " + str(e)) | ||||
|          | ||||
|         print(traceback.print_exc()) | ||||
|             debug("catched exception in worker for unknown user: " + str(e)) | ||||
|  | ||||
|         debug(traceback.debug_exc()) | ||||
|  | ||||
|     finally: | ||||
|         id = None | ||||
| @ -62,7 +67,7 @@ async def socket_worker(websocket, path): | ||||
|  | ||||
|         if connection is None: | ||||
|             id = "unknown_user" | ||||
|         print("close connection to user: " + id) | ||||
|         debug("close connection to user: " + id) | ||||
|  | ||||
| if cert_file is not None and key_file is not None: | ||||
|     ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) | ||||
|  | ||||
							
								
								
									
										6
									
								
								match.py
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								match.py
									
									
									
									
									
								
							| @ -3,6 +3,8 @@ import numpy as np | ||||
| import json | ||||
| import base64 | ||||
|  | ||||
| from tools import debug | ||||
|  | ||||
| # decoder/encoder took from: https://stackoverflow.com/a/19271311 | ||||
|  | ||||
|  | ||||
| @ -158,7 +160,7 @@ class Match(object): | ||||
|         player_mark = FIELD_USER_A if self.is_player_a else FIELD_USER_B | ||||
|  | ||||
|         if not self.is_move_valid(sub_x, sub_y, x, y): | ||||
|             print("invalid move") | ||||
|             debug("invalid move") | ||||
|             return False | ||||
|  | ||||
|         # else: move! | ||||
| @ -169,7 +171,7 @@ class Match(object): | ||||
|  | ||||
|         # check whether this indicates changes in the global field: | ||||
|         if self.global_field[sub_y, sub_x] != FIELD_EMPTY: | ||||
|             print("field not empty") | ||||
|             debug("field not empty") | ||||
|             return False | ||||
|  | ||||
|         subgrid = self.complete_field[sub_y * self.n: ( | ||||
|  | ||||
| @ -5,14 +5,16 @@ import uuid | ||||
| import settings | ||||
| from match import Match | ||||
|  | ||||
| from tools import debug | ||||
|  | ||||
|  | ||||
| class MatchManager(object): | ||||
|     def __init__(self): | ||||
|         pass | ||||
|  | ||||
|     def get_match(self, id): | ||||
|         query = f"SELECT * FROM matches WHERE id='{id}'" | ||||
|         result = DatabaseConnection.global_single_query(query) | ||||
|         query = "SELECT * FROM matches WHERE id=%s" | ||||
|         result = DatabaseConnection.global_single_query(query, (id)) | ||||
|         if len(result) == 0: | ||||
|             return None | ||||
|         match = Match(n=settings.n, match_id=id, player_a_name=result[0]['user_a'], | ||||
| @ -20,45 +22,46 @@ class MatchManager(object): | ||||
|         return match | ||||
|  | ||||
|     def get_matches_for_user(self, user_name): | ||||
|         query = f"SELECT * FROM matches WHERE user_a='{user_name}' OR user_b='{user_name}'" | ||||
|         return DatabaseConnection.global_single_query(query) | ||||
|         query = "SELECT * FROM matches WHERE user_a=%s OR user_b=%s" | ||||
|         return DatabaseConnection.global_single_query(query, (user_name, user_name)) | ||||
|  | ||||
|     def create_new_match(self, user_a, user_b): | ||||
|         match_id = uuid.uuid4().hex | ||||
|         # check if already existent (but should not be the case) | ||||
|         if len(DatabaseConnection.global_single_query(f"SELECT id FROM matches WHERE id='{match_id}'")) > 0: | ||||
|         if len(DatabaseConnection.global_single_query("SELECT id FROM matches WHERE id=%s", (match_id))) > 0: | ||||
|             return self.create_new_match(user_a, user_b) | ||||
|  | ||||
|         match = Match(n=settings.n, match_id=match_id, player_a_name=user_a, player_b_name=user_b) | ||||
|         match = Match(n=settings.n, match_id=match_id, | ||||
|                       player_a_name=user_a, player_b_name=user_b) | ||||
|         now = datetime.datetime.now() | ||||
|         query = f"INSERT INTO matches (id, user_a, user_b, match_state, active_user, last_active) VALUES ('{match_id}', '{user_a}', '{user_b}', '{match.to_json_state()}', '{match.get_current_player()}','{get_sql_time(now)}')" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "INSERT INTO matches (id, user_a, user_b, match_state, active_user, last_active) VALUES (%s, %s, %s, %s, %s,%s)" | ||||
|         DatabaseConnection.global_single_execution( | ||||
|             query, (match_id, user_a, user_b, match.to_json_state(), match.get_current_player(), get_sql_time(now))) | ||||
|         return match | ||||
|      | ||||
|  | ||||
|     def update_match(self, match_id, match): | ||||
|         now = get_sql_time(datetime.datetime.now()) | ||||
|         query = f"UPDATE matches SET match_state='{match.to_json_state()}', active_user='{match.get_current_player()}', last_active='{now}' WHERE id='{match_id}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|      | ||||
|         query = "UPDATE matches SET match_state=%s, active_user=%s, last_active=%s WHERE id=%s" | ||||
|         DatabaseConnection.global_single_execution( | ||||
|             query, (match.to_json_state(), match.get_current_player(), now, match_id)) | ||||
|  | ||||
|     def apply_move(self, move_data): | ||||
|         match = self.get_match(move_data['id']) | ||||
|         if match is None: | ||||
|             return None | ||||
|          | ||||
|         if not match.move(move_data): | ||||
|             print("error applying match move") | ||||
|             return None | ||||
|          | ||||
|          | ||||
|         self.update_match(move_data['id'], match) | ||||
|         print("updated match") | ||||
|         return match | ||||
|      | ||||
|     def is_match(self, player_a, player_b): | ||||
|         query = f"SELECT * FROM matches WHERE (user_a='{player_a}' AND user_b='{player_b}') OR (user_a='{player_b}' AND user_b='{player_a}')" | ||||
|         return len(DatabaseConnection.global_single_query(query)) | ||||
|      | ||||
|     def delete_match(self, match_id): | ||||
|         query = f"DELETE FROM matches WHERE id='{match_id}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|  | ||||
|         if not match.move(move_data): | ||||
|             debug("error applying match move") | ||||
|             return None | ||||
|  | ||||
|         self.update_match(move_data['id'], match) | ||||
|         debug("updated match") | ||||
|         return match | ||||
|  | ||||
|     def is_match(self, player_a, player_b): | ||||
|         query = "SELECT * FROM matches WHERE (user_a=%s AND user_b=%s) OR (user_a=%s AND user_b=%s)" | ||||
|         return len(DatabaseConnection.global_single_query(query, (player_a, player_b, player_b, player_a))) | ||||
|  | ||||
|     def delete_match(self, match_id): | ||||
|         query = "DELETE FROM matches WHERE id=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (match_id)) | ||||
|  | ||||
| @ -9,59 +9,60 @@ class SessionManager(object): | ||||
|         self.session_lifespan_timedelta = session_lifespan_timedelta | ||||
|  | ||||
|     def get_session_by_id(self, session_id): | ||||
|         query = f"SELECT * FROM sessions WHERE id='{session_id}'" | ||||
|         return DatabaseConnection.global_single_query(query) | ||||
|         query = "SELECT * FROM sessions WHERE id=%s" | ||||
|         return DatabaseConnection.global_single_query(query, (session_id)) | ||||
|  | ||||
|     def touch_session(self, session_id): | ||||
|         query = f"UPDATE sessions SET last_seen='{datetime.datetime.now()}' WHERE id='{session_id}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "UPDATE sessions SET last_seen=%s WHERE id=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (datetime.datetime.now(), session_id)) | ||||
|  | ||||
|     def get_session_by_registered_user(self, user_name): | ||||
|         query = f"SELECT * FROM sessions WHERE registered_user='{user_name}'" | ||||
|         return DatabaseConnection.global_single_query(query) | ||||
|         query = "SELECT * FROM sessions WHERE registered_user=%s" | ||||
|         return DatabaseConnection.global_single_query(query, (user_name)) | ||||
|  | ||||
|     def get_session_by_temp_user(self, user_name): | ||||
|         query = f"SELECT * FROM sessions WHERE temp_user='{user_name}'" | ||||
|         return DatabaseConnection.global_single_query(query) | ||||
|         query = "SELECT * FROM sessions WHERE temp_user=%s" | ||||
|         return DatabaseConnection.global_single_query(query, (user_name)) | ||||
|  | ||||
|     def create_session_for_registered_user(self, user_name): | ||||
|         new_id = uuid.uuid4().hex | ||||
|         # check if already existent (but should not be the case) | ||||
|         if len(DatabaseConnection.global_single_query(f"SELECT id FROM sessions WHERE id='{new_id}'")) > 0: | ||||
|         if len(DatabaseConnection.global_single_query("SELECT id FROM sessions WHERE id=%s", (str(new_id)))) > 0: | ||||
|             # okay, next try: | ||||
|             return self.create_session_for_registered_user(user_name) | ||||
|          | ||||
|         # delete other active sessions: | ||||
|         query = f"DELETE FROM sessions WHERE registered_user='{user_name}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|  | ||||
|         query = f"INSERT INTO sessions (id, registered_user, temp_user, last_seen) VALUES ( '{new_id}', '{user_name}', NULL, '{datetime.datetime.now()}')" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         # delete other active sessions: | ||||
|         query = "DELETE FROM sessions WHERE registered_user=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (user_name)) | ||||
|  | ||||
|         query = "INSERT INTO sessions (id, registered_user, temp_user, last_seen) VALUES ( %s, %s, NULL, %s)" | ||||
|         DatabaseConnection.global_single_execution(query, (new_id, user_name, datetime.datetime.now())) | ||||
|  | ||||
|         return new_id | ||||
|  | ||||
|     def create_session_for_temp_user(self, user_name): | ||||
|         new_id = uuid.uuid4().hex | ||||
|         # check if already existent (but should not be the case) | ||||
|         if len(DatabaseConnection.global_single_query(f"SELECT id FROM sessions WHERE id='{new_id}'")) > 0: | ||||
|         if len(DatabaseConnection.global_single_query("SELECT id FROM sessions WHERE id=%s", (new_id))) > 0: | ||||
|             # okay, next try: | ||||
|             return self.create_session_for_registered_user(user_name) | ||||
|          | ||||
|  | ||||
|         # delete other active sessions: | ||||
|         query = f"DELETE FROM sessions WHERE temp_user='{user_name}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|          | ||||
|         query = f"INSERT INTO sessions (id, registered_user, temp_user, last_seen) VALUES ( '{new_id}', NULL, '{user_name}', '{datetime.datetime.now()}')" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "DELETE FROM sessions WHERE temp_user=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (user_name)) | ||||
|  | ||||
|         query = "INSERT INTO sessions (id, registered_user, temp_user, last_seen) VALUES ( %s, NULL, %s, %s)" | ||||
|         DatabaseConnection.global_single_execution(query, (new_id, user_name, datetime.datetime.now())) | ||||
|         return new_id | ||||
|  | ||||
|     def delete_session(self, session_id): | ||||
|         query = f"DELETE FROM sessions WHERE id='{session_id}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "DELETE FROM sessions WHERE id=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (session_id)) | ||||
|  | ||||
|     def revoke_inactive_sessions(self): | ||||
|         revoke_time = datetime.datetime.now() - self.session_lifespan_timedelta | ||||
|         query = f"SELECT * from sessions WHERE last_seen < '{get_sql_time(revoke_time)}'" | ||||
|         revoked_sessions = DatabaseConnection.global_single_query(query) | ||||
|         query = f"DELETE FROM sessions WHERE last_seen < '{get_sql_time(revoke_time)}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "SELECT * from sessions WHERE last_seen < %s" | ||||
|         revoked_sessions = DatabaseConnection.global_single_query(query, (get_sql_time(revoke_time))) | ||||
|         query = "DELETE FROM sessions WHERE last_seen < %s" | ||||
|         DatabaseConnection.global_single_execution(query, (get_sql_time(revoke_time))) | ||||
|         return revoked_sessions | ||||
|  | ||||
							
								
								
									
										5
									
								
								tools.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tools.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | ||||
| import datetime | ||||
|  | ||||
|  | ||||
| def debug(msg): | ||||
|     print("[" + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "]: " + msg) | ||||
| @ -10,23 +10,23 @@ class UserManager(object): | ||||
|         pass | ||||
|  | ||||
|     def get_user(self, user_name): | ||||
|         query = f"SELECT name, last_seen FROM users where name='{user_name}'" | ||||
|         return DatabaseConnection.global_single_query(query) | ||||
|         query = "SELECT name, last_seen FROM users where name=%s" | ||||
|         return DatabaseConnection.global_single_query(query, (user_name)) | ||||
|  | ||||
|     def delete_user(self, user_name): | ||||
|         query = f"DELETE FROM users where name='{user_name}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "DELETE FROM users where name=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (user_name)) | ||||
|  | ||||
|         # remove from friends: | ||||
|         query = f"DELETE FROM friends WHERE user='{user_name}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "DELETE FROM friends WHERE user=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (user_name)) | ||||
|  | ||||
|         query = f"DELETE FROM friends WHERE friend='{user_name}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "DELETE FROM friends WHERE friend=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (user_name)) | ||||
|  | ||||
|     def verify_user(self, user_name, pw): | ||||
|         query = f"SELECT * FROM users where name='{user_name}'" | ||||
|         users = DatabaseConnection.global_single_query(query) | ||||
|         query = "SELECT * FROM users where name=%s" | ||||
|         users = DatabaseConnection.global_single_query(query, (user_name)) | ||||
|         if len(self.get_user(user_name)) == 0: | ||||
|             return False | ||||
|  | ||||
| @ -41,33 +41,33 @@ class UserManager(object): | ||||
|         assert len(self.get_user(user_name)) == 0 | ||||
|         pw_salt = uuid.uuid4().hex | ||||
|         pw_hash = hashlib.sha512(pw.encode() + pw_salt.encode()).hexdigest() | ||||
|         query = f"INSERT INTO users (name, pw_hash, pw_salt, last_seen) VALUES ( '{user_name}', '{pw_hash}', '{pw_salt}', '{datetime.datetime.now()}')" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "INSERT INTO users (name, pw_hash, pw_salt, last_seen) VALUES ( %s, %s, %s, %s)" | ||||
|         DatabaseConnection.global_single_execution(query, (user_name, pw_hash, pw_salt, datetime.datetime.now())) | ||||
|      | ||||
|     def touch_user(self, user_name): | ||||
|         matches = self.get_user(user_name) | ||||
|         assert len(matches) == 1 | ||||
|         query = f"UPDATE users SET last_seen='{datetime.datetime.now()}' WHERE name='{user_name}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "UPDATE users SET last_seen=%s WHERE name=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (datetime.datetime.now(), user_name)) | ||||
|      | ||||
|     def add_friend_to_user(self, user_name, friend_name): | ||||
|         if len(self.get_user(friend_name)) > 0: | ||||
|             query = f"INSERT INTO friends (user, friend) VALUES ( '{user_name}', '{friend_name}')" | ||||
|             DatabaseConnection.global_single_execution(query) | ||||
|             query = "INSERT INTO friends (user, friend) VALUES ( %s, %s)" | ||||
|             DatabaseConnection.global_single_execution(query, (user_name, friend_name)) | ||||
|             return True | ||||
|         return False | ||||
|      | ||||
|     def get_friends_for_user(self, user_name): | ||||
|         query = f"SELECT friend FROM friends WHERE user='{user_name}'" | ||||
|         tmp = DatabaseConnection.global_single_query(query) | ||||
|         query = "SELECT friend FROM friends WHERE user=%s" | ||||
|         tmp = DatabaseConnection.global_single_query(query, (user_name)) | ||||
|         friends = set() | ||||
|         for entry in tmp: | ||||
|             friends.add(entry['friend']) | ||||
|         return friends | ||||
|      | ||||
|     def remove_friend_from_user(self, user_name, friend_name): | ||||
|         query = f"DELETE FROM friends WHERE user='{user_name}' AND friend='{friend_name}'" | ||||
|         DatabaseConnection.global_single_execution(query) | ||||
|         query = "DELETE FROM friends WHERE user=%s AND friend=%s" | ||||
|         DatabaseConnection.global_single_execution(query, (user_name, friend_name)) | ||||
|  | ||||
|     def get_all_users(self): | ||||
|         query = "SELECT name, last_seen FROM users" | ||||
|  | ||||
		Reference in New Issue
	
	Block a user