session, user and accounts revokation
This commit is contained in:
parent
392c38f09f
commit
61edbf8419
@ -38,6 +38,7 @@ communication with the web client is done by a (far from any standard and almost
|
|||||||
"type": "match_update",
|
"type": "match_update",
|
||||||
"data": {
|
"data": {
|
||||||
"id": "...",
|
"id": "...",
|
||||||
|
"revoke_time": <revoke_time>,
|
||||||
"match_state": <null| <match_state>>
|
"match_state": <null| <match_state>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ from match import Match
|
|||||||
|
|
||||||
from tools import debug, elo_p_win, elo_update
|
from tools import debug, elo_p_win, elo_update
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
@ -60,10 +62,13 @@ class ConnectionHandler(object):
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
session_manager: SessionManager,
|
session_manager: SessionManager,
|
||||||
user_manager: UserManager,
|
user_manager: UserManager,
|
||||||
match_manager: MatchManager):
|
match_manager: MatchManager,
|
||||||
|
revoke_check_timedelta):
|
||||||
self.session_manager = session_manager
|
self.session_manager = session_manager
|
||||||
self.user_manager = user_manager
|
self.user_manager = user_manager
|
||||||
self.match_manager = match_manager
|
self.match_manager = match_manager
|
||||||
|
self.revoke_check_timedelta = revoke_check_timedelta
|
||||||
|
self.lastCheck = datetime.datetime.now()
|
||||||
|
|
||||||
self.open_connections_by_user = {}
|
self.open_connections_by_user = {}
|
||||||
self.open_connections_by_id = {}
|
self.open_connections_by_id = {}
|
||||||
@ -259,6 +264,7 @@ class ConnectionHandler(object):
|
|||||||
"type": "match_update",
|
"type": "match_update",
|
||||||
"data": {
|
"data": {
|
||||||
"id": m.id,
|
"id": m.id,
|
||||||
|
"revoke_time": m.revoke_time,
|
||||||
"match_state": state
|
"match_state": state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -271,6 +277,7 @@ class ConnectionHandler(object):
|
|||||||
"type": "match_update",
|
"type": "match_update",
|
||||||
"data": {
|
"data": {
|
||||||
"id": m.id,
|
"id": m.id,
|
||||||
|
"revoke_time": m.revoke_time,
|
||||||
"match_state": state
|
"match_state": state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -427,6 +434,7 @@ class ConnectionHandler(object):
|
|||||||
"type": "match_update",
|
"type": "match_update",
|
||||||
"data": {
|
"data": {
|
||||||
"id": db_match['id'],
|
"id": db_match['id'],
|
||||||
|
"revoke_time": match.get_sql_revoke_time(),
|
||||||
"match_state": json.loads(match.to_json_state())
|
"match_state": json.loads(match.to_json_state())
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -460,6 +468,7 @@ class ConnectionHandler(object):
|
|||||||
'type': 'match_update',
|
'type': 'match_update',
|
||||||
'data': {
|
'data': {
|
||||||
'id': match.id,
|
'id': match.id,
|
||||||
|
'revoke_time': match.get_sql_revoke_time(),
|
||||||
'match_state': json.loads(match_state)
|
'match_state': json.loads(match_state)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -477,6 +486,7 @@ class ConnectionHandler(object):
|
|||||||
'type': 'match_update',
|
'type': 'match_update',
|
||||||
'data': {
|
'data': {
|
||||||
'id': match.id,
|
'id': match.id,
|
||||||
|
'revoke_time': match.get_sql_revoke_time(),
|
||||||
'match_state': json.loads(match_state)
|
'match_state': json.loads(match_state)
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
@ -536,6 +546,7 @@ class ConnectionHandler(object):
|
|||||||
'type': 'match_update',
|
'type': 'match_update',
|
||||||
'data': {
|
'data': {
|
||||||
'id': match_id,
|
'id': match_id,
|
||||||
|
'revoke_time': match.get_sql_revoke_time(),
|
||||||
'match_state': json.loads(match_state)
|
'match_state': json.loads(match_state)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -552,12 +563,16 @@ class ConnectionHandler(object):
|
|||||||
if match is not None:
|
if match is not None:
|
||||||
match_state = match.to_json_state()
|
match_state = match.to_json_state()
|
||||||
|
|
||||||
await conn.send(json.dumps({
|
if match_state is not None:
|
||||||
'type': 'match_update',
|
|
||||||
'data': {
|
await conn.send(json.dumps({
|
||||||
'match_state': json.loads(match_state)
|
'type': 'match_update',
|
||||||
}
|
'data': {
|
||||||
}))
|
'id': match.id,
|
||||||
|
'revoke_time': match.get_sql_revoke_time(),
|
||||||
|
'match_state': json.loads(match_state)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
async def _on_friend_request(self, conn, data):
|
async def _on_friend_request(self, conn, data):
|
||||||
msg = "error in handling friend request"
|
msg = "error in handling friend request"
|
||||||
@ -654,6 +669,14 @@ class ConnectionHandler(object):
|
|||||||
async def disconnect(self, conn):
|
async def disconnect(self, conn):
|
||||||
self._del_connection(conn)
|
self._del_connection(conn)
|
||||||
|
|
||||||
|
async def revoke_check(self):
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
if self.lastCheck + self.revoke_check_timedelta < now:
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
self.match_manager.check_matches_lifespan()
|
||||||
|
self.session_manager.revoke_inactive_sessions()
|
||||||
|
self.user_manager.revoke_inactive_accounts()
|
||||||
|
|
||||||
async def handle_message(self, conn, msg_str):
|
async def handle_message(self, conn, msg_str):
|
||||||
msg = parse_message(msg_str)
|
msg = parse_message(msg_str)
|
||||||
|
|
||||||
@ -665,6 +688,7 @@ class ConnectionHandler(object):
|
|||||||
t = msg['type']
|
t = msg['type']
|
||||||
|
|
||||||
self.user_manager.touch_user(conn.user_name)
|
self.user_manager.touch_user(conn.user_name)
|
||||||
|
self.session_manager.touch_session(conn.id)
|
||||||
if t == "match_request":
|
if t == "match_request":
|
||||||
await self._on_match_req(conn, msg['data'])
|
await self._on_match_req(conn, msg['data'])
|
||||||
|
|
||||||
@ -685,3 +709,5 @@ class ConnectionHandler(object):
|
|||||||
|
|
||||||
else:
|
else:
|
||||||
debug("could not interpret message: " + msg_str)
|
debug("could not interpret message: " + msg_str)
|
||||||
|
|
||||||
|
await self.revoke_check()
|
||||||
|
@ -35,8 +35,14 @@ class DatabaseConnection(object):
|
|||||||
assert DatabaseConnection.instance is not None
|
assert DatabaseConnection.instance is not None
|
||||||
DatabaseConnection.instance.commit()
|
DatabaseConnection.instance.commit()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def global_ping():
|
||||||
|
assert DatabaseConnection.instance is not None
|
||||||
|
DatabaseConnection.instance.connection.ping()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def global_single_query(query, params=None):
|
def global_single_query(query, params=None):
|
||||||
|
DatabaseConnection.global_ping()
|
||||||
if ';' in query:
|
if ';' in query:
|
||||||
# Possible injection!
|
# Possible injection!
|
||||||
raise SQLInjectionError()
|
raise SQLInjectionError()
|
||||||
@ -51,6 +57,7 @@ class DatabaseConnection(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def global_single_execution(sql_statement, params=None):
|
def global_single_execution(sql_statement, params=None):
|
||||||
|
DatabaseConnection.global_ping()
|
||||||
if ';' in sql_statement:
|
if ';' in sql_statement:
|
||||||
# Possible injection detected!
|
# Possible injection detected!
|
||||||
raise SQLInjectionError()
|
raise SQLInjectionError()
|
||||||
|
8
main.py
8
main.py
@ -14,10 +14,10 @@ import datetime
|
|||||||
|
|
||||||
from tools import debug
|
from tools import debug
|
||||||
|
|
||||||
um = UserManager()
|
um = UserManager(account_revoke_time)
|
||||||
sm = SessionManager(datetime.timedelta(hours=12))
|
sm = SessionManager(session_revove_time)
|
||||||
mm = MatchManager(user_manager=um)
|
mm = MatchManager(um, match_revoke_time)
|
||||||
ch = ConnectionHandler(sm, um, mm)
|
ch = ConnectionHandler(sm, um, mm, revoke_check_interval)
|
||||||
|
|
||||||
|
|
||||||
DatabaseConnection(db_host,
|
DatabaseConnection(db_host,
|
||||||
|
12
match.py
12
match.py
@ -5,6 +5,8 @@ import base64
|
|||||||
|
|
||||||
from tools import debug
|
from tools import debug
|
||||||
|
|
||||||
|
from database_connection import get_sql_time
|
||||||
|
|
||||||
# decoder/encoder took from: https://stackoverflow.com/a/19271311
|
# decoder/encoder took from: https://stackoverflow.com/a/19271311
|
||||||
|
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ class Match(object):
|
|||||||
FIELD_USER_B = 2
|
FIELD_USER_B = 2
|
||||||
FIELD_DRAW = 3
|
FIELD_DRAW = 3
|
||||||
|
|
||||||
def __init__(self, n, match_id, player_a_name, player_b_name, json_state=None):
|
def __init__(self, n, match_id, revoke_time, player_a_name, player_b_name, json_state=None):
|
||||||
self.n = n
|
self.n = n
|
||||||
self.id = match_id
|
self.id = match_id
|
||||||
self.complete_field = np.zeros(shape=(n*n, n*n), dtype=int)
|
self.complete_field = np.zeros(shape=(n*n, n*n), dtype=int)
|
||||||
@ -48,10 +50,14 @@ class Match(object):
|
|||||||
self.is_player_a = True
|
self.is_player_a = True
|
||||||
self.player_a_name = player_a_name
|
self.player_a_name = player_a_name
|
||||||
self.player_b_name = player_b_name
|
self.player_b_name = player_b_name
|
||||||
|
self.revoke_time = revoke_time
|
||||||
|
|
||||||
if json_state is not None:
|
if json_state is not None:
|
||||||
self.from_json_state(json_state)
|
self.from_json_state(json_state)
|
||||||
|
|
||||||
|
def get_sql_revoke_time(self):
|
||||||
|
return get_sql_time(self.revoke_time)
|
||||||
|
|
||||||
def from_json_state(self, json_state):
|
def from_json_state(self, json_state):
|
||||||
match_obj = json.loads(json_state)
|
match_obj = json.loads(json_state)
|
||||||
self.complete_field = np.array(match_obj['complete_field'], dtype=int)
|
self.complete_field = np.array(match_obj['complete_field'], dtype=int)
|
||||||
@ -155,6 +161,10 @@ class Match(object):
|
|||||||
return is_col or is_row or is_main_diag or is_sec_diag
|
return is_col or is_row or is_main_diag or is_sec_diag
|
||||||
|
|
||||||
def move(self, move_dict):
|
def move(self, move_dict):
|
||||||
|
if self.game_over:
|
||||||
|
debug("invalid move")
|
||||||
|
return False
|
||||||
|
|
||||||
sub_x = int(move_dict['sub_x'])
|
sub_x = int(move_dict['sub_x'])
|
||||||
sub_y = int(move_dict['sub_y'])
|
sub_y = int(move_dict['sub_y'])
|
||||||
x = int(move_dict['x'])
|
x = int(move_dict['x'])
|
||||||
|
@ -10,16 +10,18 @@ from tools import debug, elo_p_win, elo_update
|
|||||||
|
|
||||||
|
|
||||||
class MatchManager(object):
|
class MatchManager(object):
|
||||||
def __init__(self, user_manager: UserManager = None):
|
def __init__(self, user_manager: UserManager, match_lifespan_timedelta: datetime.timedelta):
|
||||||
self.user_manager = user_manager
|
self.user_manager = user_manager
|
||||||
pass
|
self.match_lifespan_timedelta = match_lifespan_timedelta
|
||||||
|
|
||||||
def get_match(self, id: str) -> Match:
|
def get_match(self, id: str) -> Match:
|
||||||
query = "SELECT * FROM matches WHERE id=%s"
|
query = "SELECT * FROM matches WHERE id=%s"
|
||||||
result = DatabaseConnection.global_single_query(query, (id))
|
result = DatabaseConnection.global_single_query(query, (id))
|
||||||
if len(result) == 0:
|
if len(result) == 0:
|
||||||
return None
|
return None
|
||||||
match = Match(n=settings.n, match_id=id, player_a_name=result[0]['user_a'],
|
|
||||||
|
revoke_time = result[0]['last_active'] + self.match_lifespan_timedelta
|
||||||
|
match = Match(n=settings.n, match_id=id, revoke_time=revoke_time, player_a_name=result[0]['user_a'],
|
||||||
player_b_name=result[0]['user_b'], json_state=result[0]['match_state'])
|
player_b_name=result[0]['user_b'], json_state=result[0]['match_state'])
|
||||||
return match
|
return match
|
||||||
|
|
||||||
@ -33,19 +35,22 @@ class MatchManager(object):
|
|||||||
if len(DatabaseConnection.global_single_query("SELECT id FROM matches WHERE id=%s", (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)
|
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)
|
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
|
match = Match(n=settings.n, match_id=match_id, revoke_time=now + self.match_lifespan_timedelta,
|
||||||
|
player_a_name=user_a, player_b_name=user_b)
|
||||||
|
|
||||||
query = "INSERT INTO matches (id, user_a, user_b, match_state, active_user, last_active) VALUES (%s, %s, %s, %s, %s,%s)"
|
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(
|
DatabaseConnection.global_single_execution(
|
||||||
query, (match_id, user_a, user_b, match.to_json_state(), match.get_current_player(), get_sql_time(now)))
|
query, (match_id, user_a, user_b, match.to_json_state(), match.get_current_player(), get_sql_time(now)))
|
||||||
return match
|
return match
|
||||||
|
|
||||||
def update_match(self, match_id: str, match: Match) -> None:
|
def update_match(self, match_id: str, match: Match, update_in_db=True) -> None:
|
||||||
now = get_sql_time(datetime.datetime.now())
|
|
||||||
query = "UPDATE matches SET match_state=%s, active_user=%s, last_active=%s WHERE id=%s"
|
if (update_in_db):
|
||||||
DatabaseConnection.global_single_execution(
|
now = get_sql_time(datetime.datetime.now())
|
||||||
query, (match.to_json_state(), match.get_current_player(), now, match_id))
|
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))
|
||||||
|
|
||||||
# check whether we have to update the elo values (game over triggered by the last move)
|
# check whether we have to update the elo values (game over triggered by the last move)
|
||||||
if match.game_over:
|
if match.game_over:
|
||||||
@ -111,3 +116,32 @@ class MatchManager(object):
|
|||||||
def delete_match(self, match_id: str) -> None:
|
def delete_match(self, match_id: str) -> None:
|
||||||
query = "DELETE FROM matches WHERE id=%s"
|
query = "DELETE FROM matches WHERE id=%s"
|
||||||
DatabaseConnection.global_single_execution(query, (match_id))
|
DatabaseConnection.global_single_execution(query, (match_id))
|
||||||
|
|
||||||
|
def check_matches_lifespan(self):
|
||||||
|
time_now = datetime.datetime.now()
|
||||||
|
time_revoke = time_now - self.match_lifespan_timedelta
|
||||||
|
|
||||||
|
query = "SELECT * FROM matches WHERE last_active < %s"
|
||||||
|
tmp = DatabaseConnection.global_single_query(
|
||||||
|
query, (get_sql_time(time_revoke)))
|
||||||
|
|
||||||
|
for entry in tmp:
|
||||||
|
revoke_time = entry['last_active'] + self.match_lifespan_timedelta
|
||||||
|
match = Match(n=settings.n, match_id=entry['id'], revoke_time=revoke_time, player_a_name=entry['user_a'],
|
||||||
|
player_b_name=entry['user_b'], json_state=entry['match_state'])
|
||||||
|
|
||||||
|
if (not match.game_over):
|
||||||
|
match.game_over = True
|
||||||
|
|
||||||
|
# check who is the current player who did not make the move in time:
|
||||||
|
match.player_won = match.player_b_name if match.is_player_a else match.player_a_name
|
||||||
|
|
||||||
|
self.update_match(entry['id'], match, False)
|
||||||
|
|
||||||
|
# delete matches from db:
|
||||||
|
|
||||||
|
debug("deleting revoked sessions: " + str(tmp))
|
||||||
|
|
||||||
|
query = "DELETE FROM matches WHERE last_active < %s"
|
||||||
|
tmp = DatabaseConnection.global_single_execution(
|
||||||
|
query, (get_sql_time(time_revoke)))
|
||||||
|
@ -3,6 +3,8 @@ from user import User
|
|||||||
import datetime
|
import datetime
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from tools import debug
|
||||||
|
|
||||||
|
|
||||||
class SessionManager(object):
|
class SessionManager(object):
|
||||||
def __init__(self, session_lifespan_timedelta):
|
def __init__(self, session_lifespan_timedelta):
|
||||||
@ -65,4 +67,6 @@ class SessionManager(object):
|
|||||||
revoked_sessions = DatabaseConnection.global_single_query(query, (get_sql_time(revoke_time)))
|
revoked_sessions = DatabaseConnection.global_single_query(query, (get_sql_time(revoke_time)))
|
||||||
query = "DELETE FROM sessions WHERE last_seen < %s"
|
query = "DELETE FROM sessions WHERE last_seen < %s"
|
||||||
DatabaseConnection.global_single_execution(query, (get_sql_time(revoke_time)))
|
DatabaseConnection.global_single_execution(query, (get_sql_time(revoke_time)))
|
||||||
return revoked_sessions
|
debug("delete revoked sessions: " + str(revoked_sessions))
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import datetime
|
||||||
|
|
||||||
cert_file = None
|
cert_file = None
|
||||||
key_file = None
|
key_file = None
|
||||||
|
|
||||||
@ -15,5 +17,12 @@ db_charset = 'utf8mb4'
|
|||||||
elo_start_value = 1000
|
elo_start_value = 1000
|
||||||
elo_default_k = 20
|
elo_default_k = 20
|
||||||
|
|
||||||
|
# revoke times:
|
||||||
|
account_revoke_time = datetime.timedelta(days=45)
|
||||||
|
session_revove_time = datetime.timedelta(days=20)
|
||||||
|
match_revoke_time = datetime.timedelta(days=7)
|
||||||
|
|
||||||
|
revoke_check_interval = datetime.timedelta(hours=1)
|
||||||
|
|
||||||
# field dimension (warning: this constant is not constantly used)
|
# field dimension (warning: this constant is not constantly used)
|
||||||
n = 3
|
n = 3
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
from database_connection import DatabaseConnection, SQLInjectionError
|
from database_connection import DatabaseConnection, SQLInjectionError, get_sql_time
|
||||||
from user import User
|
from user import User
|
||||||
import datetime
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
import uuid
|
import uuid
|
||||||
from settings import elo_start_value
|
from settings import elo_start_value
|
||||||
|
|
||||||
|
from tools import debug
|
||||||
|
|
||||||
|
|
||||||
class UserManager(object):
|
class UserManager(object):
|
||||||
def __init__(self):
|
def __init__(self, account_lifespan_timedelta):
|
||||||
|
self.account_lifespan_timedelta = account_lifespan_timedelta
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_user(self, user_name):
|
def get_user(self, user_name):
|
||||||
@ -134,3 +137,14 @@ class UserManager(object):
|
|||||||
def get_all_users(self):
|
def get_all_users(self):
|
||||||
query = "SELECT name, last_seen FROM users"
|
query = "SELECT name, last_seen FROM users"
|
||||||
return DatabaseConnection.global_single_query(query)
|
return DatabaseConnection.global_single_query(query)
|
||||||
|
|
||||||
|
def revoke_inactive_accounts(self):
|
||||||
|
revoke_time = datetime.datetime.now() - self.account_lifespan_timedelta
|
||||||
|
query = "SELECT * from users WHERE last_seen < %s"
|
||||||
|
revoked_sessions = DatabaseConnection.global_single_query(query, (get_sql_time(revoke_time)))
|
||||||
|
|
||||||
|
for entry in revoked_sessions:
|
||||||
|
name = entry['name']
|
||||||
|
self.delete_user(name)
|
||||||
|
|
||||||
|
debug("deleted users: " + str(revoked_sessions))
|
||||||
|
Loading…
Reference in New Issue
Block a user