From 39fe8c3f57f45f15e6d3183527f8e3f3e02eaa24 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 23 Aug 2019 16:04:50 -0600 Subject: [PATCH] Adjust permissions, include super admins. --- get5/match.html | 256 ++++++++++++++++++++++++++++++++++++++++ get5/match.py | 139 +++++++++++----------- get5/models.py | 6 +- get5/season.py | 2 +- get5/server.py | 29 +++-- get5/server_create.html | 82 +++++++++++++ get5/server_test.py | 6 +- get5/servers.html | 81 +++++++++++++ get5/team.py | 29 ++--- get5/util.py | 20 ++++ 10 files changed, 547 insertions(+), 103 deletions(-) create mode 100755 get5/match.html mode change 100644 => 100755 get5/match.py mode change 100644 => 100755 get5/models.py mode change 100644 => 100755 get5/season.py mode change 100644 => 100755 get5/server.py create mode 100755 get5/server_create.html mode change 100644 => 100755 get5/server_test.py create mode 100755 get5/servers.html mode change 100644 => 100755 get5/team.py mode change 100644 => 100755 get5/util.py diff --git a/get5/match.html b/get5/match.html new file mode 100755 index 0000000..056452d --- /dev/null +++ b/get5/match.html @@ -0,0 +1,256 @@ +{% from "macros.html" import score_symbol %} + +{% extends "layout.html" %} + +{% macro ind_team_table(team, map_stats) %} +

{{team.name}}

+ + + + + + + + + + + + + + + + + + + {% for player in map_stats.player_stats.filter_by(team_id=team.id)|sort(attribute='kills', reverse=True) %} + {% if player.roundsplayed > 0 %} + + + + + + + + + + + + + + + + + {% endif %} + {% endfor %} + + +
PlayerKillsDeathsAssistsFlash assists1v11v21v3RatingFPRADRHSP
{{ player.get_player_name() }} {{ player.kills }} {{ player.deaths }} {{ player.assists }} {{ player.flashbang_assists }} {{ player.v1 }} {{ player.v2 }} {{ player.v3 }} {{ player.get_rating() | round(2) }} {{ player.get_fpr() | round(2) }} {{ player.get_adr() | round(1) }} {{ player.get_hsp() | round(2) }}
+{% endmacro %} + + +{% block content %} + +{% with messages = get_flashed_messages(with_categories=true) %} +{% if messages %} +
+
Command response
+ +
+ {% for message in messages %} + {{ message[1] }} +
+ {% endfor %} +
+ +
+
+{% endif %} +{% endwith %} + +
+ +
+ +

+ {{ team1.get_logo_or_flag_html(1.0, team2) }} {{team1.name}} + {{ match.team1_score }} + {{ score_symbol(match.team1_score, match.team2_score) }} + {{ match.team2_score }} + {{ team2.get_logo_or_flag_html(1.0, team1) }} {{team2.name}} + + {% if (admin_access or match_owner or super_admin_access) and (match.live() or match.pending()) %} + + {% endif %} + +

+ + +
+ {% if match.cancelled %} + + {% endif %} + + {% if match.forfeit %} + + {% endif %} + + {% if connect_string is not none and not match.finalized() and not match.live() %} + + Connect + + {% endif %} + + {% if gotv_string is not none and not match.finalized() and not match.live() %} + Connect to GoTV + {% endif %} + + {% if match.start_time is none %} + + {% endif %} + + + {% if vetoes.count() > 0 %} +
+
Vetoes
+ + + + + + + + + + + + {% for veto in vetoes %} + + + + + + {% endfor %} + + + +
Veto/PickMapTeam Name
{{ veto.pick_or_veto }} {{ veto.map }} {{ veto.team_name }}
+
+ {% endif %} + {% for map_stats in map_stat_list %} +
+
+
+ Map {{map_stats.map_number + 1}}: {{ map_stats.map_name }}, + {{team1.name}} {{ score_symbol(map_stats.team1_score, map_stats.team2_score) }} {{team2.name}}, + {{map_stats.team1_score}}:{{map_stats.team2_score}} +
+ +
+ {% if map_stats.demoFile is not none and map_stats.demoFile != '' %} + Download Demo + {% endif %} +

