From 732560c7b2038d46dd29bdd9b56196aba995f293 Mon Sep 17 00:00:00 2001 From: PhlexPlexico Date: Wed, 21 Aug 2019 13:03:05 -0600 Subject: [PATCH 01/16] Appending data tables to everywhere. Basically removing pagination in favour of DT. --- get5/leaderboard.py | 1 - get5/match.py | 10 ++- get5/season.py | 16 ++--- get5/templates/match.html | 119 ++++++++++++++++++------------------ get5/templates/matches.html | 19 +++--- get5/templates/seasons.html | 17 ++++-- 6 files changed, 92 insertions(+), 90 deletions(-) diff --git a/get5/leaderboard.py b/get5/leaderboard.py index eb891f5..da1829a 100644 --- a/get5/leaderboard.py +++ b/get5/leaderboard.py @@ -98,7 +98,6 @@ def getPlayerLeaderboard(seasonid=None): dctPlayer['name'] = (player.get_player_name()) dctPlayer['kills'] = (sum(c.kills for c in totalStats)) dctPlayer['deaths'] = (sum(c.deaths for c in totalStats)) - app.logger.info("{}".format(totalStats.count())) dctPlayer['kdr'] = (mean(c.get_kdr() for c in totalStats)) dctPlayer['assists'] = (sum(c.assists for c in totalStats)) dctPlayer['adr'] = (mean(c.get_adr() for c in totalStats)) diff --git a/get5/match.py b/get5/match.py index ff7d18c..e0f624b 100644 --- a/get5/match.py +++ b/get5/match.py @@ -546,21 +546,19 @@ def match_backup(matchid): @match_blueprint.route("/matches") def matches(): - page = util.as_int(request.values.get('page'), on_fail=1) matches = Match.query.order_by(-Match.id).filter_by( - cancelled=False).paginate(page, 20) + cancelled=False) return render_template('matches.html', user=g.user, matches=matches, - my_matches=False, all_matches=True, page=page) + my_matches=False, all_matches=True) @match_blueprint.route("/matches/") def matches_user(userid): user = User.query.get_or_404(userid) - page = util.as_int(request.values.get('page'), on_fail=1) - matches = user.matches.order_by(-Match.id).paginate(page, 20) + matches = user.matches.order_by(-Match.id) is_owner = (g.user is not None) and (userid == g.user.id) return render_template('matches.html', user=g.user, matches=matches, - my_matches=is_owner, all_matches=False, match_owner=user, page=page) + my_matches=is_owner, all_matches=False, match_owner=user) @match_blueprint.route("/mymatches") diff --git a/get5/season.py b/get5/season.py index 981cc31..d67d755 100644 --- a/get5/season.py +++ b/get5/season.py @@ -42,10 +42,9 @@ class SeasonForm(Form): @season_blueprint.route('/seasons') def seasons(): - page = util.as_int(request.values.get('page'), on_fail=1) - seasons = Season.query.order_by(-Season.id).paginate(page, 20) + seasons = Season.query.order_by(-Season.id) return render_template('seasons.html', user=g.user, seasons=seasons, - my_seasons=False, all_seasons=True, page=page) + my_seasons=False, all_seasons=True) @season_blueprint.route('/season/create', methods=['GET', 'POST']) @@ -83,24 +82,21 @@ def season_create(): @season_blueprint.route("/season/") def season_matches(seasonid): season_info = Season.query.get_or_404(seasonid) - page = util.as_int(request.values.get('page'), on_fail=1) matches = Match.query.order_by(-Match.id).filter_by(season_id=seasonid, - cancelled=False).paginate(page, 20) - + cancelled=False) return render_template('matches.html', user=g.user, matches=matches, season_matches=True, all_matches=False, - page=page, season=season_info) + season=season_info) @season_blueprint.route("/season/user/") def seasons_user(userid): user = User.query.get_or_404(userid) - page = util.as_int(request.values.get('page'), on_fail=1) - seasons = user.seasons.order_by(-Season.id).paginate(page, 20) + seasons = user.seasons.order_by(-Season.id) is_owner = (g.user is not None) and (userid == g.user.id) app.logger.info('User is {}'.format(g.user)) return render_template('seasons.html', user=g.user, seasons=seasons, - my_seasons=is_owner, all_matches=False, season_owner=user, page=page) + my_seasons=is_owner, all_matches=False, season_owner=user) @season_blueprint.route('/season//edit', methods=['GET', 'POST']) diff --git a/get5/templates/match.html b/get5/templates/match.html index 180a5a7..7fb8e7b 100644 --- a/get5/templates/match.html +++ b/get5/templates/match.html @@ -2,42 +2,52 @@ {% extends "layout.html" %} -{% macro player_stat_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 %} - - {{ 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) }} - -{% endif %} -{% endfor %} +{% 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) %} @@ -133,12 +143,12 @@

