diff --git a/dsst/dsst_gtk3/client.py b/dsst/dsst_gtk3/client.py index b7da4b3..8c39cee 100644 --- a/dsst/dsst_gtk3/client.py +++ b/dsst/dsst_gtk3/client.py @@ -31,6 +31,7 @@ class Access: soc.close() return message.get('data') + if __name__ == '__main__': access = Access({'host': 'europa', 'port': 12345, 'buffer_size': 1024, 'auth_token': 'a'}) action = 'load_seasons' diff --git a/dsst/dsst_gtk3/resources/glade/window.glade b/dsst/dsst_gtk3/resources/glade/window.glade index 70535b4..71c2ed4 100644 --- a/dsst/dsst_gtk3/resources/glade/window.glade +++ b/dsst/dsst_gtk3/resources/glade/window.glade @@ -542,7 +542,7 @@ True False - [DeathCount] + Computing... 1 @@ -553,7 +553,7 @@ True False - Total Drinks: + Total Shots: 1 @@ -565,7 +565,7 @@ True False - [DrinkCount] + Computing... 1 @@ -588,7 +588,7 @@ True False - [ToatalBooze] + Computing... 1 @@ -647,7 +647,7 @@ True False - [PlayerDrinks] + Computing... 1 @@ -658,7 +658,7 @@ True False - [PlayerBooze] + Computing... 1 @@ -669,7 +669,7 @@ True False - [PlayerAlc] + Computing... 1 @@ -680,7 +680,7 @@ True False - [TotalAlc] + Computing... 1 @@ -703,6 +703,7 @@ True False + Computing... 1 diff --git a/dsst/dsst_server/auth.py b/dsst/dsst_server/auth.py new file mode 100644 index 0000000..53fe71d --- /dev/null +++ b/dsst/dsst_server/auth.py @@ -0,0 +1,33 @@ +READ_TOKENS = [] +WRITE_TOKENS = [] + + +class AuthenticationError(Exception): + def __init__(self, message): + self.message = message + + def get_response(self): + return { + 'success': False, + 'message': 'Authentication Failed:\n'.format(self.message) + } + + +def check_read(func): + def wrapper(*args, **kwargs): + token = args[0] + if token in READ_TOKENS + WRITE_TOKENS: + return func(*args[1:], **kwargs) + else: + raise AuthenticationError('Token "{}" has no read access on database.'.format(token)) + return wrapper + + +def check_write(func): + def wrapper(*args, **kwargs): + token = args[0] + if token in WRITE_TOKENS: + return func(*args[1:], **kwargs) + else: + raise AuthenticationError('Token "{}" has no write access on database.'.format(token)) + return wrapper diff --git a/dsst/dsst_server/func_read.py b/dsst/dsst_server/func_read.py index 40afa44..e84d77e 100644 --- a/dsst/dsst_server/func_read.py +++ b/dsst/dsst_server/func_read.py @@ -1,40 +1,49 @@ from dsst_server.data_access import sql, sql_func, mapping +from dsst_server.auth import check_read from common import models from playhouse import shortcuts class ReadFunctions: @staticmethod + @check_read def load_db_meta(*_): return sql.db.database @staticmethod + @check_read def load_seasons(*_): return [mapping.db_to_season(season) for season in sql.Season.select()] @staticmethod + @check_read def load_seasons_all(*_): return [shortcuts.model_to_dict(season, backrefs=True, max_depth=2) for season in sql.Season.select()] @staticmethod + @check_read def load_episodes(season_id, *_): if not season_id: raise Exception('Exception: Missing argument (season_id)') return [mapping.db_to_episode(ep) for ep in sql.Season.get(sql.Season.id == season_id).episodes] @staticmethod + @check_read def load_players(*_): return [mapping.db_to_player(player) for player in sql.Player.select()] @staticmethod + @check_read def load_enemies(season_id, *_): pass @staticmethod + @check_read def load_drinks(*_): return [mapping.db_to_drink(drink) for drink in sql.Drink.select()] @staticmethod + @check_read def load_season_stats(season_id, *_): season = sql.Season.get(sql.Season.id == season_id) players = sql_func.players_for_season(season_id) diff --git a/dsst/dsst_server/func_write.py b/dsst/dsst_server/func_write.py index 5aebaa4..888e182 100644 --- a/dsst/dsst_server/func_write.py +++ b/dsst/dsst_server/func_write.py @@ -1,13 +1,16 @@ from common import models from dsst_server.data_access import sql +from dsst_server.auth import check_write class WriteFunctions: @staticmethod + @check_write def create_season(season: 'models.Season'): return 'Season created.' @staticmethod + @check_write def update_enemy(enemy: 'models.Enemy', *_): (sql.Enemy .insert(id=enemy.id, boss=enemy.boss, name=enemy.name, season=enemy.season) @@ -17,6 +20,7 @@ class WriteFunctions: .execute()) @staticmethod + @check_write def update_player(player: 'models.Player', *_): (sql.Player .insert(id=player.id, name=player.name, hex_id=player.hex_id) @@ -25,6 +29,7 @@ class WriteFunctions: .execute()) @staticmethod + @check_write def update_drink(drink: 'models.Drink', *_): (sql.Drink .insert(id=drink.id, name=drink.name, vol=drink.vol) @@ -33,6 +38,7 @@ class WriteFunctions: .execute()) @staticmethod + @check_write def save_death(death: 'models.Death'): with sql.db.atomic(): created_id = (sql.Death @@ -43,6 +49,7 @@ class WriteFunctions: sql.Penalty.create(death=created_id, size=penalty.size, drink=penalty.drink, player=penalty.player) @staticmethod + @check_write def save_victory(victory: 'models.Victory'): (sql.Victory .insert(info=victory.info, player=victory.player, enemy=victory.enemy, time=victory.time, @@ -50,6 +57,7 @@ class WriteFunctions: .execute()) @staticmethod + @check_write def update_season(season: 'models.Season', *_): (sql.Season .insert(id=season.id, number=season.number, game_name=season.game_name, start_date=season.start_date, @@ -62,6 +70,7 @@ class WriteFunctions: .execute()) @staticmethod + @check_write def update_episode(episode: 'models.Episode', *_): players = list(sql.Player.select().where(sql.Player.id << [player.id for player in episode.players])) new_ep_id = (sql.Episode @@ -76,3 +85,8 @@ class WriteFunctions: db_episode = sql.Episode.get(sql.Episode.id == new_ep_id) db_episode.players = players db_episode.save() + + @staticmethod + @check_write + def delete_player(player_id: int, *_): + sql.Player.delete_by_id(int) diff --git a/dsst/dsst_server/server.py b/dsst/dsst_server/server.py index d13812d..ae02cf1 100644 --- a/dsst/dsst_server/server.py +++ b/dsst/dsst_server/server.py @@ -5,7 +5,7 @@ import sys import os from common import util, models -from dsst_server import func_read, func_write +from dsst_server import func_read, func_write, auth from dsst_server.func_proxy import FunctionProxy from dsst_server.data_access import sql, sql_func from dsst_server.config import DEFAULT_CONFIG @@ -16,8 +16,8 @@ class DsstServer: self.socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) print('Created socket') server_conf = config.get('server') - self.socket_server.bind((server_conf.get('host'), server_conf.get('port'))) - print('Bound socket to {} on host {}'.format(server_conf.get('port'), server_conf.get('host'))) + self.socket_server.bind(('', server_conf.get('port'))) + print('Bound socket to port {}'.format(server_conf.get('port'))) # Initialize database db_config = config.get('database') @@ -25,15 +25,10 @@ class DsstServer: sql_func.create_tables() print('Database initialized ({})'.format(sql.db.database)) - # Load access tokens and map them to their allowed methods - read_actions = util.list_class_methods(func_read.ReadFunctions) - write_actions = util.list_class_methods(func_write.WriteFunctions) - parm_access = { - 'r': read_actions, - 'rw': read_actions + write_actions - } - self.tokens = {token: parm_access[perms] for token, perms in config.get('tokens').items()} - print('Loaded auth tokens: {}'.format(self.tokens.keys())) + # Load access tokens + auth.READ_TOKENS = config.get('tokens').get('readonly') + auth.WRITE_TOKENS = config.get('tokens').get('readwrite') + print('Auth tokens loaded') def run(self): self.socket_server.listen(5) @@ -46,30 +41,23 @@ class DsstServer: data = util.recv_msg(client) request = pickle.loads(data) print('Request: {}'.format(request)) - # Validate auth token in request - token = request.get('auth_token') - if token not in self.tokens: - util.send_msg(client, pickle.dumps({'success': False, 'message': 'Auth token invalid'})) - print('Rejected request from {}. Auth token invalid ({})'.format(address, token)) - continue - # Check read functions + # Get requested function from function proxy action_name = request.get('action') - if action_name in self.tokens[token]: - action = getattr(FunctionProxy, action_name) - try: - value = action(*request.get('args')) - except Exception as e: - response = {'success': False, 'message': 'Exception was thrown on server.\n{}'.format(e)} - util.send_msg(client, pickle.dumps(response)) - raise - response = {'success': True, 'data': value} + action = getattr(FunctionProxy, action_name) + try: + value = action(request.get('auth_token'), *request.get('args')) + except auth.AuthenticationError as e: + response = e.get_response() util.send_msg(client, pickle.dumps(response)) - continue - else: - msg = 'Action does not exist on server ({})'.format(request.get('action')) - util.send_msg(client, pickle.dumps({'success': False, 'message': msg})) + raise + except Exception as e: + response = {'success': False, 'message': 'Exception was thrown on server.\n{}'.format(e)} + util.send_msg(client, pickle.dumps(response)) + raise + response = {'success': True, 'data': value} + util.send_msg(client, pickle.dumps(response)) except Exception as e: - print(e) + print('Exception: ' + str(e)) finally: client.close() print('Connection to client closed')