Started at {{ map_stats.start_time.strftime('%Y-%m-%d %H:%M') }}

+ + {% if map_stats.end_time is not none %} +

Ended at {{ map_stats.end_time.strftime('%Y-%m-%d %H:%M') }}

+ {% endif %} + + {{ind_team_table(team1, map_stats)}} + {{ind_team_table(team2, map_stats)}} + +
+ +
+ {% endfor %} + +
+ + +
+
+ + + + +{% endblock %} \ No newline at end of file diff --git a/get5/match.py b/get5/match.py old mode 100644 new mode 100755 index 23aeeb1..9edd5ff --- a/get5/match.py +++ b/get5/match.py @@ -197,7 +197,7 @@ def match_create(): max_matches = config_setting('USER_MAX_MATCHES') season_id = None - if max_matches >= 0 and num_matches >= max_matches and not g.user.admin: + if max_matches >= 0 and num_matches >= max_matches and not (util.is_admin(g.user) or util.is_super_admin(g.user)): flash('You already have the maximum number of matches ({}) created'.format( num_matches)) @@ -293,7 +293,6 @@ def match_create(): @match_blueprint.route('/match//forfeit/') def match_forfeit(matchid, teamwinner): - app.logger.info("Match server id is: {}".format(matchid)) match = Match.query.get_or_404(matchid) super_admintools_check(g.user, match) if teamwinner == 1: @@ -330,25 +329,6 @@ def match_forfeit(matchid, teamwinner): return redirect('/mymatches') -def check_private_or_public(user, match, team1, team2): - if match.is_private_match(): - if not user: - raise BadRequestError("Please login before viewing this match.") - # Get team lists, and check if logged in user is part of match. - if not (user.id == match.user_id) or (config_setting( - 'ADMINS_ACCESS_ALL_MATCHES') and user.admin) or user.super_admin: - isPlayer = False - playerstats_steam = PlayerStats.query(PlayerStats.steam_id).filter_by( - match_id=match.id) - playerList = list(set(team1.auths + team2.auths + playerstats_steam)) - for player in playerList: - if user.steam_id == player: - isPlayer = True - break - if not isPlayer: - raise BadRequestError( - "You cannot view this match as you were not a part of it!") - @match_blueprint.route('/match/') def match(matchid): match = Match.query.get_or_404(matchid) @@ -382,22 +362,22 @@ def match(matchid): app.logger.info('Attempted to connect to server {}, but it is offline' .format(server.ip_string)) - is_owner = False - is_server_owner = False + is_match_owner = False + is_server_op = False has_admin_access = False has_super_admin_access = False if g.user: - is_owner = (g.user.id == match.user_id) - has_admin_access = is_owner or (config_setting( - 'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin) - has_super_admin_access = g.user.super_admin - is_server_owner = server_owner_check(g.user, server) + is_match_owner = (g.user.id == match.user_id) + has_admin_access = (config_setting( + 'ADMINS_ACCESS_ALL_MATCHES') and util.is_admin(g.user)) + has_super_admin_access = util.is_super_admin(g.user) + is_server_op = util.is_server_owner(g.user, server) return render_template( 'match.html', user=g.user, admin_access=has_admin_access, match=match, team1=team1, team2=team2, map_stat_list=map_stat_list, completed=completed, connect_string=connect_string, gotv_string=gotv_string, super_admin_access=has_super_admin_access, vetoes=vetoes, - server_owner=is_server_owner) + server_owner=is_server_op, match_owner=is_match_owner) @match_blueprint.route('/match//scoreboard') @@ -459,47 +439,6 @@ def match_config(matchid): mimetype='application/json') return response -def server_owner_check(user, server): - if user is None or server is None: - return False - elif user.super_admin or user.id == server.user_id or ( - user.admin and get5.config_setting( - 'ADMINS_ACCESS_ALL_MATCHES')): - return True - else: - return False - -def super_admintools_check(user, match): - if user is None: - raise BadRequestError('You do not have access to this page') - - grant_admin_access = user.super_admin - - if not grant_admin_access: - raise BadRequestError('You do not have access to this page') - - if match.finished(): - raise BadRequestError('Match already finished') - - if match.cancelled: - raise BadRequestError('Match is cancelled') - - -def admintools_check(user, match): - if user is None: - raise BadRequestError('You do not have access to this page') - - grant_admin_access = user.admin and get5.config_setting( - 'ADMINS_ACCESS_ALL_MATCHES') - if user.id != match.user_id and not grant_admin_access: - raise BadRequestError('You do not have access to this page') - - if match.finished(): - raise BadRequestError('Match already finished') - - if match.cancelled: - raise BadRequestError('Match is cancelled') - @match_blueprint.route('/match//cancel') def match_cancel(matchid): @@ -525,13 +464,14 @@ def match_cancel(matchid): @match_blueprint.route('/match//rcon') def match_rcon(matchid): match = Match.query.get_or_404(matchid) - admintools_check(g.user, match) command = request.values.get('command') server = GameServer.query.get_or_404(match.server_id) + owns_server = util.is_server_owner(g.user, server) + is_sadmin = util.is_super_admin(g.user) # Check to see if user owns server. - if public_server and not server_owner_check(g.user, server): - raise BadRequestError('You do not have access to this page') + if not owns_server or not is_sadmin: + raise BadRequestError('You are not the server owner.') if command: try: @@ -713,3 +653,56 @@ def generate(): # add a filename response.headers.set("Content-Disposition", "attachment", filename=logName) return response + + +# Begin Helper Functions + + +def super_admintools_check(user, match): + if user is None: + raise BadRequestError('You do not have access to this page') + + if not util.is_super_admin(user): + raise BadRequestError('You do not have access to this page') + + if match.finished(): + raise BadRequestError('Match already finished') + + if match.cancelled: + raise BadRequestError('Match is cancelled') + + +def admintools_check(user, match): + if user is None: + raise BadRequestError('You do not have access to this page') + + grant_admin_access = util.is_admin(user) and get5.config_setting( + 'ADMINS_ACCESS_ALL_MATCHES') + if user.id != match.user_id and not grant_admin_access: + raise BadRequestError('You do not have access to this page') + + if match.finished(): + raise BadRequestError('Match already finished') + + if match.cancelled: + raise BadRequestError('Match is cancelled') + +def check_private_or_public(user, match, team1, team2): + if match.is_private_match(): + if not user: + raise BadRequestError("Please login before viewing this match.") + # Get team lists, and check if logged in user is part of match. + if not (user.id == match.user_id) or (config_setting( + 'ADMINS_ACCESS_ALL_MATCHES') and util.lis_admin(user)) or util.is_super_admin(user): + isPlayer = False + playerstats_steam = PlayerStats.query(PlayerStats.steam_id).filter_by( + match_id=match.id) + playerList = list(set(team1.auths + team2.auths + playerstats_steam)) + for player in playerList: + if user.steam_id == player: + isPlayer = True + break + if not isPlayer: + raise BadRequestError( + "You cannot view this match as you were not a part of it!") +# End Helper Functions \ No newline at end of file diff --git a/get5/models.py b/get5/models.py old mode 100644 new mode 100755 index 81d84b1..7b0ad75 --- a/get5/models.py +++ b/get5/models.py @@ -135,7 +135,7 @@ def create(user, name, tag, flag, logo, auths, public_team=False, preferred_name rv = Team() rv.user_id = user.id rv.set_data(name, tag, flag, logo, auths, - (public_team and user.admin), preferred_names) + (public_team and util.is_admin(user)), preferred_names) db.session.add(rv) return rv @@ -153,6 +153,8 @@ def can_edit(self, user): return False if self.user_id == user.id: return True + if util.is_super_admin(user): + return True return False def get_players(self): @@ -292,6 +294,8 @@ def can_edit(self, user): return False if self.user_id == user.id: return True + if util.is_super_admin(user): + return True return False def can_delete(self, user): diff --git a/get5/season.py b/get5/season.py old mode 100644 new mode 100755 index d67d755..14747d0 --- a/get5/season.py +++ b/get5/season.py @@ -57,7 +57,7 @@ def season_create(): if request.method == 'POST': num_seasons = g.user.seasons.count() max_seasons = config_setting('USER_MAX_SEASONS') - if max_seasons >= 0 and num_seasons >= max_seasons and not g.user.admin: + if max_seasons >= 0 and num_seasons >= max_seasons and not (util.is_admin(g.user) or util.is_super_admin(g.user)): flash('You already have the maximum number of seasons ({}) created'.format( num_seasons)) diff --git a/get5/server.py b/get5/server.py old mode 100644 new mode 100755 index 4bc7b38..4a9df2f --- a/get5/server.py +++ b/get5/server.py @@ -1,4 +1,4 @@ -from get5 import app, db, flash_errors, config_setting +from get5 import app, db, flash_errors, config_setting, BadRequestError from models import GameServer import util @@ -43,7 +43,7 @@ def server_create(): if request.method == 'POST': num_servers = g.user.servers.count() max_servers = config_setting('USER_MAX_SERVERS') - if max_servers >= 0 and num_servers >= max_servers and not g.user.admin: + if max_servers >= 0 and num_servers >= max_servers and not (util.is_admin(g.user) or util.is_super_admin(g.user)): flash('You already have the maximum number of servers ({}) stored'.format( num_servers)) @@ -59,7 +59,7 @@ def server_create(): data['display_name'], data['ip_string'], data['port'], encRcon, - data['public_server'] and g.user.admin) + data['public_server'] and util.is_admin(g.user)) if mock or util.check_server_connection(server, dbKey): db.session.commit() @@ -74,15 +74,18 @@ def server_create(): flash_errors(form) return render_template('server_create.html', user=g.user, form=form, - edit=False, is_admin=g.user.admin) + edit=False, is_admin=util.is_admin(g.user)) @server_blueprint.route('/server//edit', methods=['GET', 'POST']) def server_edit(serverid): server = GameServer.query.get_or_404(serverid) - is_owner = g.user and (g.user.id == server.user_id) + is_owner = (g.user and (util.is_server_owner(g.user, server))) + is_sadmin = (g.user and util.is_super_admin(g.user)) + app.logger.info("Owner: {} Sadmin: {}".format(is_owner, is_sadmin)) if not is_owner: - return 'Not your server', 400 + if not is_sadmin: + raise BadRequestError('You do not have access to this server.') # Attempt encryption/decryption rconDecrypt = util.decrypt(dbKey, server.rcon_password) @@ -105,7 +108,7 @@ def server_edit(serverid): server.ip_string = data['ip_string'] server.port = data['port'] server.rcon_password = encRcon - server.public_server = (data['public_server'] and g.user.admin) + server.public_server = (data['public_server'] and util.is_admin(g.user)) if mock or util.check_server_connection(server, dbKey): db.session.commit() @@ -118,18 +121,20 @@ def server_edit(serverid): flash_errors(form) return render_template('server_create.html', user=g.user, form=form, - edit=True, is_admin=g.user.admin) + edit=True, is_admin=util.is_admin(g.user), is_sadmin=util.is_super_admin(g.user)) @server_blueprint.route('/server//delete', methods=['GET']) def server_delete(serverid): server = GameServer.query.get_or_404(serverid) - is_owner = (g.user is not None) and (g.user.id == server.user_id) + is_owner = g.user and (g.user.id == server.user_id) + is_sadmin = g.user and util.is_super_admin(g.user) if not is_owner: - return 'Not your server', 400 + if not is_sadmin: + raise BadRequestError('You do not have access to this server.') if server.in_use: - return 'Cannot delete when in use', 400 + raise BadRequestError('Cannot delete server when in use.') matches = g.user.matches.filter_by(server_id=serverid) for m in matches: @@ -147,5 +152,7 @@ def myservers(): servers = GameServer.query.filter_by( user_id=g.user.id).order_by(-GameServer.id).limit(50) + if util.is_super_admin(g.user): + servers = GameServer.query.order_by(-GameServer.id) return render_template('servers.html', user=g.user, servers=servers) diff --git a/get5/server_create.html b/get5/server_create.html new file mode 100755 index 0000000..fc82460 --- /dev/null +++ b/get5/server_create.html @@ -0,0 +1,82 @@ +{% from "macros.html" import show_flashed_messages %} + +{% extends "layout.html" %} +{% block content %} + +
+ + {{ show_flashed_messages() }} + + + + +
+ + {{ form.csrf_token }} + +
+ {{ form.display_name.label(class="col-sm-2 control-label") }} +
+ {{ form.display_name(class="form-control", placeholder="Optional") }} +
+
+ +
+ {{ form.ip_string.label(class="col-sm-2 control-label") }} +
+ {{ form.ip_string(class="form-control", placeholder="Server IP") }} +
+
+ +
+ {{ form.port.label(class="col-sm-2 control-label") }} +
+ {{ form.port(class="form-control", placeholder="27015") }} +
+
+ +
+ {{ form.rcon_password.label(class="col-sm-2 control-label") }} +
+ {{ form.rcon_password(class="form-control", placeholder="RCON password") }} +