{% endif %} - + {% if vetoes.count() > 0 %}
Vetoes
- +
@@ -172,7 +182,7 @@

{% if map_stats.demoFile is not none and map_stats.demoFile != '' %} - Download Demo + Download Demo {% endif %}

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

@@ -180,29 +190,9 @@

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

{% endif %} -

Veto/Pick
- - - - - - - - - - - - - - - - - - {{ player_stat_table(team1, map_stats) }} - {{ player_stat_table(team2, map_stats) }} - - -
PlayerKillsDeathsAssistsFlash assists1v11v21v3RatingFPRADRHSP
+ {{ind_team_table(team1, map_stats)}} + {{ind_team_table(team2, map_stats)}} +
@@ -243,6 +233,13 @@

} }); - + {% endblock %} \ No newline at end of file diff --git a/get5/templates/matches.html b/get5/templates/matches.html index a054c57..6348a69 100644 --- a/get5/templates/matches.html +++ b/get5/templates/matches.html @@ -1,5 +1,3 @@ -{% from "macros.html" import pagination_buttons, pagination_active %} - {% extends "layout.html" %} {% block content %} @@ -36,7 +34,7 @@

- {% for match in matches.items %} + {% for match in matches %} {{match.id}} @@ -71,12 +69,8 @@

- {{ pagination_buttons(matches) }} - -{{ pagination_active(matches) }} - {% if my_matches %} {% endif %} + + {% endblock %} \ No newline at end of file diff --git a/get5/templates/seasons.html b/get5/templates/seasons.html index 80c730a..9ecade9 100644 --- a/get5/templates/seasons.html +++ b/get5/templates/seasons.html @@ -1,4 +1,3 @@ -{% from "macros.html" import pagination_buttons, pagination_active %} {% extends "layout.html" %} {% block content %} @@ -26,7 +25,7 @@

- {% for season in seasons.items %} + {% for season in seasons %} {{season.id}} @@ -64,12 +63,9 @@

