mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-17 22:14:25 +01:00
Supports PY3, refinements to chal editor and viewer, model changes to resolve issues
This commit is contained in:
@@ -22,7 +22,7 @@ def create_app(config='CTFd.config'):
|
|||||||
global mail
|
global mail
|
||||||
mail = Mail(app)
|
mail = Mail(app)
|
||||||
|
|
||||||
Session(app)
|
#Session(app)
|
||||||
|
|
||||||
from CTFd.views import views
|
from CTFd.views import views
|
||||||
from CTFd.challenges import challenges
|
from CTFd.challenges import challenges
|
||||||
|
|||||||
114
CTFd/admin.py
114
CTFd/admin.py
@@ -147,7 +147,6 @@ def admin_css():
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
css = request.form['css']
|
css = request.form['css']
|
||||||
css = set_config('css', css)
|
css = set_config('css', css)
|
||||||
print css
|
|
||||||
return "1"
|
return "1"
|
||||||
return "0"
|
return "0"
|
||||||
|
|
||||||
@@ -198,12 +197,12 @@ def admin_chals():
|
|||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
chals = Challenges.query.add_columns('id', 'name', 'value', 'description', 'category').order_by(Challenges.value).all()
|
chals = Challenges.query.add_columns('id', 'name', 'value', 'description', 'category').order_by(Challenges.value).all()
|
||||||
|
|
||||||
json = {'game':[]}
|
json_data = {'game':[]}
|
||||||
for x in chals:
|
for x in chals:
|
||||||
json['game'].append({'id':x[1], 'name':x[2], 'value':x[3], 'description':x[4], 'category':x[5]})
|
json_data['game'].append({'id':x[1], 'name':x[2], 'value':x[3], 'description':x[4], 'category':x[5]})
|
||||||
|
|
||||||
db.session.close()
|
db.session.close()
|
||||||
return jsonify(json)
|
return jsonify(json_data)
|
||||||
else:
|
else:
|
||||||
return render_template('admin/chals.html')
|
return render_template('admin/chals.html')
|
||||||
|
|
||||||
@@ -212,21 +211,25 @@ def admin_chals():
|
|||||||
@admins_only
|
@admins_only
|
||||||
def admin_keys(chalid):
|
def admin_keys(chalid):
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
keys = Keys.query.filter_by(chal=chalid).all()
|
chal = Challenges.query.filter_by(id=chalid).first_or_404()
|
||||||
json = {'keys':[]}
|
json_data = {'keys':[]}
|
||||||
for x in keys:
|
flags = json.loads(chal.flags)
|
||||||
json['keys'].append({'id':x.id, 'key':x.flag, 'type':x.key_type})
|
for i, x in enumerate(flags):
|
||||||
return jsonify(json)
|
json_data['keys'].append({'id':i, 'key':x['flag'], 'type':x['type']})
|
||||||
|
return jsonify(json_data)
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
keys = Keys.query.filter_by(chal=chalid).all()
|
chal = Challenges.query.filter_by(id=chalid).first()
|
||||||
for x in keys:
|
|
||||||
db.session.delete(x)
|
|
||||||
|
|
||||||
newkeys = request.form.getlist('keys[]')
|
newkeys = request.form.getlist('keys[]')
|
||||||
newvals = request.form.getlist('vals[]')
|
newvals = request.form.getlist('vals[]')
|
||||||
|
print(list(zip(newkeys, newvals)))
|
||||||
|
flags = []
|
||||||
for flag, val in zip(newkeys, newvals):
|
for flag, val in zip(newkeys, newvals):
|
||||||
key = Keys(chalid, flag, val)
|
flag_dict = {'flag':flag, 'type':int(val)}
|
||||||
db.session.add(key)
|
flags.append(flag_dict)
|
||||||
|
json_data = json.dumps(flags)
|
||||||
|
|
||||||
|
chal.flags = json_data
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db.session.close()
|
db.session.close()
|
||||||
@@ -238,10 +241,10 @@ def admin_keys(chalid):
|
|||||||
def admin_tags(chalid):
|
def admin_tags(chalid):
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
tags = Tags.query.filter_by(chal=chalid).all()
|
tags = Tags.query.filter_by(chal=chalid).all()
|
||||||
json = {'tags':[]}
|
json_data = {'tags':[]}
|
||||||
for x in tags:
|
for x in tags:
|
||||||
json['tags'].append({'id':x.id, 'chal':x.chal, 'tag':x.tag})
|
json_data['tags'].append({'id':x.id, 'chal':x.chal, 'tag':x.tag})
|
||||||
return jsonify(json)
|
return jsonify(json_data)
|
||||||
|
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
newtags = request.form.getlist('tags[]')
|
newtags = request.form.getlist('tags[]')
|
||||||
@@ -269,10 +272,10 @@ def admin_delete_tags(tagid):
|
|||||||
def admin_files(chalid):
|
def admin_files(chalid):
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
files = Files.query.filter_by(chal=chalid).all()
|
files = Files.query.filter_by(chal=chalid).all()
|
||||||
json = {'files':[]}
|
json_data = {'files':[]}
|
||||||
for x in files:
|
for x in files:
|
||||||
json['files'].append({'id':x.id, 'file':x.location})
|
json_data['files'].append({'id':x.id, 'file':x.location})
|
||||||
return jsonify(json)
|
return jsonify(json_data)
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if request.form['method'] == "delete":
|
if request.form['method'] == "delete":
|
||||||
f = Files.query.filter_by(id=request.form['file']).first_or_404()
|
f = Files.query.filter_by(id=request.form['file']).first_or_404()
|
||||||
@@ -305,11 +308,20 @@ def admin_files(chalid):
|
|||||||
return redirect('/admin/chals')
|
return redirect('/admin/chals')
|
||||||
|
|
||||||
|
|
||||||
@admin.route('/admin/teams')
|
@admin.route('/admin/teams', defaults={'page':'1'})
|
||||||
|
@admin.route('/admin/teams/<page>')
|
||||||
@admins_only
|
@admins_only
|
||||||
def admin_teams():
|
def admin_teams(page):
|
||||||
teams = Teams.query.all()
|
page = abs(int(page))
|
||||||
return render_template('admin/teams.html', teams=teams)
|
results_per_page = 50
|
||||||
|
page_start = results_per_page * ( page - 1 )
|
||||||
|
page_end = results_per_page * ( page - 1 ) + results_per_page
|
||||||
|
|
||||||
|
teams = Teams.query.slice(page_start, page_end).all()
|
||||||
|
count = db.session.query(db.func.count(Teams.id)).first()[0]
|
||||||
|
print(count)
|
||||||
|
pages = int(count / results_per_page) + (count % results_per_page > 0)
|
||||||
|
return render_template('admin/teams.html', teams=teams, pages=pages)
|
||||||
|
|
||||||
|
|
||||||
@admin.route('/admin/team/<teamid>', methods=['GET', 'POST'])
|
@admin.route('/admin/team/<teamid>', methods=['GET', 'POST'])
|
||||||
@@ -317,17 +329,19 @@ def admin_teams():
|
|||||||
def admin_team(teamid):
|
def admin_team(teamid):
|
||||||
user = Teams.query.filter_by(id=teamid).first()
|
user = Teams.query.filter_by(id=teamid).first()
|
||||||
solves = Solves.query.filter_by(teamid=teamid).all()
|
solves = Solves.query.filter_by(teamid=teamid).all()
|
||||||
addrs = Tracking.query.filter_by(team=teamid).group_by(Tracking.ip).all()
|
addrs = Tracking.query.filter_by(team=teamid).order_by(Tracking.date.desc()).group_by(Tracking.ip).all()
|
||||||
|
wrong_keys = WrongKeys.query.filter_by(team=teamid).order_by(WrongKeys.date.desc()).all()
|
||||||
score = user.score()
|
score = user.score()
|
||||||
place = user.place()
|
place = user.place()
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
return render_template('admin/team.html', solves=solves, team=user, addrs=addrs, score=score, place=place)
|
return render_template('admin/team.html', solves=solves, team=user, addrs=addrs, score=score, place=place, wrong_keys=wrong_keys)
|
||||||
elif request.method == 'POST':
|
elif request.method == 'POST':
|
||||||
admin_user = request.form.get('admin', "false")
|
admin_user = request.form.get('admin', "false")
|
||||||
admin_user = 1 if admin_user == "true" else 0
|
admin_user = 1 if admin_user == "true" else 0
|
||||||
if admin:
|
if admin:
|
||||||
user.admin = 1
|
user.admin = 1
|
||||||
|
user.banned = 1
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return jsonify({'data': ['success']})
|
return jsonify({'data': ['success']})
|
||||||
|
|
||||||
@@ -407,16 +421,16 @@ def delete_team(teamid):
|
|||||||
def admin_graph(graph_type):
|
def admin_graph(graph_type):
|
||||||
if graph_type == 'categories':
|
if graph_type == 'categories':
|
||||||
categories = db.session.query(Challenges.category, db.func.count(Challenges.category)).group_by(Challenges.category).all()
|
categories = db.session.query(Challenges.category, db.func.count(Challenges.category)).group_by(Challenges.category).all()
|
||||||
json = {'categories':[]}
|
json_data = {'categories':[]}
|
||||||
for category, count in categories:
|
for category, count in categories:
|
||||||
json['categories'].append({'category':category, 'count':count})
|
json_data['categories'].append({'category':category, 'count':count})
|
||||||
return jsonify(json)
|
return jsonify(json_data)
|
||||||
elif graph_type == "solves":
|
elif graph_type == "solves":
|
||||||
solves = Solves.query.add_columns(db.func.count(Solves.chalid)).group_by(Solves.chalid).all()
|
solves = Solves.query.add_columns(db.func.count(Solves.chalid)).group_by(Solves.chalid).all()
|
||||||
json = {}
|
json_data = {}
|
||||||
for chal, count in solves:
|
for chal, count in solves:
|
||||||
json[chal.chal.name] = count
|
json_data[chal.chal.name] = count
|
||||||
return jsonify(json)
|
return jsonify(json_data)
|
||||||
|
|
||||||
|
|
||||||
@admin.route('/admin/scoreboard')
|
@admin.route('/admin/scoreboard')
|
||||||
@@ -436,10 +450,10 @@ def admin_scores():
|
|||||||
quickest = db.func.max(Solves.date).label('quickest')
|
quickest = db.func.max(Solves.date).label('quickest')
|
||||||
teams = db.session.query(Solves.teamid, Teams.name, score).join(Teams).join(Challenges).filter(Teams.banned == None).group_by(Solves.teamid).order_by(score.desc(), quickest)
|
teams = db.session.query(Solves.teamid, Teams.name, score).join(Teams).join(Challenges).filter(Teams.banned == None).group_by(Solves.teamid).order_by(score.desc(), quickest)
|
||||||
db.session.close()
|
db.session.close()
|
||||||
json = {'teams':[]}
|
json_data = {'teams':[]}
|
||||||
for i, x in enumerate(teams):
|
for i, x in enumerate(teams):
|
||||||
json['teams'].append({'place':i+1, 'id':x.teamid, 'name':x.name,'score':int(x.score)})
|
json_data['teams'].append({'place':i+1, 'id':x.teamid, 'name':x.name,'score':int(x.score)})
|
||||||
return jsonify(json)
|
return jsonify(json_data)
|
||||||
|
|
||||||
|
|
||||||
@admin.route('/admin/solves/<teamid>', methods=['GET'])
|
@admin.route('/admin/solves/<teamid>', methods=['GET'])
|
||||||
@@ -450,10 +464,10 @@ def admin_solves(teamid="all"):
|
|||||||
else:
|
else:
|
||||||
solves = Solves.query.filter_by(teamid=teamid).all()
|
solves = Solves.query.filter_by(teamid=teamid).all()
|
||||||
db.session.close()
|
db.session.close()
|
||||||
json = {'solves':[]}
|
json_data = {'solves':[]}
|
||||||
for x in solves:
|
for x in solves:
|
||||||
json['solves'].append({'id':x.id, 'chal':x.chal.name, 'chalid':x.chalid,'team':x.teamid, 'value': x.chal.value, 'category':x.chal.category, 'time':unix_time(x.date)})
|
json_data['solves'].append({'id':x.id, 'chal':x.chal.name, 'chalid':x.chalid,'team':x.teamid, 'value': x.chal.value, 'category':x.chal.category, 'time':unix_time(x.date)})
|
||||||
return jsonify(json)
|
return jsonify(json_data)
|
||||||
|
|
||||||
|
|
||||||
@admin.route('/admin/solves/<teamid>/<chalid>/delete', methods=['POST'])
|
@admin.route('/admin/solves/<teamid>/<chalid>/delete', methods=['POST'])
|
||||||
@@ -531,14 +545,14 @@ def admin_fails(teamid='all'):
|
|||||||
fails = WrongKeys.query.count()
|
fails = WrongKeys.query.count()
|
||||||
solves = Solves.query.count()
|
solves = Solves.query.count()
|
||||||
db.session.close()
|
db.session.close()
|
||||||
json = {'fails':str(fails), 'solves': str(solves)}
|
json_data = {'fails':str(fails), 'solves': str(solves)}
|
||||||
return jsonify(json)
|
return jsonify(json_data)
|
||||||
else:
|
else:
|
||||||
fails = WrongKeys.query.filter_by(team=teamid).count()
|
fails = WrongKeys.query.filter_by(team=teamid).count()
|
||||||
solves = Solves.query.filter_by(teamid=teamid).count()
|
solves = Solves.query.filter_by(teamid=teamid).count()
|
||||||
db.session.close()
|
db.session.close()
|
||||||
json = {'fails':str(fails), 'solves': str(solves)}
|
json_data = {'fails':str(fails), 'solves': str(solves)}
|
||||||
return jsonify(json)
|
return jsonify(json_data)
|
||||||
|
|
||||||
|
|
||||||
@admin.route('/admin/chal/new', methods=['POST'])
|
@admin.route('/admin/chal/new', methods=['POST'])
|
||||||
@@ -546,14 +560,12 @@ def admin_fails(teamid='all'):
|
|||||||
def admin_create_chal():
|
def admin_create_chal():
|
||||||
files = request.files.getlist('files[]')
|
files = request.files.getlist('files[]')
|
||||||
|
|
||||||
# Create challenge
|
## TODO: Expand to support multiple flags
|
||||||
chal = Challenges(request.form['name'], request.form['desc'], request.form['value'], request.form['category'])
|
flags = [{'flag':request.form['key'], 'type':int(request.form['key_type[0]'])}]
|
||||||
db.session.add(chal)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
# Add keys
|
# Create challenge
|
||||||
key = Keys(chal.id, request.form['key'], request.form['key_type[0]'])
|
chal = Challenges(request.form['name'], request.form['desc'], request.form['value'], request.form['category'], flags)
|
||||||
db.session.add(key)
|
db.session.add(chal)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
for f in files:
|
for f in files:
|
||||||
@@ -562,7 +574,7 @@ def admin_create_chal():
|
|||||||
if len(filename) <= 0:
|
if len(filename) <= 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
md5hash = hashlib.md5(filename).hexdigest()
|
md5hash = hashlib.md5(os.urandom(64)).hexdigest()
|
||||||
|
|
||||||
if not os.path.exists(os.path.join(os.path.normpath(app.static_folder), 'uploads', md5hash)):
|
if not os.path.exists(os.path.join(os.path.normpath(app.static_folder), 'uploads', md5hash)):
|
||||||
os.makedirs(os.path.join(os.path.normpath(app.static_folder), 'uploads', md5hash))
|
os.makedirs(os.path.join(os.path.normpath(app.static_folder), 'uploads', md5hash))
|
||||||
@@ -581,7 +593,7 @@ def admin_create_chal():
|
|||||||
def admin_delete_chal():
|
def admin_delete_chal():
|
||||||
challenge = Challenges.query.filter_by(id=request.form['id']).first()
|
challenge = Challenges.query.filter_by(id=request.form['id']).first()
|
||||||
if challenge:
|
if challenge:
|
||||||
WrongKeys.query.filter_by(chal=challenge.id).delete()
|
WrongKeys.query.filter_by(chalid=challenge.id).delete()
|
||||||
Solves.query.filter_by(chalid=challenge.id).delete()
|
Solves.query.filter_by(chalid=challenge.id).delete()
|
||||||
Keys.query.filter_by(chal=challenge.id).delete()
|
Keys.query.filter_by(chal=challenge.id).delete()
|
||||||
files = Files.query.filter_by(chal=challenge.id).all()
|
files = Files.query.filter_by(chal=challenge.id).all()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from CTFd.models import db, Challenges, Files, Solves, WrongKeys, Keys
|
|||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
challenges = Blueprint('challenges', __name__)
|
challenges = Blueprint('challenges', __name__)
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ def challenges_view():
|
|||||||
if can_view_challenges():
|
if can_view_challenges():
|
||||||
return render_template('chals.html', ctftime=ctftime())
|
return render_template('chals.html', ctftime=ctftime())
|
||||||
else:
|
else:
|
||||||
return redirect(url_for('login', next="challenges"))
|
return redirect('/login')
|
||||||
|
|
||||||
|
|
||||||
@challenges.route('/chals', methods=['GET'])
|
@challenges.route('/chals', methods=['GET'])
|
||||||
@@ -55,7 +56,7 @@ def chals_per_solves():
|
|||||||
for chal, count in solves:
|
for chal, count in solves:
|
||||||
json[chal.chal.name] = count
|
json[chal.chal.name] = count
|
||||||
return jsonify(json)
|
return jsonify(json)
|
||||||
return redirect(url_for('login', next="/chals/solves"))
|
return redirect('/login')
|
||||||
|
|
||||||
|
|
||||||
@challenges.route('/solves')
|
@challenges.route('/solves')
|
||||||
@@ -80,7 +81,7 @@ def attempts():
|
|||||||
chals = Challenges.query.add_columns('id').all()
|
chals = Challenges.query.add_columns('id').all()
|
||||||
json = {'maxattempts':[]}
|
json = {'maxattempts':[]}
|
||||||
for chal, chalid in chals:
|
for chal, chalid in chals:
|
||||||
fails = WrongKeys.query.filter_by(team=session['id'], chal=chalid).count()
|
fails = WrongKeys.query.filter_by(team=session['id'], chalid=chalid).count()
|
||||||
if fails >= int(get_config("max_tries")) and int(get_config("max_tries")) > 0:
|
if fails >= int(get_config("max_tries")) and int(get_config("max_tries")) > 0:
|
||||||
json['maxattempts'].append({'chalid':chalid})
|
json['maxattempts'].append({'chalid':chalid})
|
||||||
return jsonify(json)
|
return jsonify(json)
|
||||||
@@ -97,7 +98,7 @@ def fails(teamid):
|
|||||||
|
|
||||||
@challenges.route('/chal/<chalid>/solves', methods=['GET'])
|
@challenges.route('/chal/<chalid>/solves', methods=['GET'])
|
||||||
def who_solved(chalid):
|
def who_solved(chalid):
|
||||||
solves = Solves.query.filter_by(chalid=chalid)
|
solves = Solves.query.filter_by(chalid=chalid).order_by(Solves.date.asc())
|
||||||
json = {'teams':[]}
|
json = {'teams':[]}
|
||||||
for solve in solves:
|
for solve in solves:
|
||||||
json['teams'].append({'id':solve.team.id, 'name':solve.team.name, 'date':solve.date})
|
json['teams'].append({'id':solve.team.id, 'name':solve.team.name, 'date':solve.date})
|
||||||
@@ -109,12 +110,16 @@ def chal(chalid):
|
|||||||
if not ctftime():
|
if not ctftime():
|
||||||
return redirect('/challenges')
|
return redirect('/challenges')
|
||||||
if authed():
|
if authed():
|
||||||
fails = WrongKeys.query.filter_by(team=session['id'],chal=chalid).count()
|
fails = WrongKeys.query.filter_by(team=session['id'], chalid=chalid).count()
|
||||||
logger = logging.getLogger('keys')
|
logger = logging.getLogger('keys')
|
||||||
data = (time.strftime("%m/%d/%Y %X"), session['username'].encode('utf-8'), request.form['key'].encode('utf-8'), get_kpm(session['id']))
|
data = (time.strftime("%m/%d/%Y %X"), session['username'].encode('utf-8'), request.form['key'].encode('utf-8'), get_kpm(session['id']))
|
||||||
print "[{0}] {1} submitted {2} with kpm {3}".format(*data)
|
print("[{0}] {1} submitted {2} with kpm {3}".format(*data))
|
||||||
|
|
||||||
|
# Hit max attempts
|
||||||
if fails >= int(get_config("max_tries")) and int(get_config("max_tries")) > 0:
|
if fails >= int(get_config("max_tries")) and int(get_config("max_tries")) > 0:
|
||||||
return "4" #too many tries on this challenge
|
return "4" #too many tries on this challenge
|
||||||
|
|
||||||
|
# Anti-bruteforce / submitting keys too quickly
|
||||||
if get_kpm(session['id']) > 10:
|
if get_kpm(session['id']) > 10:
|
||||||
wrong = WrongKeys(session['id'], chalid, request.form['key'])
|
wrong = WrongKeys(session['id'], chalid, request.form['key'])
|
||||||
db.session.add(wrong)
|
db.session.add(wrong)
|
||||||
@@ -122,21 +127,26 @@ def chal(chalid):
|
|||||||
db.session.close()
|
db.session.close()
|
||||||
logger.warn("[{0}] {1} submitted {2} with kpm {3} [TOO FAST]".format(*data))
|
logger.warn("[{0}] {1} submitted {2} with kpm {3} [TOO FAST]".format(*data))
|
||||||
return "3" # Submitting too fast
|
return "3" # Submitting too fast
|
||||||
|
|
||||||
solves = Solves.query.filter_by(teamid=session['id'], chalid=chalid).first()
|
solves = Solves.query.filter_by(teamid=session['id'], chalid=chalid).first()
|
||||||
|
|
||||||
|
# Challange not solved yet
|
||||||
if not solves:
|
if not solves:
|
||||||
keys = Keys.query.filter_by(chal=chalid).all()
|
chal = Challenges.query.filter_by(id=chalid).first()
|
||||||
key = request.form['key'].strip().lower()
|
key = str(request.form['key'].strip().lower())
|
||||||
|
keys = json.loads(chal.flags)
|
||||||
for x in keys:
|
for x in keys:
|
||||||
if x.key_type == 0: #static key
|
if x['type'] == 0: #static key
|
||||||
if x.flag.strip().lower() == key:
|
print(x['flag'], key.strip().lower())
|
||||||
|
if x['flag'] == key.strip().lower():
|
||||||
solve = Solves(chalid=chalid, teamid=session['id'], ip=request.remote_addr, flag=key)
|
solve = Solves(chalid=chalid, teamid=session['id'], ip=request.remote_addr, flag=key)
|
||||||
db.session.add(solve)
|
db.session.add(solve)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db.session.close()
|
db.session.close()
|
||||||
logger.info("[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format(*data))
|
logger.info("[{0}] {1} submitted {2} with kpm {3} [CORRECT]".format(*data))
|
||||||
return "1" # key was correct
|
return "1" # key was correct
|
||||||
elif x.key_type == 1: #regex
|
elif x['type'] == 1: #regex
|
||||||
res = re.match(str(x), key, re.IGNORECASE)
|
res = re.match(str(x['flag']), key, re.IGNORECASE)
|
||||||
if res and res.group() == key:
|
if res and res.group() == key:
|
||||||
solve = Solves(chalid=chalid, teamid=session['id'], ip=request.remote_addr, flag=key)
|
solve = Solves(chalid=chalid, teamid=session['id'], ip=request.remote_addr, flag=key)
|
||||||
db.session.add(solve)
|
db.session.add(solve)
|
||||||
@@ -151,6 +161,8 @@ def chal(chalid):
|
|||||||
db.session.close()
|
db.session.close()
|
||||||
logger.info("[{0}] {1} submitted {2} with kpm {3} [WRONG]".format(*data))
|
logger.info("[{0}] {1} submitted {2} with kpm {3} [WRONG]".format(*data))
|
||||||
return '0' # key was wrong
|
return '0' # key was wrong
|
||||||
|
|
||||||
|
# Challenge already solved
|
||||||
else:
|
else:
|
||||||
logger.info("{0} submitted {1} with kpm {2} [ALREADY SOLVED]".format(*data))
|
logger.info("{0} submitted {1} with kpm {2} [ALREADY SOLVED]".format(*data))
|
||||||
return "2" # challenge was already solved
|
return "2" # challenge was already solved
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
##### SERVER SETTINGS #####
|
##### SERVER SETTINGS #####
|
||||||
SECRET_KEY = os.urandom(64)
|
SECRET_KEY = os.urandom(64)
|
||||||
|
#SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:password@localhost/ctfd'
|
||||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///ctfd.db'
|
SQLALCHEMY_DATABASE_URI = 'sqlite:///ctfd.db'
|
||||||
SESSION_TYPE = "filesystem"
|
SESSION_TYPE = "filesystem"
|
||||||
SESSION_FILE_DIR = "/tmp/flask_session"
|
SESSION_FILE_DIR = "/tmp/flask_session"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ from passlib.hash import bcrypt_sha256
|
|||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import json
|
||||||
|
|
||||||
def sha512(string):
|
def sha512(string):
|
||||||
return hashlib.sha512(string).hexdigest()
|
return hashlib.sha512(string).hexdigest()
|
||||||
@@ -37,13 +38,15 @@ class Challenges(db.Model):
|
|||||||
description = db.Column(db.Text)
|
description = db.Column(db.Text)
|
||||||
value = db.Column(db.Integer)
|
value = db.Column(db.Integer)
|
||||||
category = db.Column(db.String(80))
|
category = db.Column(db.String(80))
|
||||||
# add open category
|
flags = db.Column(db.Text)
|
||||||
|
hidden = db.Column(db.Boolean)
|
||||||
|
|
||||||
def __init__(self, name, description, value, category):
|
def __init__(self, name, description, value, category, flags):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.description = description
|
self.description = description
|
||||||
self.value = value
|
self.value = value
|
||||||
self.category = category
|
self.category = category
|
||||||
|
self.flags = json.dumps(flags)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<chal %r>' % self.name
|
return '<chal %r>' % self.name
|
||||||
@@ -102,7 +105,7 @@ class Teams(db.Model):
|
|||||||
def __init__(self, name, email, password):
|
def __init__(self, name, email, password):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.email = email
|
self.email = email
|
||||||
self.password = bcrypt_sha256.encrypt(password)
|
self.password = bcrypt_sha256.encrypt(str(password))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<team %r>' % self.name
|
return '<team %r>' % self.name
|
||||||
@@ -152,14 +155,15 @@ class Solves(db.Model):
|
|||||||
|
|
||||||
class WrongKeys(db.Model):
|
class WrongKeys(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
chal = db.Column(db.Integer, db.ForeignKey('challenges.id'))
|
chalid = db.Column(db.Integer, db.ForeignKey('challenges.id'))
|
||||||
team = db.Column(db.Integer, db.ForeignKey('teams.id'))
|
team = db.Column(db.Integer, db.ForeignKey('teams.id'))
|
||||||
date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
date = db.Column(db.DateTime, default=datetime.datetime.utcnow)
|
||||||
flag = db.Column(db.Text)
|
flag = db.Column(db.Text)
|
||||||
|
chal = db.relationship('Challenges', foreign_keys="WrongKeys.chalid", lazy='joined')
|
||||||
|
|
||||||
def __init__(self, team, chal, flag):
|
def __init__(self, team, chalid, flag):
|
||||||
self.team = team
|
self.team = team
|
||||||
self.chal = chal
|
self.chalid = chalid
|
||||||
self.flag = flag
|
self.flag = flag
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ table{
|
|||||||
|
|
||||||
#score-graph{
|
#score-graph{
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
|
clear:both;
|
||||||
}
|
}
|
||||||
|
|
||||||
#keys-pie-graph{
|
#keys-pie-graph{
|
||||||
@@ -81,4 +82,26 @@ table{
|
|||||||
width: 600px;
|
width: 600px;
|
||||||
float: left;
|
float: left;
|
||||||
max-height: 330px;
|
max-height: 330px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chal-button{
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chal-button > p{
|
||||||
|
font-family: monospace;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
height: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chal-button > span{
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-key{
|
||||||
|
float:right;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,14 @@
|
|||||||
|
//http://stackoverflow.com/a/2648463 - wizardry!
|
||||||
|
String.prototype.format = String.prototype.f = function() {
|
||||||
|
var s = this,
|
||||||
|
i = arguments.length;
|
||||||
|
|
||||||
|
while (i--) {
|
||||||
|
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
//http://stackoverflow.com/a/7616484
|
//http://stackoverflow.com/a/7616484
|
||||||
String.prototype.hashCode = function() {
|
String.prototype.hashCode = function() {
|
||||||
var hash = 0, i, chr, len;
|
var hash = 0, i, chr, len;
|
||||||
@@ -16,8 +27,9 @@ function loadchal(id) {
|
|||||||
obj = $.grep(challenges['game'], function (e) {
|
obj = $.grep(challenges['game'], function (e) {
|
||||||
return e.id == id;
|
return e.id == id;
|
||||||
})[0]
|
})[0]
|
||||||
|
$('.chal-title').text(obj.name);
|
||||||
$('.chal-name').val(obj.name);
|
$('.chal-name').val(obj.name);
|
||||||
$('.chal-desc').html(obj.description);
|
$('.chal-desc').val(obj.description);
|
||||||
$('.chal-value').val(obj.value);
|
$('.chal-value').val(obj.value);
|
||||||
$('.chal-category').val(obj.category);
|
$('.chal-category').val(obj.category);
|
||||||
$('.chal-id').val(obj.id);
|
$('.chal-id').val(obj.id);
|
||||||
@@ -44,9 +56,13 @@ function loadkeys(chal){
|
|||||||
keys = keys['keys'];
|
keys = keys['keys'];
|
||||||
$('#current-keys').empty();
|
$('#current-keys').empty();
|
||||||
for(x=0; x<keys.length; x++){
|
for(x=0; x<keys.length; x++){
|
||||||
$('#current-keys').append($("<input class='current-key' type='text'>").val(keys[x].key));
|
var elem = $('<div>');
|
||||||
$('#current-keys').append('<input type="radio" name="key_type['+x+']" value="0">Static');
|
elem.append($("<input class='current-key' type='text'>").val(keys[x].key));
|
||||||
$('#current-keys').append('<input type="radio" name="key_type['+x+']" value="1">Regex');
|
elem.append('<input type="radio" name="key_type['+x+']" value="0">Static');
|
||||||
|
elem.append('<input type="radio" name="key_type['+x+']" value="1">Regex');
|
||||||
|
elem.append('<a href="#" onclick="$(this).parent().remove()" class="remove-key">Remove</a>');
|
||||||
|
|
||||||
|
$('#current-keys').append(elem);
|
||||||
$('#current-keys input[name="key_type['+x+']"][value="'+keys[x].type+'"]').prop('checked',true);
|
$('#current-keys input[name="key_type['+x+']"][value="'+keys[x].type+'"]').prop('checked',true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -59,7 +75,7 @@ function updatekeys(){
|
|||||||
$('.current-key').each(function(){
|
$('.current-key').each(function(){
|
||||||
keys.push($(this).val());
|
keys.push($(this).val());
|
||||||
})
|
})
|
||||||
$('#current-keys > input[name*="key_type"]:checked').each(function(){
|
$('#current-keys input[name*="key_type"]:checked').each(function(){
|
||||||
vals.push($(this).val());
|
vals.push($(this).val());
|
||||||
})
|
})
|
||||||
$.post('/admin/keys/'+chal, {'keys':keys, 'vals':vals, 'nonce': $('#nonce').val()})
|
$.post('/admin/keys/'+chal, {'keys':keys, 'vals':vals, 'nonce': $('#nonce').val()})
|
||||||
@@ -146,13 +162,10 @@ function loadchals(){
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i = categories.length - 1; i >= 0; i--) {
|
|
||||||
$('#new-challenge select').append('<option value="' + categories[i] + '">' + categories[i] + '</option>');
|
|
||||||
$('#update-challenge select').append('<option value="' + categories[i] + '">' + categories[i] + '</option>');
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i = 0; i <= challenges['game'].length - 1; i++) {
|
for (var i = 0; i <= challenges['game'].length - 1; i++) {
|
||||||
$('#' + challenges['game'][i].category.replace(/ /g,"-").hashCode()).append($('<button class="radius" value="' + challenges['game'][i].id + '">' + challenges['game'][i].value + '</button>'));
|
var chal = challenges['game'][i]
|
||||||
|
var chal_button = $('<button class="chal-button" value="{0}"><p>{1}</p><span>{2}</span></button>'.format(chal.id, chal.name, chal.value))
|
||||||
|
$('#' + challenges['game'][i].category.replace(/ /g,"-").hashCode()).append(chal_button);
|
||||||
};
|
};
|
||||||
|
|
||||||
$('#challenges button').click(function (e) {
|
$('#challenges button').click(function (e) {
|
||||||
@@ -162,8 +175,6 @@ function loadchals(){
|
|||||||
loadfiles(this.value);
|
loadfiles(this.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('tr').append('<button class="radius create-challenge"><i class="fa fa-plus"></i></button>');
|
|
||||||
|
|
||||||
$('.create-challenge').click(function (e) {
|
$('.create-challenge').click(function (e) {
|
||||||
$('#new-chal-category').val($($(this).siblings()[0]).text().trim())
|
$('#new-chal-category').val($($(this).siblings()[0]).text().trim())
|
||||||
$('#new-chal-title').text($($(this).siblings()[0]).text().trim())
|
$('#new-chal-title').text($($(this).siblings()[0]).text().trim())
|
||||||
@@ -177,9 +188,7 @@ $('#submit-key').click(function (e) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
$('#submit-keys').click(function (e) {
|
$('#submit-keys').click(function (e) {
|
||||||
if (confirm('Updating keys. Are you sure?')){
|
updatekeys()
|
||||||
updatekeys()
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#submit-tags').click(function (e) {
|
$('#submit-tags').click(function (e) {
|
||||||
@@ -212,15 +221,38 @@ $(".tag-insert").keyup(function (e) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.create-category').click(function (e) {
|
|
||||||
$('#new-category').foundation('reveal', 'open');
|
|
||||||
|
// Markdown Preview
|
||||||
|
$('#desc-edit').on('toggled', function (event, tab) {
|
||||||
|
if (tab[0].id == 'desc-preview'){
|
||||||
|
$(tab[0]).html(marked($('#desc-editor').val(), {'gfm':true, 'breaks':true}))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
count = 1;
|
$('#new-desc-edit').on('toggled', function (event, tab) {
|
||||||
$('#create-key').click(function (e) {
|
if (tab[0].id == 'new-desc-preview'){
|
||||||
$('#current-keys').append("<input class='current-key' type='text' placeholder='Blank Key'>");
|
$(tab[0]).html(marked($('#new-desc-editor').val(), {'gfm':true, 'breaks':true}))
|
||||||
$('#current-keys').append('<input type="radio" name="key_type['+count+']" value="0">Static');
|
}
|
||||||
$('#current-keys').append('<input type="radio" name="key_type['+count+']" value="1">Regex');
|
});
|
||||||
count++;
|
|
||||||
|
// Open New Challenge modal when New Challenge button is clicked
|
||||||
|
$('.create-challenge').click(function (e) {
|
||||||
|
$('#create-challenge').foundation('reveal', 'open');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$('#create-key').click(function(e){
|
||||||
|
var amt = $('#current-keys input[type=text]').length
|
||||||
|
// $('#current-keys').append("<input class='current-key' type='text' placeholder='Blank Key'>");
|
||||||
|
// $('#current-keys').append('<input type="radio" name="key_type[{0}]" value="0">Static'.format(amt));
|
||||||
|
// $('#current-keys').append('<input type="radio" name="key_type[{0}]" value="1">Regex'.format(amt));
|
||||||
|
|
||||||
|
var elem = $('<div>');
|
||||||
|
elem.append("<input class='current-key' type='text' placeholder='Blank Key'>");
|
||||||
|
elem.append('<input type="radio" name="key_type[{0}]" value="0">Static'.format(amt));
|
||||||
|
elem.append('<input type="radio" name="key_type[{0}]" value="1">Regex'.format(amt));
|
||||||
|
elem.append('<a href="#" onclick="$(this).parent().remove()" class="remove-key">Remove</a>');
|
||||||
|
$('#current-keys').append(elem);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(function(){
|
$(function(){
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ table{
|
|||||||
|
|
||||||
#score-graph{
|
#score-graph{
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
|
clear:both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown{
|
.dropdown{
|
||||||
@@ -91,6 +92,24 @@ table{
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chal-button{
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chal-button > p{
|
||||||
|
font-family: monospace;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
height: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chal-button > span{
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (min-width: 40.063em){
|
@media only screen and (min-width: 40.063em){
|
||||||
.top-bar .dropdown{
|
.top-bar .dropdown{
|
||||||
display: block;
|
display: block;
|
||||||
@@ -103,3 +122,8 @@ table{
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.scroll-wrap{
|
||||||
|
height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|||||||
@@ -206,7 +206,9 @@ function loadchals() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (var i = 0; i <= challenges['game'].length - 1; i++) {
|
for (var i = 0; i <= challenges['game'].length - 1; i++) {
|
||||||
$('#' + challenges['game'][i].category.replace(/ /g,"-").hashCode()).append($('<button value="' + challenges['game'][i].id + '">' + challenges['game'][i].value + '</button>'));
|
var chal = challenges['game'][i]
|
||||||
|
var chal_button = $('<button class="chal-button" value="{0}"><p>{1}</p><span>{2}</span></button>'.format(chal.id, chal.name, chal.value))
|
||||||
|
$('#' + challenges['game'][i].category.replace(/ /g,"-").hashCode()).append(chal_button);
|
||||||
};
|
};
|
||||||
updatesolves()
|
updatesolves()
|
||||||
marktoomanyattempts()
|
marktoomanyattempts()
|
||||||
|
|||||||
@@ -9,6 +9,16 @@ String.prototype.format = String.prototype.f = function() {
|
|||||||
return s;
|
return s;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function colorhash (x) {
|
||||||
|
color = ""
|
||||||
|
for (var i = 20; i <= 60; i+=20){
|
||||||
|
x += i
|
||||||
|
x *= i
|
||||||
|
color += x.toString(16)
|
||||||
|
};
|
||||||
|
return "#" + color.substring(0, 6)
|
||||||
|
}
|
||||||
|
|
||||||
function htmlentities(string) {
|
function htmlentities(string) {
|
||||||
return $('<div/>').text(string).html();
|
return $('<div/>').text(string).html();
|
||||||
}
|
}
|
||||||
@@ -37,7 +47,6 @@ function UTCtoDate(utc){
|
|||||||
d.setUTCSeconds(utc)
|
d.setUTCSeconds(utc)
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
function scoregraph () {
|
function scoregraph () {
|
||||||
var times = []
|
var times = []
|
||||||
var scores = []
|
var scores = []
|
||||||
@@ -52,12 +61,15 @@ function scoregraph () {
|
|||||||
|
|
||||||
xs_data = {}
|
xs_data = {}
|
||||||
column_data = []
|
column_data = []
|
||||||
|
colors = {}
|
||||||
for (var i = 0; i < teams.length; i++) {
|
for (var i = 0; i < teams.length; i++) {
|
||||||
times = []
|
times = []
|
||||||
team_scores = []
|
team_scores = []
|
||||||
for (var x = 0; x < scores[teams[i]].length; x++) {
|
for (var x = 0; x < scores[teams[i]].length; x++) {
|
||||||
times.push(scores[teams[i]][x].time)
|
times.push(scores[teams[i]][x].time)
|
||||||
team_scores.push(scores[teams[i]][x].value)
|
team_scores.push(scores[teams[i]][x].value)
|
||||||
|
console.log(scores[teams[i]])
|
||||||
|
colors[teams[i]] = colorhash(scores[teams[i]][x].id)
|
||||||
};
|
};
|
||||||
team_scores = cumulativesum(team_scores)
|
team_scores = cumulativesum(team_scores)
|
||||||
|
|
||||||
@@ -79,7 +91,8 @@ function scoregraph () {
|
|||||||
data: {
|
data: {
|
||||||
xs: xs_data,
|
xs: xs_data,
|
||||||
columns: column_data,
|
columns: column_data,
|
||||||
type: "step"
|
type: "step",
|
||||||
|
colors: colors
|
||||||
// labels: true
|
// labels: true
|
||||||
},
|
},
|
||||||
axis : {
|
axis : {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
function teamid (){
|
function teamid (){
|
||||||
loc = window.location.pathname
|
loc = window.location.pathname
|
||||||
return loc.substring(loc.lastIndexOf('/')+1, loc.length);
|
return parseInt(loc.substring(loc.lastIndexOf('/')+1, loc.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
function colorhash (x) {
|
function colorhash (x) {
|
||||||
@@ -165,4 +165,4 @@ function category_breakdown_graph(){
|
|||||||
|
|
||||||
category_breakdown_graph()
|
category_breakdown_graph()
|
||||||
keys_percentage_graph()
|
keys_percentage_graph()
|
||||||
scoregraph()
|
scoregraph()
|
||||||
|
|||||||
@@ -46,7 +46,7 @@
|
|||||||
{% block content %} {% endblock %}
|
{% block content %} {% endblock %}
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/foundation/5.4.7/js/vendor/jquery.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/foundation/5.4.7/js/vendor/jquery.js"></script>
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/foundation/5.4.7/js/vendor/modernizr.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/foundation/5.4.7/js/vendor/modernizr.js"></script>
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/pagedown/1.0/Markdown.Converter.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/marked/0.3.2/marked.min.js"></script>
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/foundation/5.4.7/js/foundation.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/foundation/5.4.7/js/foundation.min.js"></script>
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/foundation/5.4.7/js/foundation/foundation.topbar.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/foundation/5.4.7/js/foundation/foundation.topbar.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -55,4 +55,4 @@
|
|||||||
{% block scripts %} {% endblock %}
|
{% block scripts %} {% endblock %}
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -2,47 +2,31 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<input type="hidden" value="{{ nonce }}" id="nonce">
|
<input type="hidden" value="{{ nonce }}" id="nonce">
|
||||||
<div id="new-category" class="reveal-modal" data-reveal>
|
|
||||||
<form method="POST" action="/admin/chal/new" enctype="multipart/form-data">
|
|
||||||
<h3>New Category</h3>
|
|
||||||
<input name='nonce' type='hidden' value="{{ nonce }}">
|
|
||||||
<input type='text' name='category' placeholder='Category'><br/>
|
|
||||||
<a href="#" data-reveal-id="create-challenge" class="button">Create</a>
|
|
||||||
<a class="close-reveal-modal">×</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="create-challenge" class="reveal-modal" data-reveal>
|
<div id="create-challenge" class="reveal-modal" data-reveal>
|
||||||
<h3>New Challenge</h3>
|
<h3>New Challenge</h3>
|
||||||
<input type='text' name='name' placeholder='Name'><br/>
|
|
||||||
<textarea name='desc' placeholder='Description'></textarea><br/>
|
|
||||||
<input type='number' name='value' placeholder='Value'><br/>
|
|
||||||
<input type='text' name='key' placeholder='Key'><br/>
|
|
||||||
<input type="radio" name="key_type[0]" value="0">Static
|
|
||||||
<input type="radio" name="key_type[0]" value="1">Regex
|
|
||||||
<br/>
|
|
||||||
<input type="file" name="files[]" multiple="multiple">
|
|
||||||
|
|
||||||
<button type='submit'>Create</button>
|
|
||||||
<a class="close-reveal-modal">×</a>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="new-challenge" class="reveal-modal" data-reveal>
|
|
||||||
<form method="POST" action="/admin/chal/new" enctype="multipart/form-data">
|
<form method="POST" action="/admin/chal/new" enctype="multipart/form-data">
|
||||||
<h3><span id="new-chal-title"></span> challenge</h3>
|
|
||||||
<input name='nonce' type='hidden' value="{{ nonce }}">
|
|
||||||
<input type='text' name='name' placeholder='Name'><br/>
|
<input type='text' name='name' placeholder='Name'><br/>
|
||||||
<textarea class="textbox" name='desc' placeholder='Description'></textarea><br/>
|
<input type='text' name='category' placeholder='Category'><br/>
|
||||||
<input type='number' name='value' placeholder='Value'><br/>
|
|
||||||
<input id="new-chal-category" type="hidden" name="category">
|
|
||||||
<input type='text' name='key' placeholder='Key'><br/>
|
|
||||||
|
|
||||||
<input type="file" name="files[]" multiple="multiple">
|
<ul id="new-desc-edit" class="tabs" data-tab>
|
||||||
|
<li class="tab-title active"><a href="#new-desc-write" style="padding: 0 2rem;">Write</a></li>
|
||||||
<br/>
|
<li class="tab-title"><a href="#new-desc-preview" style="padding: 0 2rem;">Preview</a></li>
|
||||||
|
</ul>
|
||||||
|
<div class="tabs-content">
|
||||||
|
<div class="content active" id="new-desc-write">
|
||||||
|
<textarea id="new-desc-editor" class="new-chal-desc" name='desc' placeholder='Description' rows="10"></textarea><br>
|
||||||
|
</div>
|
||||||
|
<div class="content" id="new-desc-preview">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type='number' name='value' placeholder='Value'><br/>
|
||||||
|
<input type='text' name='key' placeholder='Key'><br/>
|
||||||
<input type="radio" name="key_type[0]" value="0">Static
|
<input type="radio" name="key_type[0]" value="0">Static
|
||||||
<input type="radio" name="key_type[0]" value="1">Regex
|
<input type="radio" name="key_type[0]" value="1">Regex
|
||||||
<br/>
|
<br/>
|
||||||
|
<input type="file" name="files[]" multiple="multiple">
|
||||||
|
|
||||||
<button type='submit'>Create</button>
|
<button type='submit'>Create</button>
|
||||||
<a class="close-reveal-modal">×</a>
|
<a class="close-reveal-modal">×</a>
|
||||||
@@ -111,15 +95,26 @@
|
|||||||
|
|
||||||
<div id="update-challenge" class="reveal-modal" data-reveal>
|
<div id="update-challenge" class="reveal-modal" data-reveal>
|
||||||
<form method="POST" action="/admin/chal/update">
|
<form method="POST" action="/admin/chal/update">
|
||||||
<h3>Update Challenge</h3>
|
<h3 class="chal-title text-center"></h3>
|
||||||
<input name='nonce' type='hidden' value="{{ nonce }}">
|
<input name='nonce' type='hidden' value="{{ nonce }}">
|
||||||
<input class="chal-name" type='text' name='name' placeholder='Name'><br/>
|
Name: <input class="chal-name" type='text' name='name' placeholder='Name'><br>
|
||||||
<textarea class="chal-desc textbox" name='desc' placeholder='Description'></textarea><br/>
|
Category: <input class="chal-category" type="text" name="category" placeholder="Category"><br>
|
||||||
<input class="chal-value" type='number' name='value' placeholder='Value'><br/>
|
Description:
|
||||||
<select class="chal-category" name="category">
|
|
||||||
<option>-</option>
|
<ul id="desc-edit" class="tabs" data-tab>
|
||||||
</select>
|
<li class="tab-title active"><a href="#desc-write" style="padding: 0 2rem;">Write</a></li>
|
||||||
<input class="chal-id" type='hidden' name='id' placeholder='ID'><br/>
|
<li class="tab-title"><a href="#desc-preview" style="padding: 0 2rem;">Preview</a></li>
|
||||||
|
</ul>
|
||||||
|
<div class="tabs-content">
|
||||||
|
<div class="content active" id="desc-write">
|
||||||
|
<textarea id="desc-editor" class="chal-desc" name='desc' placeholder='Description' rows="10"></textarea><br>
|
||||||
|
</div>
|
||||||
|
<div class="content" id="desc-preview">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Value: <input class="chal-value" type='number' name='value' placeholder='Value'><br>
|
||||||
|
<input class="chal-id" type='hidden' name='id' placeholder='ID'>
|
||||||
|
|
||||||
<a href="#" data-reveal-id="update-tags" class="secondary button">Tags</a>
|
<a href="#" data-reveal-id="update-tags" class="secondary button">Tags</a>
|
||||||
<a href="#" data-reveal-id="update-files" class="secondary button">Files</a>
|
<a href="#" data-reveal-id="update-files" class="secondary button">Files</a>
|
||||||
@@ -130,14 +125,13 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
<div class="row">
|
<h1 class="text-center">Challenges</h1>
|
||||||
<h1>CTF</h1>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<table id='challenges'>
|
<table id='challenges'>
|
||||||
</table>
|
</table>
|
||||||
<button style="width:100%;" class="radius create-category">New Category</button>
|
<button style="width:100%;" class="radius create-challenge">New Challenge</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<table id="scoreboard">
|
<table id="scoreboard">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>Place</b></td>
|
<td width="10px"><b>Place</b></td>
|
||||||
<td><b>Team</b></td>
|
<td><b>Team</b></td>
|
||||||
<td><b>Score</b></td>
|
<td><b>Score</b></td>
|
||||||
<td><b>Status</b></td>
|
<td><b>Status</b></td>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<h1 id="team-id">{{ team.name }}</h1>
|
<h1 id="team-id">{{ team.name }}</h1>
|
||||||
|
<h2 id="team-email" class="text-center">{{ team.email }}</h2>
|
||||||
<h2 id="team-place" class="text-center">
|
<h2 id="team-place" class="text-center">
|
||||||
{%if place %}
|
{%if place %}
|
||||||
{{ place }} <small>place</small>
|
{{ place }} <small>place</small>
|
||||||
@@ -19,11 +20,14 @@
|
|||||||
|
|
||||||
<div id="keys-pie-graph"></div>
|
<div id="keys-pie-graph"></div>
|
||||||
<div id="categories-pie-graph"></div>
|
<div id="categories-pie-graph"></div>
|
||||||
|
<div id="score-graph"></div>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
<h3>Solves</h3>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>Challenge</b></td>
|
<td><b>Challenge</b></td>
|
||||||
|
<td><b>Submitted</b></td>
|
||||||
<td><b>Category</b></td>
|
<td><b>Category</b></td>
|
||||||
<td><b>Value</b></td>
|
<td><b>Value</b></td>
|
||||||
<td><b>Time</b></td>
|
<td><b>Time</b></td>
|
||||||
@@ -31,12 +35,19 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for solve in solves %}
|
{% for solve in solves %}
|
||||||
<tr><td>{{ solve.chal.name }}</td><td>{{ solve.chal.category }}</td><td>{{ solve.chal.value }}</td><td class="solve-time">{{ solve.date|unix_time_millis }}</td></tr>
|
<tr>
|
||||||
|
<td>{{ solve.chal.name }}</td>
|
||||||
|
<td>{{ solve.flag }}</td>
|
||||||
|
<td>{{ solve.chal.category }}</td>
|
||||||
|
<td>{{ solve.chal.value }}</td>
|
||||||
|
<td class="solve-time">{{ solve.date|unix_time_millis }}</td>
|
||||||
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
<h3>IP Addresses</h3>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>IP Address</b></td>
|
<td><b>IP Address</b></td>
|
||||||
@@ -50,7 +61,25 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div id="score-graph"></div>
|
<table>
|
||||||
|
<h3>Wrong Keys</h3>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td><b>Challenge</b></td>
|
||||||
|
<td><b>Submitted</b></td>
|
||||||
|
<td><b>Time</b></td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for wrong_key in wrong_keys %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ wrong_key.chal.name }}</td>
|
||||||
|
<td>{{ wrong_key.flag }}</td>
|
||||||
|
<td class="solve-time">{{ wrong_key.date|unix_time_millis }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
<table id="teamsboard">
|
<table id="teamsboard">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="text-center"><b>ID</b>
|
<td width="10px" class="text-center"><b>ID</b>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-center"><b>Team</b>
|
<td class="text-center"><b>Team</b>
|
||||||
</td>
|
</td>
|
||||||
@@ -98,12 +98,12 @@
|
|||||||
{% for team in teams %}
|
{% for team in teams %}
|
||||||
<tr name="{{ team.id }}">
|
<tr name="{{ team.id }}">
|
||||||
<td class="team-id">{{ team.id }}</td>
|
<td class="team-id">{{ team.id }}</td>
|
||||||
<td class="team-name"><a href="/admin/team/{{ team.id }}">{{ team.name }}</a>
|
<td class="team-name"><a href="/admin/team/{{ team.id }}">{{ team.name | truncate(32) }}</a>
|
||||||
</td>
|
</td>
|
||||||
<td class="team-email">{{ team.email }}</td>
|
<td class="team-email">{{ team.email | truncate(32) }}</td>
|
||||||
<td class="team-website">{% if team.website and team.website.startswith('http') %}<a href="{{ team.website }}">{{ team.website }}</a>{% endif %}
|
<td class="team-website">{% if team.website and team.website.startswith('http') %}<a href="{{ team.website }}">{{ team.website | truncate(32) }}</a>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="team-affiliation"><span>{% if team.affiliation %}{{ team.affiliation }}{% endif %}</span>
|
<td class="team-affiliation"><span>{% if team.affiliation %}{{ team.affiliation | truncate(20) }}{% endif %}</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="team-country"><span>{% if team.country %}{{ team.country }}{% endif %}</span>
|
<td class="team-country"><span>{% if team.country %}{{ team.country }}{% endif %}</span>
|
||||||
</td>
|
</td>
|
||||||
@@ -122,6 +122,15 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{% if pages > 1 %}
|
||||||
|
<div class="text-center">Page
|
||||||
|
<br>
|
||||||
|
{% for page in range(1, pages + 1) %}
|
||||||
|
<a href="/admin/teams/{{ page }}">{{ page }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
<a href="">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -248,4 +257,4 @@ $('.fa-envelope').click(function(){
|
|||||||
load_email_modal(id);
|
load_email_modal(id);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -5,18 +5,20 @@
|
|||||||
|
|
||||||
<div id="chal-solves-window" class="reveal-modal" data-reveal>
|
<div id="chal-solves-window" class="reveal-modal" data-reveal>
|
||||||
<h3>Solved By</h3>
|
<h3>Solved By</h3>
|
||||||
<table>
|
<div class="scroll-wrap">
|
||||||
<thead>
|
<table>
|
||||||
<tr>
|
<thead>
|
||||||
<td><b>Name</b>
|
<tr>
|
||||||
</td>
|
<td><b>Name</b>
|
||||||
<td><b>Date</b>
|
</td>
|
||||||
</td>
|
<td><b>Date</b>
|
||||||
</tr>
|
</td>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody id="chal-solves-names">
|
</thead>
|
||||||
</tbody>
|
<tbody id="chal-solves-names">
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
<a class="close-reveal-modal" data-reveal-id="chal-window">×</a>
|
<a class="close-reveal-modal" data-reveal-id="chal-window">×</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -36,8 +38,9 @@
|
|||||||
<a class="close-reveal-modal">×</a>
|
<a class="close-reveal-modal">×</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div>
|
||||||
<h2 class="text-center">Challenges</h2>
|
<h1 class="text-center">Challenges</h1>
|
||||||
|
<br>
|
||||||
|
|
||||||
<div class="large-12 columns">
|
<div class="large-12 columns">
|
||||||
<table id='challenges'>
|
<table id='challenges'>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/c3/0.4.0/c3.min.css">
|
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/c3/0.4.0/c3.min.css">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<h1>Scoreboard</h1>
|
||||||
<br>
|
<br>
|
||||||
<div id="score-graph"></div>
|
<div id="score-graph"></div>
|
||||||
<br>
|
<br>
|
||||||
@@ -11,7 +12,7 @@
|
|||||||
<table id="scoreboard">
|
<table id="scoreboard">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td><b>Place</b>
|
<td width="10px"><b>Place</b>
|
||||||
</td>
|
</td>
|
||||||
<td><b>Team</b>
|
<td><b>Team</b>
|
||||||
</td>
|
</td>
|
||||||
@@ -33,4 +34,4 @@
|
|||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.min.js"></script>
|
||||||
<script src="//cdnjs.cloudflare.com/ajax/libs/c3/0.4.0/c3.min.js"></script>
|
<script src="//cdnjs.cloudflare.com/ajax/libs/c3/0.4.0/c3.min.js"></script>
|
||||||
<script src="/static/js/scoreboard.js"></script>
|
<script src="/static/js/scoreboard.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
<div id="keys-pie-graph"></div>
|
<div id="keys-pie-graph"></div>
|
||||||
<div id="categories-pie-graph"></div>
|
<div id="categories-pie-graph"></div>
|
||||||
|
<div id="score-graph"></div>
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -36,7 +37,6 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div id="score-graph"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/c3/0.4.0/c3.min.css">
|
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/c3/0.4.0/c3.min.css">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<h1>Teams</h1>
|
||||||
<br>
|
<br>
|
||||||
<table id="teamsboard">
|
<table id="teamsboard">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td width="10px"><b>ID</b></td>
|
||||||
<td><b>Team</b></td>
|
<td><b>Team</b></td>
|
||||||
<td><b>Website</b></td>
|
<td><b>Website</b></td>
|
||||||
<td><b>Affiliation</b></td>
|
<td><b>Affiliation</b></td>
|
||||||
@@ -17,7 +19,8 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for team in teams %}
|
{% for team in teams %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="/team/{{ team.id }}">{{ team.name }}</a></td>
|
<td class="team-id">{{ team.id }}</td>
|
||||||
|
<td><a href="/team/{{ team.id }}">{{ team.name | truncate(32) }}</a></td>
|
||||||
<td>{% if team.website and team.website.startswith('http') %}<a href="{{ team.website }}">{{ team.website }}</a>{% endif %}</td>
|
<td>{% if team.website and team.website.startswith('http') %}<a href="{{ team.website }}">{{ team.website }}</a>{% endif %}</td>
|
||||||
<td><span>{% if team.affiliation %}{{ team.affiliation }}{% endif %}</span></td>
|
<td><span>{% if team.affiliation %}{{ team.affiliation }}{% endif %}</span></td>
|
||||||
<td><span>{% if team.country %}{{ team.country }}{% endif %}</span></td>
|
<td><span>{% if team.country %}{{ team.country }}{% endif %}</span></td>
|
||||||
@@ -25,8 +28,17 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{% if team_pages > 1 %}
|
||||||
|
<div class="text-center">Page
|
||||||
|
<br>
|
||||||
|
{% for page in range(1, team_pages + 1) %}
|
||||||
|
<a href="/teams/{{ page }}">{{ page }}</a>
|
||||||
|
{% endfor %}
|
||||||
|
<a href="">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from CTFd.models import db, WrongKeys, Pages, Config
|
from CTFd.models import db, WrongKeys, Pages, Config
|
||||||
from CTFd import mail
|
from CTFd import mail
|
||||||
|
|
||||||
from urlparse import urlparse, urljoin
|
from six.moves.urllib.parse import urlparse, urljoin
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from flask import current_app as app, g, request, redirect, url_for, session, render_template
|
from flask import current_app as app, g, request, redirect, url_for, session, render_template
|
||||||
from flask.ext.mail import Message
|
from flask.ext.mail import Message
|
||||||
@@ -140,7 +140,7 @@ def admins_only(f):
|
|||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
if session.get('admin', None) is None:
|
if session.get('admin', None) is None:
|
||||||
return redirect(url_for('login', next=request.url))
|
return redirect('/login')
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
@@ -159,23 +159,27 @@ def ctftime():
|
|||||||
|
|
||||||
if start:
|
if start:
|
||||||
start = int(start)
|
start = int(start)
|
||||||
|
else:
|
||||||
|
start = 0
|
||||||
if end:
|
if end:
|
||||||
end = int(end)
|
end = int(end)
|
||||||
|
else:
|
||||||
|
end = 0
|
||||||
|
|
||||||
if start and end:
|
if start and end:
|
||||||
if start < time.time() < end:
|
if start < time.time() < end:
|
||||||
# Within the two time bounds
|
# Within the two time bounds
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if start < time.time() and end is None:
|
if start < time.time() and end == 0:
|
||||||
# CTF starts on a date but never ends
|
# CTF starts on a date but never ends
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if start is None and time.time() < end:
|
if start == 0 and time.time() < end:
|
||||||
# CTF started but ends at a date
|
# CTF started but ends at a date
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if start is None and end is None:
|
if start == 0 and end == 0:
|
||||||
# CTF has no time requirements
|
# CTF has no time requirements
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ def tracker():
|
|||||||
@views.before_request
|
@views.before_request
|
||||||
def csrf():
|
def csrf():
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
print(session)
|
||||||
|
print(request.form.get('nonce'))
|
||||||
if session['nonce'] != request.form.get('nonce'):
|
if session['nonce'] != request.form.get('nonce'):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
@@ -62,6 +64,7 @@ def setup():
|
|||||||
password = request.form['password']
|
password = request.form['password']
|
||||||
admin = Teams(name, email, password)
|
admin = Teams(name, email, password)
|
||||||
admin.admin = True
|
admin.admin = True
|
||||||
|
admin.banned = True
|
||||||
|
|
||||||
## Index page
|
## Index page
|
||||||
html = request.form['html']
|
html = request.form['html']
|
||||||
@@ -96,6 +99,7 @@ def setup():
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
app.setup = False
|
app.setup = False
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
|
print(session.get('nonce'))
|
||||||
return render_template('setup.html', nonce=session.get('nonce'))
|
return render_template('setup.html', nonce=session.get('nonce'))
|
||||||
return redirect('/')
|
return redirect('/')
|
||||||
|
|
||||||
@@ -120,11 +124,19 @@ def static_html(template):
|
|||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@views.route('/teams')
|
@views.route('/teams', defaults={'page':'1'})
|
||||||
def teams():
|
@views.route('/teams/<page>')
|
||||||
teams = Teams.query.all()
|
def teams(page):
|
||||||
return render_template('teams.html', teams=teams)
|
page = abs(int(page))
|
||||||
|
results_per_page = 50
|
||||||
|
page_start = results_per_page * ( page - 1 )
|
||||||
|
page_end = results_per_page * ( page - 1 ) + results_per_page
|
||||||
|
|
||||||
|
teams = Teams.query.slice(page_start, page_end).all()
|
||||||
|
count = db.session.query(db.func.count(Teams.id)).first()[0]
|
||||||
|
print(count)
|
||||||
|
pages = int(count / results_per_page) + (count % results_per_page > 0)
|
||||||
|
return render_template('teams.html', teams=teams, team_pages=pages)
|
||||||
|
|
||||||
@views.route('/team/<teamid>', methods=['GET', 'POST'])
|
@views.route('/team/<teamid>', methods=['GET', 'POST'])
|
||||||
def team(teamid):
|
def team(teamid):
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
APScheduler==3.0.1
|
APScheduler
|
||||||
Flask==0.10.1
|
Flask
|
||||||
Flask-Mail==0.9.1
|
Flask-Mail
|
||||||
Flask-SQLAlchemy==2.0
|
Flask-SQLAlchemy
|
||||||
Flask-Session==0.1.1
|
Flask-Session
|
||||||
SQLAlchemy==0.9.8
|
SQLAlchemy
|
||||||
passlib==1.6.2
|
passlib
|
||||||
bcrypt
|
bcrypt
|
||||||
six==1.8.0
|
six
|
||||||
itsdangerous==0.24
|
itsdangerous
|
||||||
requests==2.3.0
|
requests
|
||||||
|
PyMySQL
|
||||||
|
|||||||
Reference in New Issue
Block a user