Your server information will not be exposed to other users, and encrypted in the database.

+
+
+ + {% if is_admin or is_sadmin %} +
+ {{ form.public_server.label(class="col-sm-2 control-label") }} +
+ {{ form.public_server(class="col-sm-offset-1") }} +
+
+ {% endif %} + + {% if edit %} +
+ +
+ {% else %} +
+ +
+ {% endif %} + +
+ +
+ + +
+ + + +{% endblock %} diff --git a/get5/server_test.py b/get5/server_test.py old mode 100644 new mode 100755 index 141c93c..3d6b9a9 --- a/get5/server_test.py +++ b/get5/server_test.py @@ -95,7 +95,7 @@ def test_server_deletion(self): sess['user_id'] = 1 response = c.get('/server/1/delete') self.assertEqual(response.status_code, 400) - self.assertIn('Cannot delete when in use', response.data) + self.assertIn('{"message":"Cannot delete server when in use."}\n', response.data) # Can't delete some else's server when logged in with self.app as c: @@ -103,13 +103,13 @@ def test_server_deletion(self): sess['user_id'] = 2 response = c.get('/server/2/delete') self.assertEqual(response.status_code, 400) - self.assertIn('Not your server', response.data) + self.assertIn('{"message":"You do not have access to this server."}\n', response.data) # Can't delete some else's server without being logged in with self.app as c: response = c.get('/server/1/delete') self.assertEqual(response.status_code, 400) - self.assertIn('Not your server', response.data) + self.assertIn('{"message":"You do not have access to this server."}\n', response.data) # Make sure a user can't edit someone else's servers def test_edit_server_wronguser(self): diff --git a/get5/servers.html b/get5/servers.html new file mode 100755 index 0000000..6166586 --- /dev/null +++ b/get5/servers.html @@ -0,0 +1,81 @@ +{% extends "layout.html" %} +{% block content %} + +
+ +
    + + {% if (servers.all() | length) == 0 %} +
  • + No servers found. +
  • + + {% else %} + + + + + + + + + + + + + + + {% for server in servers %} + + + + + + + + + + {% endfor %} + + +
    Server IDDisplay NameIP AddressPortStatus
    {{ server.id }}{{ server.display_name }}{{ server.ip_string }}{{ server.port }} + {% if server.in_use %} + In use + {% else %} + Free + {% endif %} + + Edit + {% if not server.in_use %} + Delete + {% endif %} +
    + {% endif %} + +