- {{ pagination_buttons(seasons) }} -{{ pagination_active(seasons) }} - {% if my_seasons %} {% endif %} + + {% endblock %} \ No newline at end of file From 2c307dd34fa836f86490baa1f91f84969e325073 Mon Sep 17 00:00:00 2001 From: PhlexPlexico <3514085+PhlexPlexico@users.noreply.github.com> Date: Wed, 21 Aug 2019 14:50:51 -0600 Subject: [PATCH 02/16] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e306480..09c18cb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,6 +44,6 @@ six==1.10.0 SQLAlchemy==1.3.0 statistics==1.0.3.5 webassets==0.11.1 -Werkzeug==0.14 +Werkzeug==0.15.3 wrapt==1.10.8 WTForms==2.1 From 1c5a28841d3b0b91d988c44857ef26bc1423c657 Mon Sep 17 00:00:00 2001 From: PhlexPlexico Date: Thu, 22 Aug 2019 14:01:58 -0600 Subject: [PATCH 03/16] FFW added. --- get5/match.py | 55 +++++++++++++++++++++++++++++++++++-- get5/templates/match.html | 4 +++ get5/templates/matches.html | 10 ++++++- get5/templates/seasons.html | 9 +++++- 4 files changed, 74 insertions(+), 4 deletions(-) diff --git a/get5/match.py b/get5/match.py index e0f624b..f35c24e 100644 --- a/get5/match.py +++ b/get5/match.py @@ -288,6 +288,40 @@ def match_create(): 'match_create.html', form=form, user=g.user, teams=g.user.teams, match_text_option=config_setting('CREATE_MATCH_TITLE_TEXT')) +@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: + winnerId = match.team1_id + elif teamwinner == 2: + winnerId = match.team2_id + else: + raise BadRequestError('Did not select a proper team.') + + match.winner = winnerId + if teamwinner == 1: + match.team1_score = 1 + match.team2_score = 0 + else: + match.team1_score = 0 + match.team2_score = 1 + + match.end_time = datetime.now() + match.forfeit = 1 + server = GameServer.query.get(match.server_id) + if server: + server.in_use = False + + db.session.commit() + + try: + server.send_rcon_command('get5_endmatch', raise_errors=True) + except util.RconError as e: + flash('Failed to cancel match: ' + str(e)) + + return redirect('/mymatches') @match_blueprint.route('/match/') def match(matchid): @@ -302,7 +336,7 @@ def match(matchid): map_stat_list = match.map_stats.all() completed = match.winner try: - if server is not None and completed is None: + if server is not None and (completed is None and match.cancelled == 0): password = server.receive_rcon_value('sv_password') connect_string = str("steam://connect/") + str(server.ip_string) + str(":") + \ str(server.port) + str("/") + str(password) @@ -324,11 +358,13 @@ def match(matchid): 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 = (config_setting( + 'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin) 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, vetoes=vetoes) + gotv_string=gotv_string, super_admin_access=has_super_admin_access, vetoes=vetoes) @match_blueprint.route('/match//scoreboard') @@ -390,6 +426,21 @@ def match_config(matchid): return response +def super_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 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') diff --git a/get5/templates/match.html b/get5/templates/match.html index 7fb8e7b..fa5f9dd 100644 --- a/get5/templates/match.html +++ b/get5/templates/match.html @@ -96,6 +96,10 @@

  • Send rcon command
  • Load a backup file
  • + {% if super_admin_access %} +
  • Forfeit Team 1
  • +
  • Forfeit Team 2
  • + {% endif %}
  • Cancel match
  • diff --git a/get5/templates/matches.html b/get5/templates/matches.html index 6348a69..28aadef 100644 --- a/get5/templates/matches.html +++ b/get5/templates/matches.html @@ -103,8 +103,16 @@

    "order": [], "filter": false, "bInfo" : false, - pageLength: 20 + pageLength: 20, + preDrawCallback: function (settings){ + var api = new $.fn.dataTable.Api(settings); + var pagination = $(this) + .closest('.dataTables_wrapper') + .find('.dataTables_paginate'); + pagination.toggle(api.page.info().pages > 1); + } }); + {% endblock %} \ No newline at end of file diff --git a/get5/templates/seasons.html b/get5/templates/seasons.html index 9ecade9..c997802 100644 --- a/get5/templates/seasons.html +++ b/get5/templates/seasons.html @@ -89,7 +89,14 @@

    "order": [], "filter": false, "bInfo" : false, - pageLength: 20 + pageLength: 20, + preDrawCallback: function (settings){ + var api = new $.fn.dataTable.Api(settings); + var pagination = $(this) + .closest('.dataTables_wrapper') + .find('.dataTables_paginate'); + pagination.toggle(api.page.info().pages > 1); + } }); From 07ee1b08b832941611fc20f325bedabf6203e176 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 22 Aug 2019 16:10:10 -0600 Subject: [PATCH 04/16] Instantiate boolean. --- get5/match.py | 1 + 1 file changed, 1 insertion(+) diff --git a/get5/match.py b/get5/match.py index f35c24e..783de87 100644 --- a/get5/match.py +++ b/get5/match.py @@ -354,6 +354,7 @@ def match(matchid): is_owner = 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( From b5a5d5da7dc4c60a40d7d907ebe9408bfc4cf362 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 22 Aug 2019 17:08:57 -0600 Subject: [PATCH 05/16] Add in map stats. --- get5/match.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/get5/match.py b/get5/match.py index 783de87..7be3233 100644 --- a/get5/match.py +++ b/get5/match.py @@ -301,13 +301,16 @@ def match_forfeit(matchid, teamwinner): raise BadRequestError('Did not select a proper team.') match.winner = winnerId + map_stats = MapStats.get_or_create(match.id, 0, '', '') if teamwinner == 1: match.team1_score = 1 match.team2_score = 0 + map_stats.team1_score = 16 else: match.team1_score = 0 match.team2_score = 1 - + map_stats.team2_score = 16 + match.start_time = datetime.now() match.end_time = datetime.now() match.forfeit = 1 server = GameServer.query.get(match.server_id) From edb9254601a8b9de57fc68ecb1ec06698b05aaf0 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 22 Aug 2019 17:11:24 -0600 Subject: [PATCH 06/16] Adjust get team name. --- get5/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/get5/models.py b/get5/models.py index 2ce7bf0..b0b8ac9 100644 --- a/get5/models.py +++ b/get5/models.py @@ -482,10 +482,10 @@ def send_to_server(self): return True def get_team1(self): - return Team.query.get(self.team1_id) + return Team.query.get(self.team1_id).with_entities(Team.name).first() def get_team2(self): - return Team.query.get(self.team2_id) + return Team.query.get(self.team2_id).with_entities(Team.name).first() def get_user(self): return User.query.get(self.user_id) From 820252cfc6ec9404e6210f571b117675476ef26a Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 22 Aug 2019 17:12:20 -0600 Subject: [PATCH 07/16] Update query result. --- get5/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/get5/models.py b/get5/models.py index b0b8ac9..2a8399f 100644 --- a/get5/models.py +++ b/get5/models.py @@ -482,10 +482,10 @@ def send_to_server(self): return True def get_team1(self): - return Team.query.get(self.team1_id).with_entities(Team.name).first() + return Team.query.with_entities(Team.name).get(self.team1_id).first() def get_team2(self): - return Team.query.get(self.team2_id).with_entities(Team.name).first() + return Team.query.with_entities(Team.name).get(self.team2_id).first() def get_user(self): return User.query.get(self.user_id) From 8887c96afe93b7b61db2d16223abdf1fc0f5eaeb Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 22 Aug 2019 17:13:18 -0600 Subject: [PATCH 08/16] Revert. --- get5/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/get5/models.py b/get5/models.py index 2a8399f..2ce7bf0 100644 --- a/get5/models.py +++ b/get5/models.py @@ -482,10 +482,10 @@ def send_to_server(self): return True def get_team1(self): - return Team.query.with_entities(Team.name).get(self.team1_id).first() + return Team.query.get(self.team1_id) def get_team2(self): - return Team.query.with_entities(Team.name).get(self.team2_id).first() + return Team.query.get(self.team2_id) def get_user(self): return User.query.get(self.user_id) From 9acf77a26d0a28e9bbf1376e9988f7dfd47368f0 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 22 Aug 2019 17:15:31 -0600 Subject: [PATCH 09/16] Update naming in forfeit. --- get5/templates/match.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/get5/templates/match.html b/get5/templates/match.html index fa5f9dd..97d4a5b 100644 --- a/get5/templates/match.html +++ b/get5/templates/match.html @@ -121,7 +121,7 @@

    {% endif %} From 0031e7f80aae61c8f1071062253dee6d0edadbb8 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 22 Aug 2019 17:16:51 -0600 Subject: [PATCH 10/16] Missing datetime. --- get5/match.py | 1 + 1 file changed, 1 insertion(+) diff --git a/get5/match.py b/get5/match.py index 7be3233..b159048 100644 --- a/get5/match.py +++ b/get5/match.py @@ -313,6 +313,7 @@ def match_forfeit(matchid, teamwinner): match.start_time = datetime.now() match.end_time = datetime.now() match.forfeit = 1 + map_stats.end_time = datetime.now() server = GameServer.query.get(match.server_id) if server: server.in_use = False From 4563c53aea9042f31723a22f6e47d9409e393320 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 22 Aug 2019 23:30:11 -0600 Subject: [PATCH 11/16] Remove enforce teams as it doesn't work. Add in private match viewing. --- README.md | 1 - get5/get5_test.py | 2 +- get5/match.py | 43 +++++++++++++++++++++------- get5/match_test.py | 4 --- get5/models.py | 12 ++++---- get5/templates/match_create.html | 7 ----- migrations/versions/f5efc36b3cc9_.py | 23 +++++++++++++++ 7 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 migrations/versions/f5efc36b3cc9_.py diff --git a/README.md b/README.md index 6db46ec..f702e5c 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,6 @@ Please see the [installation instructions](https://github.com/PhlexPlexico/get5- Autoformatting: ```sh -cd get5 autopep8 -r get5 --in-place autopep8 -r get5 --diff # should have no output ``` diff --git a/get5/get5_test.py b/get5/get5_test.py index d14c515..fc66d49 100644 --- a/get5/get5_test.py +++ b/get5/get5_test.py @@ -47,7 +47,7 @@ def create_test_data(self): db.session.commit() Match.create(user, team1.id, team2.id, '', '', 1, False, - 'Map {MAPNUMBER}', ['de_dust2', 'de_cache', 'de_mirage'], season.id, 'always_knife', 'CT', True, server.id, 0, 0, None) + 'Map {MAPNUMBER}', ['de_dust2', 'de_cache', 'de_mirage'], season.id, 'always_knife', 'CT', server.id, 0, 0, None, False) db.session.commit() vetoBan = Veto.create(1, 'EnvyUs', 'de_dust2', 'ban') diff --git a/get5/match.py b/get5/match.py index b159048..f3f0ea3 100644 --- a/get5/match.py +++ b/get5/match.py @@ -119,9 +119,6 @@ class MatchForm(Form): season_selection = SelectField('Season', coerce=int, validators=[validators.optional()]) - enforce_teams = BooleanField('Enforce Teams', - default=True) - team1_series_score = IntegerField('Team 1 Series Score', default=0, validators=[validators.NumberRange(0, 7)]) @@ -133,6 +130,9 @@ class MatchForm(Form): spectator_string = StringField('Spectator IDs', default='') + private_match = BooleanField('Private Match?', + default=False) + def add_teams(self, user): if self.team1_id.choices is None: self.team1_id.choices = [] @@ -251,16 +251,18 @@ def match_create(): suc, new_auth = steamid.auth_to_steam64(auth) if suc: specList.append(new_auth) + # End Spectator Feature + match = Match.create( g.user, form.data['team1_id'], form.data['team2_id'], form.data['team1_string'], form.data['team2_string'], max_maps, skip_veto, - form.data['match_title'], form.data[ - 'veto_mappool'], season_id, - form.data['side_type'], form.data['veto_first'], - form.data['enforce_teams'], form.data['server_id'], - team1_series_score, team2_series_score, specList) + form.data['match_title'], form.data['veto_mappool'], + season_id, form.data['side_type'], + form.data['veto_first'], form.data['server_id'], + team1_series_score, team2_series_score, specList, + form.data['private_match']) # Save plugin version data if we have it if json_reply and 'plugin_version' in json_reply: @@ -288,6 +290,7 @@ def match_create(): 'match_create.html', form=form, user=g.user, teams=g.user.teams, match_text_option=config_setting('CREATE_MATCH_TITLE_TEXT')) + @match_blueprint.route('/match//forfeit/') def match_forfeit(matchid, teamwinner): app.logger.info("Match server id is: {}".format(matchid)) @@ -299,7 +302,7 @@ def match_forfeit(matchid, teamwinner): winnerId = match.team2_id else: raise BadRequestError('Did not select a proper team.') - + match.winner = winnerId map_stats = MapStats.get_or_create(match.id, 0, '', '') if teamwinner == 1: @@ -327,9 +330,12 @@ def match_forfeit(matchid, teamwinner): return redirect('/mymatches') + @match_blueprint.route('/match/') def match(matchid): match = Match.query.get_or_404(matchid) + # Begin Private/Public Match Implementation + vetoes = Veto.query.filter_by(match_id=matchid) if match.server_id: server = GameServer.query.get_or_404(match.server_id) @@ -337,6 +343,22 @@ def match(matchid): server = None team1 = Team.query.get_or_404(match.team1_id) team2 = Team.query.get_or_404(match.team2_id) + if match.is_private_match(): + if not g.user: + raise BadRequestError("Please login before viewing this match.") + # Get team lists, and check if logged in user is part of match. + if not (g.user.id == match.user_id) or (config_setting( + 'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin): + isPlayer = False + playerList = list(set(team1.auths + team2.auths)) + for player in playerList: + if g.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!") + map_stat_list = match.map_stats.all() completed = match.winner try: @@ -364,7 +386,7 @@ def match(matchid): has_admin_access = is_owner or (config_setting( 'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin) has_super_admin_access = (config_setting( - 'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin) + 'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin) return render_template( 'match.html', user=g.user, admin_access=has_admin_access, match=match, team1=team1, team2=team2, @@ -446,6 +468,7 @@ def super_admintools_check(user, match): 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') diff --git a/get5/match_test.py b/get5/match_test.py index 005d4e5..5635dd6 100644 --- a/get5/match_test.py +++ b/get5/match_test.py @@ -39,7 +39,6 @@ def test_match_create_already_live(self): 'series_type': 'bo3', 'veto_first': 'CT', 'veto_mappool': ['de_dust2', 'de_cache', 'de_mirage'], - 'enforce_teams': True, 'season_id': None, 'team1_series_score': 0, 'team2_series_score': 0, @@ -67,7 +66,6 @@ def test_match_create_not_my_server(self): 'series_type': 'bo3', 'veto_first': 'CT', 'veto_mappool': ['de_dust2', 'de_cache', 'de_mirage'], - 'enforce_teams': True, 'season_id': None, 'team1_series_score': 0, 'team2_series_score': 0, @@ -97,7 +95,6 @@ def test_match_create(self): 'series_type': 'bo3', 'veto_first': 'CT', 'veto_mappool': ['de_dust2', 'de_cache', 'de_mirage'], - 'enforce_teams': True, 'season_id': None, 'team1_series_score': 0, 'team2_series_score': 0, @@ -152,7 +149,6 @@ def test_match_create_not_my_server(self): 'series_type': 'bo3', 'veto_first': 'CT', 'veto_mappool': ['de_dust2', 'de_cache', 'de_mirage'], - 'enforce_teams': True, 'season_id': None, 'team1_series_score': 0, 'team2_series_score': 0, diff --git a/get5/models.py b/get5/models.py index 2ce7bf0..9315472 100644 --- a/get5/models.py +++ b/get5/models.py @@ -356,7 +356,6 @@ class Match(db.Model): api_key = db.Column(db.String(32)) veto_first = db.Column(db.String(5)) veto_mappool = db.Column(db.String(500)) - enforce_teams = db.Column(db.Boolean, default=True) map_stats = db.relationship('MapStats', backref='match', lazy='dynamic') side_type = db.Column(db.String(32)) @@ -365,13 +364,14 @@ class Match(db.Model): team1_series_score = db.Column(db.Integer, default=0) team2_series_score = db.Column(db.Integer, default=0) spectator_auths = db.Column(db.PickleType) + private_match = db.Column(db.Boolean) @staticmethod def create(user, team1_id, team2_id, team1_string, team2_string, max_maps, skip_veto, title, veto_mappool, season_id, - side_type, veto_first, enforce_teams=True, server_id=None, + side_type, veto_first, server_id=None, team1_series_score=None, team2_series_score=None, - spectator_auths=None): + spectator_auths=None, private_match=False): rv = Match() rv.user_id = user.id rv.team1_id = team1_id @@ -383,7 +383,6 @@ def create(user, team1_id, team2_id, team1_string, team2_string, rv.veto_mappool = ' '.join(veto_mappool) rv.server_id = server_id rv.max_maps = max_maps - rv.enforce_teams = enforce_teams if veto_first == "CT": rv.veto_first = "team1" elif veto_first == "T": @@ -395,6 +394,7 @@ def create(user, team1_id, team2_id, team1_string, team2_string, rv.team1_series_score = team1_series_score rv.team2_series_score = team2_series_score rv.spectator_auths = spectator_auths + rv.private_match = private_match db.session.add(rv) return rv @@ -422,6 +422,9 @@ def get_status_string(self, show_winner=True): else: return 'Cancelled' + def is_private_match(self): + return self.private_match + def get_vs_string(self): team1 = self.get_team1() team2 = self.get_team2() @@ -554,7 +557,6 @@ def add_if(key, value): add_team_data('team2', self.team2_id, self.team2_string) d['cvars'] = {} - d['cvars']['get5_check_auths'] = int(self.enforce_teams) d['cvars']['get5_web_api_url'] = url_for( 'home', _external=True, _scheme='http') diff --git a/get5/templates/match_create.html b/get5/templates/match_create.html index 4fb4eb6..734c9a0 100644 --- a/get5/templates/match_create.html +++ b/get5/templates/match_create.html @@ -146,13 +146,6 @@ -
    - {{ form.enforce_teams.label(class="col-sm-2 control-label") }} -
    - {{ form.enforce_teams(class="form-control input-sm") }} -
    -
    -
    {{ form.spectator_string.label(class="col-sm-2 control-label") }}
    diff --git a/migrations/versions/f5efc36b3cc9_.py b/migrations/versions/f5efc36b3cc9_.py new file mode 100644 index 0000000..5e154d0 --- /dev/null +++ b/migrations/versions/f5efc36b3cc9_.py @@ -0,0 +1,23 @@ +"""Include private match option. + +Revision ID: f5efc36b3cc9 +Revises: ca309999b2ee +Create Date: 2019-08-22 22:46:24.950968 + +""" + +# revision identifiers, used by Alembic. +revision = 'f5efc36b3cc9' +down_revision = 'ca309999b2ee' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +def upgrade(): + op.add_column('match', sa.Column('private_match', sa.Boolean(), nullable=True, default=False)) + op.drop_column('match', 'enforce_teams') + +def downgrade(): + op.drop_column('match', 'private_match') + op.add_column('match', sa.Column('enforce_teams', sa.Boolean(), nullable=True)) From 9778cbd4c13ad1f3825e53a3f956cd8b1e485ea5 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 23 Aug 2019 00:13:16 -0600 Subject: [PATCH 12/16] Adding in use for private or public match. Lock down some admin tools, like who can add players, so we can open up players creating matches. Server owners and super admins can only send rcon commands, since you can retrieve passwords from public servers. Will need to rework admins access. For now its rough. Will think things through more later. --- get5/match.py | 57 ++++++++++++++++++---------- get5/models.py | 3 ++ get5/templates/match.html | 7 ++++ get5/templates/match_create.html | 7 ++++ instance/prod_config.py.default | 4 ++ migrations/versions/f5efc36b3cc9_.py | 4 +- 6 files changed, 61 insertions(+), 21 deletions(-) diff --git a/get5/match.py b/get5/match.py index f3f0ea3..46b64ad 100644 --- a/get5/match.py +++ b/get5/match.py @@ -330,6 +330,22 @@ 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 + playerList = list(set(team1.auths + team2.auths)) + 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): @@ -343,21 +359,7 @@ def match(matchid): server = None team1 = Team.query.get_or_404(match.team1_id) team2 = Team.query.get_or_404(match.team2_id) - if match.is_private_match(): - if not g.user: - raise BadRequestError("Please login before viewing this match.") - # Get team lists, and check if logged in user is part of match. - if not (g.user.id == match.user_id) or (config_setting( - 'ADMINS_ACCESS_ALL_MATCHES') and g.user.admin): - isPlayer = False - playerList = list(set(team1.auths + team2.auths)) - for player in playerList: - if g.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!") + check_private_or_public(g.user, match, team1, team2) map_stat_list = match.map_stats.all() completed = match.winner @@ -379,19 +381,21 @@ def match(matchid): .format(server.ip_string)) is_owner = False + is_server_owner = 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 = (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) 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) + gotv_string=gotv_string, super_admin_access=has_super_admin_access, vetoes=vetoes, + server_owner=is_server_owner) @match_blueprint.route('/match//scoreboard') @@ -407,6 +411,7 @@ def merge(a, b): match = Match.query.get_or_404(matchid) team1 = Team.query.get_or_404(match.team1_id) team2 = Team.query.get_or_404(match.team2_id) + check_private_or_public(g.user, match, team1, team2) map_num = 0 map_stat_list = match.map_stats.all() player_dict = {} @@ -452,13 +457,22 @@ 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.admin and get5.config_setting( - 'ADMINS_ACCESS_ALL_MATCHES') + grant_admin_access = user.super_admin + if not grant_admin_access: raise BadRequestError('You do not have access to this page') @@ -513,6 +527,9 @@ def match_rcon(matchid): command = request.values.get('command') server = GameServer.query.get_or_404(match.server_id) + # 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 command: try: diff --git a/get5/models.py b/get5/models.py index 9315472..81d84b1 100644 --- a/get5/models.py +++ b/get5/models.py @@ -21,6 +21,7 @@ class User(db.Model): steam_id = db.Column(db.String(40), unique=True) name = db.Column(db.String(40)) admin = db.Column(db.Boolean, default=False) + super_admin = db.Column(db.Boolean, default=False) servers = db.relationship('GameServer', backref='user', lazy='dynamic') teams = db.relationship('Team', backref='user', lazy='dynamic') matches = db.relationship('Match', backref='user', lazy='dynamic') @@ -37,6 +38,8 @@ def get_or_create(steam_id): rv.admin = ('ADMIN_IDS' in app.config) and ( steam_id in app.config['ADMIN_IDS']) + rv.super_admin = ('SUPER_ADMIN_IDS' in app.config) and ( + steam_id in app.config['SUPER_ADMIN_IDS']) return rv def get_url(self): diff --git a/get5/templates/match.html b/get5/templates/match.html index 97d4a5b..8c34bfa 100644 --- a/get5/templates/match.html +++ b/get5/templates/match.html @@ -90,15 +90,22 @@

  • Pause match
  • Unpause match
  • {% endif %} + {% if super_admin_access %}
  • Add player to team1
  • Add player to team2
  • Add player to specator list
  • + {% else %} +
  • Add player to specator list
  • + {% endif %} + {% if server_owner or super_admin_access %}
  • Send rcon command
  • + {% endif %}
  • Load a backup file
  • {% if super_admin_access %}
  • Forfeit Team 1
  • Forfeit Team 2
  • + {% endif %}
  • Cancel match
  • diff --git a/get5/templates/match_create.html b/get5/templates/match_create.html index 734c9a0..841e5c2 100644 --- a/get5/templates/match_create.html +++ b/get5/templates/match_create.html @@ -146,6 +146,13 @@

    +
    + {{ form.private_match.label(class="col-sm-2 control-label") }} +
    + {{ form.private_match(class="form-control input-sm") }} +
    +
    +
    {{ form.spectator_string.label(class="col-sm-2 control-label") }}
    diff --git a/instance/prod_config.py.default b/instance/prod_config.py.default index 1a45fb7..46bae2a 100644 --- a/instance/prod_config.py.default +++ b/instance/prod_config.py.default @@ -60,3 +60,7 @@ WHITELISTED_IDS = [] # Admins will have extra access to create "public" teams, and if ADMINS_ACCESS_ALL_MATCHES # is set, they can access admin info for all matches (can pause, cancel, etc.) ANY match. ADMIN_IDS = [] + +# Super admins will have the most access, as if ADMINS_ACCESS_ALL_MATCHES was set. +# They'll be able to access EVERYTHING for ANY match, as well as FORFEIT matches. +SUPER_ADMIN_IDS = [] \ No newline at end of file diff --git a/migrations/versions/f5efc36b3cc9_.py b/migrations/versions/f5efc36b3cc9_.py index 5e154d0..a089e2b 100644 --- a/migrations/versions/f5efc36b3cc9_.py +++ b/migrations/versions/f5efc36b3cc9_.py @@ -1,4 +1,4 @@ -"""Include private match option. +"""Include private match option, remove enforce teams, include super_admin. Revision ID: f5efc36b3cc9 Revises: ca309999b2ee @@ -16,8 +16,10 @@ def upgrade(): op.add_column('match', sa.Column('private_match', sa.Boolean(), nullable=True, default=False)) + op.add_column('user', sa.Column('super_admin', sa.Boolean(), nullable=False, default=False)) op.drop_column('match', 'enforce_teams') def downgrade(): op.drop_column('match', 'private_match') + op.drop_column('user', 'super_admin') op.add_column('match', sa.Column('enforce_teams', sa.Boolean(), nullable=True)) From 200e6abe72f0bad028182825d9d72c6b7e19793c Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 23 Aug 2019 00:23:44 -0600 Subject: [PATCH 13/16] Add in playerstats steam IDs with teams. --- get5/match.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/get5/match.py b/get5/match.py index 46b64ad..706fd4b 100644 --- a/get5/match.py +++ b/get5/match.py @@ -338,7 +338,10 @@ def check_private_or_public(user, match, team1, team2): if not (user.id == match.user_id) or (config_setting( 'ADMINS_ACCESS_ALL_MATCHES') and user.admin) or user.super_admin: isPlayer = False - playerList = list(set(team1.auths + team2.auths)) + playerstats_steam = PlayerStats.query(PlayerStats.steam_id).filter_by( + match_id=match.id) + playerList = list(set(team1.auths + team2.auths + playerstats_steam)) + app.logger.info("{}".format(playerList)) for player in playerList: if user.steam_id == player: isPlayer = True From edf749ab4c3c4e5685d39615f5c385870edd870e Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 23 Aug 2019 00:28:03 -0600 Subject: [PATCH 14/16] Remove logging. --- get5/match.py | 1 - 1 file changed, 1 deletion(-) diff --git a/get5/match.py b/get5/match.py index 706fd4b..23aeeb1 100644 --- a/get5/match.py +++ b/get5/match.py @@ -341,7 +341,6 @@ def check_private_or_public(user, match, team1, team2): playerstats_steam = PlayerStats.query(PlayerStats.steam_id).filter_by( match_id=match.id) playerList = list(set(team1.auths + team2.auths + playerstats_steam)) - app.logger.info("{}".format(playerList)) for player in playerList: if user.steam_id == player: isPlayer = True From 39fe8c3f57f45f15e6d3183527f8e3f3e02eaa24 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 23 Aug 2019 16:04:50 -0600 Subject: [PATCH 15/16] 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 From 164d7a9854d61bb98ad8ad4a4fdb87bc65f792b4 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 23 Aug 2019 16:07:10 -0600 Subject: [PATCH 16/16] Move files mistake. --- get5/match.html | 256 ------------------------------ get5/server_create.html | 82 ---------- get5/servers.html | 81 ---------- get5/templates/match.html | 6 +- get5/templates/server_create.html | 4 +- get5/templates/servers.html | 19 +++ 6 files changed, 24 insertions(+), 424 deletions(-) delete mode 100755 get5/match.html delete mode 100755 get5/server_create.html delete mode 100755 get5/servers.html mode change 100644 => 100755 get5/templates/match.html mode change 100644 => 100755 get5/templates/server_create.html mode change 100644 => 100755 get5/templates/servers.html diff --git a/get5/match.html b/get5/match.html deleted file mode 100755 index 056452d..0000000 --- a/get5/match.html +++ /dev/null @@ -1,256 +0,0 @@ -{% 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/server_create.html b/get5/server_create.html deleted file mode 100755 index fc82460..0000000 --- a/get5/server_create.html +++ /dev/null @@ -1,82 +0,0 @@ -{% 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/servers.html b/get5/servers.html deleted file mode 100755 index 6166586..0000000 --- a/get5/servers.html +++ /dev/null @@ -1,81 +0,0 @@ -{% 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/templates/match.html b/get5/templates/match.html old mode 100644 new mode 100755 index 8c34bfa..056452d --- a/get5/templates/match.html +++ b/get5/templates/match.html @@ -78,7 +78,7 @@

    {{ match.team2_score }} {{ team2.get_logo_or_flag_html(1.0, team1) }} {{team2.name}} - {% if admin_access and (match.live() or match.pending()) %} + {% if (admin_access or match_owner or super_admin_access) and (match.live() or match.pending()) %} - {% if is_admin %} + {% if is_admin or is_sadmin %}
    {{ form.public_server.label(class="col-sm-2 control-label") }}
    diff --git a/get5/templates/servers.html b/get5/templates/servers.html old mode 100644 new mode 100755 index 536af2a..6166586 --- a/get5/templates/servers.html +++ b/get5/templates/servers.html @@ -59,4 +59,23 @@ $("#myservers").parent().parent().addClass("active"); }) + + {% endblock %}