+ + + + + +{% endblock %} diff --git a/get5/team.py b/get5/team.py old mode 100644 new mode 100755 index 7187851..208b523 --- a/get5/team.py +++ b/get5/team.py @@ -1,4 +1,4 @@ -from get5 import app, db, flash_errors, config_setting +from get5 import app, db, flash_errors, config_setting, BadRequestError from models import User, Team import countries @@ -44,7 +44,7 @@ def valid_file(form, field): mock = config_setting("TESTING") if mock: return - elif not g.user.admin: + elif not util.is_admin(g.user): return filename = secure_filename(field.data.filename) # Safe method. @@ -166,7 +166,7 @@ def team_create(): if request.method == 'POST': num_teams = g.user.teams.count() max_teams = config_setting('USER_MAX_TEAMS') - if max_teams >= 0 and num_teams >= max_teams and not g.user.admin: + if max_teams >= 0 and num_teams >= max_teams and not (util.is_admin(g.user) or util.is_super_admin(g.user)): flash( 'You already have the maximum number of teams ({}) stored'.format(num_teams)) @@ -181,7 +181,7 @@ def team_create(): # Update the logo. Passing validation we have the filename in the # list now. - if not mock and g.user.admin and form.upload_logo.data: + if not mock and (util.is_admin(g.user) or util.is_super_admin(g.user)) and form.upload_logo.data: filename = secure_filename(form.upload_logo.data.filename) index_of_dot = filename.index('.') newLogoDetail = filename[:index_of_dot] @@ -191,7 +191,7 @@ def team_create(): data['logo'] = newLogoDetail team = Team.create(g.user, name, tag, flag, logo, - auths, data['public_team'] and g.user.admin, pref_names) + auths, data['public_team'] and (util.is_admin(g.user) or util.is_super_admin(g.user)), pref_names) db.session.commit() app.logger.info( @@ -203,7 +203,7 @@ def team_create(): flash_errors(form) return render_template('team_create.html', user=g.user, form=form, - edit=False, is_admin=g.user.admin, MAXPLAYER=Team.MAXPLAYERS) + edit=False, is_admin=(util.is_admin(g.user) or util.is_super_admin(g.user)), MAXPLAYER=Team.MAXPLAYERS) @team_blueprint.route('/team/', methods=['GET']) @@ -217,7 +217,7 @@ def team_edit(teamid): mock = config_setting("TESTING") team = Team.query.get_or_404(teamid) if not team.can_edit(g.user): - return 'Not your team', 400 + raise BadRequestError("Not your team.") form = TeamForm() # We wish to query this every time, since we can now upload photos. if not mock: @@ -243,18 +243,18 @@ def team_edit(teamid): field.data = None form.public_team.data = team.public_team return render_template('team_create.html', user=g.user, form=form, - edit=True, is_admin=g.user.admin, MAXPLAYER=Team.MAXPLAYERS) + edit=True, is_admin=util.is_admin(g.user), MAXPLAYER=Team.MAXPLAYERS) elif request.method == 'POST': if form.validate(): data = form.data public_team = team.public_team - if g.user.admin: + if util.is_admin(g.user): public_team = data['public_team'] # Update the logo. Passing validation we have the filename in the # list now. - if not mock and g.user.admin and form.upload_logo.data: + if not mock and util.is_admin(g.user) and form.upload_logo.data: filename = secure_filename(form.upload_logo.data.filename) index_of_dot = filename.index('.') newLogoDetail = filename[:index_of_dot] @@ -273,14 +273,14 @@ def team_edit(teamid): return render_template( 'team_create.html', user=g.user, form=form, edit=True, - is_admin=g.user.admin, MAXPLAYER=Team.MAXPLAYERS) + is_admin=util.is_admin(g.user), MAXPLAYER=Team.MAXPLAYERS) @team_blueprint.route('/team//delete') def team_delete(teamid): team = Team.query.get_or_404(teamid) if not team.can_delete(g.user): - return 'Cannot delete this team', 400 + raise BadRequestError("Cannot delete this team.") if Team.query.filter_by(id=teamid).delete(): db.session.commit() @@ -310,7 +310,7 @@ def teams_user(userid): else: # Render teams page - my_teams = (g.user is not None and userid == g.user.id) + my_teams = (g.user is not None and ((userid == g.user.id) or util.is_super_admin(g.user))) teams = user.teams.paginate(page, 20) return render_template( 'teams.html', user=g.user, teams=teams, my_teams=my_teams, @@ -340,8 +340,9 @@ def all_teams(): else: # Render teams page teams = all_public_teams.paginate(page, 20) + editable = g.user is not None and util.is_super_admin(g.user) return render_template( - 'teams.html', user=g.user, teams=teams, my_teams=False, + 'teams.html', user=g.user, teams=teams, my_teams=editable, page=page, owner=None) diff --git a/get5/util.py b/get5/util.py old mode 100644 new mode 100755 index 73153ac..006ece9 --- a/get5/util.py +++ b/get5/util.py @@ -178,3 +178,23 @@ def decrypt(key, source, decode=True): if data[-padding:] != chr(padding) * padding: return None return data[:-padding] # remove the padding + +def is_server_owner(user, server): + if user is None or server is None: + return False + elif user.id == server.user_id: + return True + else: + return False + +def is_super_admin(user): + if user is None: + return False + + return user.super_admin + +def is_admin(user): + if user is None: + return False + + return user.admin \ No newline at end of file