Problem editor.
This commit is contained in:
parent
e9bf0be78b
commit
dae7aec24e
16 changed files with 276 additions and 327 deletions
|
@ -14,7 +14,7 @@ server {
|
|||
# }
|
||||
|
||||
# Put all the pages here so Angular doesn't fail.
|
||||
location ~^/(about|chat|help|learn|login|profile|register|scoreboard|settings|team|forgot)$ {
|
||||
location ~^/(about|chat|help|learn|login|logout|profile|register|scoreboard|settings|team|forgot)$ {
|
||||
default_type text/html;
|
||||
try_files /index.html /index.html;
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
"title": "Cancer",
|
||||
"hint": "No hint!",
|
||||
"category": "Miscellaneous",
|
||||
"autogen": false,
|
||||
"autogen": true,
|
||||
"programming": false,
|
||||
"value": 20,
|
||||
"value": 100,
|
||||
"bonus": 0,
|
||||
"threshold": 0,
|
||||
"weightmap": { }
|
||||
|
|
|
@ -5,4 +5,5 @@ SQLAlchemy
|
|||
gunicorn
|
||||
requests
|
||||
voluptuous
|
||||
PIL
|
||||
PIL
|
||||
markdown2
|
|
@ -3,7 +3,7 @@ from decorators import admins_only, api_wrapper
|
|||
from models import db, Problems, Files
|
||||
from schemas import verify_to_schema, check
|
||||
|
||||
import json
|
||||
import cPickle as pickle
|
||||
|
||||
blueprint = Blueprint("admin", __name__)
|
||||
|
||||
|
@ -16,14 +16,15 @@ def problem_data():
|
|||
for problem in problems:
|
||||
problems_return.append({
|
||||
"pid": problem.pid,
|
||||
"name": problem.name,
|
||||
"title": problem.title,
|
||||
"category": problem.category,
|
||||
"description": problem.description,
|
||||
"hint": problem.hint,
|
||||
"value": problem.value,
|
||||
"threshold": problem.threshold,
|
||||
"weightmap": json.loads(problem.weightmap)
|
||||
"weightmap": problem.weightmap
|
||||
})
|
||||
problems_return.sort(key=lambda prob: prob["value"])
|
||||
return { "success": 1, "problems": problems_return }
|
||||
|
||||
"""
|
||||
|
|
|
@ -7,6 +7,7 @@ import traceback
|
|||
import utils
|
||||
|
||||
class WebException(Exception): pass
|
||||
class InternalException(Exception): pass
|
||||
response_header = { "Content-Type": "application/json; charset=utf-8" }
|
||||
|
||||
def api_wrapper(f):
|
||||
|
|
|
@ -28,7 +28,7 @@ def initialize_logs():
|
|||
for importer, modname, ispkg in pkgutil.walk_packages(path="../api"):
|
||||
create_logger(modname)
|
||||
|
||||
def log(logname, level, message):
|
||||
def log(logname, message, level=INFO):
|
||||
logger = logging.getLogger(logname)
|
||||
message = "[%s] %s" % (datetime.datetime.now().strftime("%m/%d/%Y %X"), message)
|
||||
logger.log(level, message)
|
|
@ -3,6 +3,7 @@ from flask.ext.sqlalchemy import SQLAlchemy
|
|||
import time
|
||||
import traceback
|
||||
import utils
|
||||
import cPickle as pickle
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
|
@ -70,17 +71,14 @@ class Teams(db.Model):
|
|||
return members
|
||||
|
||||
def points(self):
|
||||
score = db.func.sum(Problems.value).label("score")
|
||||
team = db.session.query(Solves.tid, score).join(Teams).join(Problems).filter(Teams.tid==self.tid).group_by(Solves.tid).first()
|
||||
if team:
|
||||
return team.score
|
||||
else:
|
||||
return 0
|
||||
""" TODO: Implement scoring with Bonus Points """
|
||||
return 0
|
||||
|
||||
def place(self, ranked=True):
|
||||
score = db.func.sum(Problems.value).label("score")
|
||||
quickest = db.func.max(Solves.date).label("quickest")
|
||||
teams = db.session.query(Solves.tid).join(Teams).join(Problems).filter().group_by(Solves.tid).order_by(score.desc(), quickest).all()
|
||||
# score = db.func.sum(Problems.value).label("score")
|
||||
# quickest = db.func.max(Solves.date).label("quickest")
|
||||
# teams = db.session.query(Solves.tid).join(Teams).join(Problems).filter().group_by(Solves.tid).order_by(score.desc(), quickest).all()
|
||||
teams = [ self.tid ]
|
||||
try:
|
||||
i = teams.index((self.tid,)) + 1
|
||||
k = i % 10
|
||||
|
@ -134,23 +132,28 @@ class Teams(db.Model):
|
|||
return False
|
||||
|
||||
class Problems(db.Model):
|
||||
pid = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(128))
|
||||
pid = db.Column(db.String(128), primary_key=True, autoincrement=False)
|
||||
title = db.Column(db.String(128))
|
||||
category = db.Column(db.String(128))
|
||||
description = db.Column(db.Text)
|
||||
hint = db.Column(db.Text)
|
||||
flag = db.Column(db.Text)
|
||||
disabled = db.Column(db.Boolean, default=False)
|
||||
value = db.Column(db.Integer)
|
||||
solves = db.Column(db.Integer, default=0)
|
||||
hint = db.Column(db.Text)
|
||||
autogen = db.Column(db.Boolean)
|
||||
bonus = db.Column(db.Integer)
|
||||
threshold = db.Column(db.Integer)
|
||||
weightmap = db.Column(db.PickleType)
|
||||
|
||||
def __init__(self, name, category, description, hint, flag, value):
|
||||
self.name = name
|
||||
def __init__(self, pid, title, category, description, value, hint="", autogen=False, bonus=0, threshold=0, weightmap={}):
|
||||
self.pid = pid
|
||||
self.title = title
|
||||
self.category = category
|
||||
self.description = description
|
||||
self.hint = hint
|
||||
self.flag = flag
|
||||
self.value = value
|
||||
self.hint = hint
|
||||
self.autogen = autogen
|
||||
self.bonus = bonus
|
||||
self.threshold = threshold
|
||||
self.weightmap = weightmap
|
||||
|
||||
class Files(db.Model):
|
||||
fid = db.Column(db.Integer, primary_key=True)
|
||||
|
@ -162,12 +165,11 @@ class Files(db.Model):
|
|||
self.location = location
|
||||
|
||||
class Solves(db.Model):
|
||||
__table_args__ = (db.UniqueConstraint("pid", "tid"), {})
|
||||
sid = db.Column(db.Integer, primary_key=True)
|
||||
pid = db.Column(db.Integer, db.ForeignKey("problems.pid"))
|
||||
tid = db.Column(db.Integer, db.ForeignKey("teams.tid"))
|
||||
pid = db.Column(db.Integer)
|
||||
tid = db.Column(db.Integer)
|
||||
date = db.Column(db.Integer, default=utils.get_time_since_epoch())
|
||||
team = db.relationship("Teams", foreign_keys="Solves.tid", lazy="joined")
|
||||
prob = db.relationship("Problems", foreign_keys="Solves.pid", lazy="joined")
|
||||
correct = db.Column(db.Boolean)
|
||||
flag = db.Column(db.Text)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from flask import current_app as app
|
|||
from werkzeug import secure_filename
|
||||
|
||||
from models import db, Files, Problems, Solves, Teams
|
||||
from decorators import admins_only, api_wrapper, login_required, WebException
|
||||
from decorators import admins_only, api_wrapper, login_required, InternalException, WebException
|
||||
|
||||
blueprint = Blueprint("problem", __name__)
|
||||
|
||||
|
@ -15,122 +15,149 @@ blueprint = Blueprint("problem", __name__)
|
|||
@admins_only
|
||||
@api_wrapper
|
||||
def problem_add():
|
||||
name = request.form["name"]
|
||||
category = request.form["category"]
|
||||
description = request.form["description"]
|
||||
hint = request.form["problem-hint"]
|
||||
flag = request.form["flag"]
|
||||
value = request.form["value"]
|
||||
name = request.form["name"]
|
||||
category = request.form["category"]
|
||||
description = request.form["description"]
|
||||
hint = request.form["problem-hint"]
|
||||
flag = request.form["flag"]
|
||||
value = request.form["value"]
|
||||
|
||||
name_exists = Problems.query.filter_by(name=name).first()
|
||||
if name_exists:
|
||||
raise WebException("Problem name already taken.")
|
||||
problem = Problems(name, category, description, hint, flag, value)
|
||||
db.session.add(problem)
|
||||
db.session.commit()
|
||||
name_exists = Problems.query.filter_by(name=name).first()
|
||||
if name_exists:
|
||||
raise WebException("Problem name already taken.")
|
||||
problem = Problems(name, category, description, hint, flag, value)
|
||||
db.session.add(problem)
|
||||
db.session.commit()
|
||||
|
||||
files = request.files.getlist("files[]")
|
||||
for _file in files:
|
||||
filename = secure_filename(_file.filename)
|
||||
files = request.files.getlist("files[]")
|
||||
for _file in files:
|
||||
filename = secure_filename(_file.filename)
|
||||
|
||||
if len(filename) == 0:
|
||||
continue
|
||||
if len(filename) == 0:
|
||||
continue
|
||||
|
||||
file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename)
|
||||
file_path = os.path.join(app.config["UPLOAD_FOLDER"], filename)
|
||||
|
||||
_file.save(file_path)
|
||||
db_file = Files(problem.pid, "/".join(file_path.split("/")[2:]))
|
||||
db.session.add(db_file)
|
||||
_file.save(file_path)
|
||||
db_file = Files(problem.pid, "/".join(file_path.split("/")[2:]))
|
||||
db.session.add(db_file)
|
||||
|
||||
db.session.commit()
|
||||
db.session.commit()
|
||||
|
||||
return { "success": 1, "message": "Success!" }
|
||||
return { "success": 1, "message": "Success!" }
|
||||
|
||||
@blueprint.route("/delete", methods=["POST"])
|
||||
@admins_only
|
||||
@api_wrapper
|
||||
def problem_delete():
|
||||
pid = request.form["pid"]
|
||||
problem = Problems.query.filter_by(pid=pid).first()
|
||||
if problem:
|
||||
Solves.query.filter_by(pid=pid).delete()
|
||||
Problems.query.filter_by(pid=pid).delete()
|
||||
db.session.commit()
|
||||
return { "success": 1, "message": "Success!" }
|
||||
raise WebException("Problem does not exist!")
|
||||
pid = request.form["pid"]
|
||||
problem = Problems.query.filter_by(pid=pid).first()
|
||||
if problem:
|
||||
Solves.query.filter_by(pid=pid).delete()
|
||||
Problems.query.filter_by(pid=pid).delete()
|
||||
db.session.commit()
|
||||
return { "success": 1, "message": "Success!" }
|
||||
raise WebException("Problem does not exist!")
|
||||
|
||||
@blueprint.route("/update", methods=["POST"])
|
||||
@admins_only
|
||||
@api_wrapper
|
||||
def problem_update():
|
||||
pid = request.form["pid"]
|
||||
name = request.form["name"]
|
||||
category = request.form["category"]
|
||||
description = request.form["description"]
|
||||
hint = request.form["hint"]
|
||||
flag = request.form["flag"]
|
||||
disabled = request.form.get("disabled", 0)
|
||||
value = request.form["value"]
|
||||
pid = request.form["pid"]
|
||||
name = request.form["name"]
|
||||
category = request.form["category"]
|
||||
description = request.form["description"]
|
||||
hint = request.form["hint"]
|
||||
flag = request.form["flag"]
|
||||
disabled = request.form.get("disabled", 0)
|
||||
value = request.form["value"]
|
||||
|
||||
problem = Problems.query.filter_by(pid=pid).first()
|
||||
if problem:
|
||||
problem.name = name
|
||||
problem.category = category
|
||||
problem.description = description
|
||||
problem.hint = hint
|
||||
problem.flag = flag
|
||||
problem.disabled = disabled
|
||||
problem.value = value
|
||||
problem = Problems.query.filter_by(pid=pid).first()
|
||||
if problem:
|
||||
problem.name = name
|
||||
problem.category = category
|
||||
problem.description = description
|
||||
problem.hint = hint
|
||||
problem.flag = flag
|
||||
problem.disabled = disabled
|
||||
problem.value = value
|
||||
|
||||
db.session.add(problem)
|
||||
db.session.commit()
|
||||
db.session.add(problem)
|
||||
db.session.commit()
|
||||
|
||||
return { "success": 1, "message": "Success!" }
|
||||
raise WebException("Problem does not exist!")
|
||||
return { "success": 1, "message": "Success!" }
|
||||
raise WebException("Problem does not exist!")
|
||||
|
||||
@blueprint.route("/submit", methods=["POST"])
|
||||
@api_wrapper
|
||||
@login_required
|
||||
def problem_submit():
|
||||
pid = request.form["pid"]
|
||||
flag = request.form["flag"]
|
||||
tid = session["tid"]
|
||||
pid = request.form["pid"]
|
||||
flag = request.form["flag"]
|
||||
tid = session["tid"]
|
||||
|
||||
problem = Problems.query.filter_by(pid=pid).first()
|
||||
team = Teams.query.filter_by(tid=tid).first()
|
||||
if problem:
|
||||
if flag == problem.flag:
|
||||
solve = Solves(pid, tid)
|
||||
team.score += problem.value
|
||||
problem.solves += 1
|
||||
db.session.add(solve)
|
||||
db.session.add(team)
|
||||
db.session.add(problem)
|
||||
db.session.commit()
|
||||
problem = Problems.query.filter_by(pid=pid).first()
|
||||
team = Teams.query.filter_by(tid=tid).first()
|
||||
if problem:
|
||||
if flag == problem.flag:
|
||||
solve = Solves(pid, tid)
|
||||
team.score += problem.value
|
||||
problem.solves += 1
|
||||
db.session.add(solve)
|
||||
db.session.add(team)
|
||||
db.session.add(problem)
|
||||
db.session.commit()
|
||||
|
||||
logger.log(__name__, logger.WARNING, "%s has solved %s by submitting %s" % (team.name, problem.name, flag))
|
||||
return { "success": 1, "message": "Correct!" }
|
||||
logger.log(__name__, logger.WARNING, "%s has solved %s by submitting %s" % (team.name, problem.name, flag))
|
||||
return { "success": 1, "message": "Correct!" }
|
||||
|
||||
else:
|
||||
logger.log(__name__, logger.WARNING, "%s has incorrectly submitted %s to %s" % (team.name, flag, problem.name))
|
||||
raise WebException("Incorrect.")
|
||||
else:
|
||||
logger.log(__name__, logger.WARNING, "%s has incorrectly submitted %s to %s" % (team.name, flag, problem.name))
|
||||
raise WebException("Incorrect.")
|
||||
|
||||
else:
|
||||
raise WebException("Problem does not exist!")
|
||||
else:
|
||||
raise WebException("Problem does not exist!")
|
||||
|
||||
@blueprint.route("/data", methods=["POST"])
|
||||
#@api_wrapper # Disable atm due to json serialization issues: will fix
|
||||
@login_required
|
||||
def problem_data():
|
||||
problems = Problems.query.add_columns("pid", "name", "category", "description", "hint", "value", "solves").order_by(Problems.value).filter_by(disabled=False).all()
|
||||
jason = []
|
||||
problems = Problems.query.add_columns("pid", "name", "category", "description", "hint", "value", "solves").order_by(Problems.value).filter_by(disabled=False).all()
|
||||
jason = []
|
||||
|
||||
for problem in problems:
|
||||
problem_files = [ str(_file.location) for _file in Files.query.filter_by(pid=int(problem.pid)).all() ]
|
||||
jason.append({"pid": problem[1], "name": problem[2] ,"category": problem[3], "description": problem[4], "hint": problem[5], "value": problem[6], "solves": problem[7], "files": problem_files})
|
||||
for problem in problems:
|
||||
problem_files = [ str(_file.location) for _file in Files.query.filter_by(pid=int(problem.pid)).all() ]
|
||||
jason.append({"pid": problem[1], "name": problem[2] ,"category": problem[3], "description": problem[4], "hint": problem[5], "value": problem[6], "solves": problem[7], "files": problem_files})
|
||||
|
||||
return jsonify(data=jason)
|
||||
return jsonify(data=jason)
|
||||
|
||||
def insert_problem(data):
|
||||
print data
|
||||
pass
|
||||
def insert_problem(data, force=False):
|
||||
with app.app_context():
|
||||
if len(list(get_problem(pid=data["pid"]).all())) > 0:
|
||||
if force == True:
|
||||
_problem = Problems.query.filter_by(pid=data["pid"]).first()
|
||||
db.session.delete(_problem)
|
||||
db.session.commit()
|
||||
else:
|
||||
raise InternalException("Problem already exists.")
|
||||
|
||||
insert = Problems(data["pid"], data["title"], data["category"], data["description"], data["value"])
|
||||
if "hint" in data: insert.hint = data["hint"]
|
||||
if "autogen" in data: insert.autogen = data["autogen"]
|
||||
if "bonus" in data: insert.bonus = data["bonus"]
|
||||
if "threshold" in data: insert.threshold = data["threshold"]
|
||||
if "weightmap" in data: insert.weightmap = data["weightmap"]
|
||||
db.session.add(insert)
|
||||
db.session.commit()
|
||||
|
||||
return True
|
||||
|
||||
def get_problem(title=None, pid=None):
|
||||
match = {}
|
||||
if title != None:
|
||||
match.update({ "title": title })
|
||||
elif pid != None:
|
||||
match.update({ "pid": pid })
|
||||
with app.app_context():
|
||||
result = Problems.query.filter_by(**match)
|
||||
return result
|
|
@ -35,52 +35,53 @@ def team_create():
|
|||
db.session.commit()
|
||||
Users.query.filter_by(uid=_user.uid).update({ "tid": team.tid })
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
|
||||
session["tid"] = team.tid
|
||||
session["tid"] = team.tid
|
||||
return { "success": 1, "message": "Success!" }
|
||||
|
||||
@blueprint.route("/delete", methods=["POST"])
|
||||
@api_wrapper
|
||||
@login_required
|
||||
def team_delete():
|
||||
username = session["username"]
|
||||
tid = session["tid"]
|
||||
team = Teams.query.filter_by(tid=tid).first()
|
||||
usr = Users.query.filter_by(username=username).first()
|
||||
owner = team.owner
|
||||
if usr.uid == owner or usr.admin:
|
||||
for member in Users.query.filter_by(tid=tid).all():
|
||||
member.tid = -1
|
||||
with app.app_context():
|
||||
db.session.add(member)
|
||||
|
||||
with app.app_context():
|
||||
db.session.delete(team)
|
||||
db.session.commit()
|
||||
session.pop("tid")
|
||||
return { "success": 1, "message": "Success!" }
|
||||
else:
|
||||
raise WebException("Not authorized.")
|
||||
username = session["username"]
|
||||
tid = session["tid"]
|
||||
team = Teams.query.filter_by(tid=tid).first()
|
||||
usr = Users.query.filter_by(username=username).first()
|
||||
owner = team.owner
|
||||
if usr.uid == owner or usr.admin:
|
||||
with app.app_context():
|
||||
for member in Users.query.filter_by(tid=tid).all():
|
||||
member.tid = -1
|
||||
db.session.add(member)
|
||||
db.session.delete(team)
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
session.pop("tid")
|
||||
return { "success": 1, "message": "Success!" }
|
||||
else:
|
||||
raise WebException("Not authorized.")
|
||||
|
||||
@blueprint.route("/remove_member", methods=["POST"])
|
||||
@api_wrapper
|
||||
@login_required
|
||||
def team_remove_member():
|
||||
username = session["username"]
|
||||
tid = session["tid"]
|
||||
team = Teams.query.filter_by(tid=tid).first()
|
||||
usr = Users.query.filter_by(username=username).first()
|
||||
owner = team.owner
|
||||
if usr.uid == owner or usr.admin:
|
||||
params = utils.flat_multi(request.form)
|
||||
user_to_remove = Users.query.filter_by(username=params.get("user"))
|
||||
user_to_remove.tid = -1
|
||||
with app.app_context():
|
||||
db.session.add(user_to_remove)
|
||||
db.session.commit()
|
||||
return { "success": 1, "message": "Success!" }
|
||||
else:
|
||||
raise WebException("Not authorized.")
|
||||
username = session["username"]
|
||||
tid = session["tid"]
|
||||
team = Teams.query.filter_by(tid=tid).first()
|
||||
usr = Users.query.filter_by(username=username).first()
|
||||
owner = team.owner
|
||||
if usr.uid == owner or usr.admin:
|
||||
params = utils.flat_multi(request.form)
|
||||
user_to_remove = Users.query.filter_by(username=params.get("user"))
|
||||
user_to_remove.tid = -1
|
||||
with app.app_context():
|
||||
db.session.add(user_to_remove)
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
return { "success": 1, "message": "Success!" }
|
||||
else:
|
||||
raise WebException("Not authorized.")
|
||||
|
||||
@blueprint.route("/invite", methods=["POST"])
|
||||
@api_wrapper
|
||||
|
@ -110,6 +111,7 @@ def team_invite():
|
|||
with app.app_context():
|
||||
db.session.add(req)
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
|
||||
return { "success": 1, "message": "Success!" }
|
||||
|
||||
|
@ -135,6 +137,7 @@ def team_invite_rescind():
|
|||
with app.app_context():
|
||||
db.session.delete(invitation)
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
|
||||
return { "success": 1, "message": "Success!" }
|
||||
|
||||
|
@ -159,6 +162,7 @@ def team_invite_request():
|
|||
with app.app_context():
|
||||
db.session.add(req)
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
|
||||
return { "success": 1, "message": "Success!" }
|
||||
|
||||
|
@ -190,6 +194,7 @@ def team_accept_invite():
|
|||
if invitation2 is not None:
|
||||
db.session.delete(invitation2)
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
|
||||
return { "success": 1, "message": "Success!" }
|
||||
|
||||
|
@ -225,6 +230,7 @@ def team_accept_invite_request():
|
|||
if invitation2 is not None:
|
||||
db.session.delete(invitation2)
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
|
||||
return { "success": 1, "message": "Success!" }
|
||||
|
||||
|
@ -280,7 +286,7 @@ TeamSchema = Schema({
|
|||
([__check_teamname], "This teamname is taken, did you forget your password?")
|
||||
),
|
||||
Required("school"): check(
|
||||
([str, Length(min=4, max=60)], "Your school name should be between 4 and 60 characters long."),
|
||||
([str, Length(min=4, max=40)], "Your school name should be between 4 and 40 characters long."),
|
||||
([utils.__check_ascii], "Please only use ASCII characters in your school name."),
|
||||
),
|
||||
}, extra=True)
|
||||
|
|
|
@ -8,7 +8,7 @@ from decorators import api_wrapper, WebException
|
|||
from schemas import verify_to_schema, check
|
||||
|
||||
import datetime
|
||||
import logger
|
||||
import logger, logging
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
|
@ -94,7 +94,7 @@ def user_register():
|
|||
db.session.commit()
|
||||
utils.generate_identicon(email, user.uid)
|
||||
|
||||
logger.log(__name__, logger.INFO, "%s registered with %s" % (name.encode("utf-8"), email.encode("utf-8")))
|
||||
logger.log(__name__, "%s registered with %s" % (name.encode("utf-8"), email.encode("utf-8")))
|
||||
login_user(username, password)
|
||||
|
||||
return { "success": 1, "message": "Success!" }
|
||||
|
|
|
@ -10,6 +10,7 @@ import config
|
|||
import json
|
||||
import logging
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from api.decorators import api_wrapper
|
||||
|
||||
|
@ -23,6 +24,8 @@ with app.app_context():
|
|||
db.init_app(app)
|
||||
db.create_all()
|
||||
|
||||
app.db = db
|
||||
|
||||
app.secret_key = config.SECRET_KEY
|
||||
|
||||
app.register_blueprint(api.admin.blueprint, url_prefix="/api/admin")
|
||||
|
@ -39,27 +42,29 @@ def api_main():
|
|||
|
||||
def run(args):
|
||||
with app.app_context():
|
||||
app.debug = keyword_args["debug"]
|
||||
keyword_args = dict(args._get_kwargs())
|
||||
app.debug = keyword_args["debug"] if "debug" in keyword_args else False
|
||||
app.run(host="0.0.0.0", port=8000)
|
||||
|
||||
def load_problems(args):
|
||||
keyword_args = dict(args._get_kwargs())
|
||||
force = keyword_args["force"] if "force" in keyword_args else False
|
||||
|
||||
if not os.path.exists(config.PROBLEM_DIR):
|
||||
logging.critical("Problems directory doesn't exist.")
|
||||
api.logger.log("api.problem.log", "Problems directory doesn't exist.")
|
||||
return
|
||||
|
||||
for (dirpath, dirnames, filenames) in os.walk(config.PROBLEM_DIR):
|
||||
if "problem.json" in filenames:
|
||||
json_file = os.path.join(dirpath, "problem.json")
|
||||
contents = open(json_file).read()
|
||||
|
||||
try:
|
||||
data = json.loads(contents)
|
||||
except ValueError as e:
|
||||
logging.warning("Invalid JSON format in file {filename} ({exception})".format(filename=json_file, exception=e))
|
||||
api.logger.log("api.problem.log", "Invalid JSON format in file {filename} ({exception})".format(filename=json_file, exception=e))
|
||||
continue
|
||||
|
||||
if not isinstance(data, dict):
|
||||
logging.warning("{filename} is not a dict.".format(filename=json_file))
|
||||
api.logger.log("api.problem.log", "{filename} is not a dict.".format(filename=json_file))
|
||||
continue
|
||||
|
||||
missing_keys = []
|
||||
|
@ -67,24 +72,29 @@ def load_problems(args):
|
|||
if key not in data:
|
||||
missing_keys.append(key)
|
||||
if len(missing_keys) > 0:
|
||||
logging.warning("{filename} is missing the following keys: {keys}".format(filename=json_file, keys=", ".join(missing_keys)))
|
||||
api.logger.log("api.problem.log", "{filename} is missing the following keys: {keys}".format(filename=json_file, keys=", ".join(missing_keys)))
|
||||
continue
|
||||
|
||||
relative_path = os.path.relpath(dirpath, config.PROBLEM_DIR)
|
||||
logging.info("Found problem '{}'".format(data["title"]))
|
||||
data["description"] = open(os.path.join(dirpath, "description.md"), "r").read()
|
||||
api.logger.log("api.problem.log", "Found problem '{}'".format(data["title"]))
|
||||
with app.app_context():
|
||||
try:
|
||||
api.problem.insert_problem(data, force=force)
|
||||
except Exception as e:
|
||||
api.logger.log("api.problem.log", "Problem '{}' was not added to the database. Error: {}".format(data["title"], e))
|
||||
api.logger.log("api.problem.log", "{}".format(traceback.format_exc()))
|
||||
|
||||
try:
|
||||
api.problem.insert_problem(data)
|
||||
except Exception as e:
|
||||
logging.warning("Problem '{}' was not added to the database. Error: {}".format(data["title"], e))
|
||||
api.logger.log("api.problem.log", "Finished.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
def main():
|
||||
parser = ArgumentParser(description="EasyCTF Server Management")
|
||||
|
||||
subparser = parser.add_subparsers(help="Select one of the following actions.")
|
||||
parser_problems = subparser.add_parser("problems", help="Manage problems.")
|
||||
subparser_problems = parser_problems.add_subparsers(help="Select one of the following actions.")
|
||||
parser_problems_load = subparser_problems.add_parser("load", help="Load all problems into database.")
|
||||
parser_problems_load.add_argument("-f", "--force", action="store_true", help="Force overwrite problems.", default=False)
|
||||
parser_problems_load.set_defaults(func=load_problems)
|
||||
|
||||
parser_run = subparser.add_parser("run", help="Run the server.")
|
||||
|
@ -92,10 +102,10 @@ if __name__ == "__main__":
|
|||
parser_run.set_defaults(func=run)
|
||||
|
||||
args = parser.parse_args()
|
||||
keyword_args, _ = dict(args._get_kwargs()), args._get_args()
|
||||
logging.getLogger().setLevel(logging.INFO)
|
||||
|
||||
if "func" in args:
|
||||
args.func(args)
|
||||
else:
|
||||
parser.print_help()
|
||||
parser.print_help()
|
||||
|
||||
main()
|
|
@ -27,6 +27,7 @@
|
|||
</head>
|
||||
|
||||
<body ng-controller="mainController">
|
||||
<div id="site-message" style="margin: 0"></div>
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
|
|
|
@ -75,10 +75,37 @@ app.config(function($routeProvider, $locationProvider) {
|
|||
$locationProvider.html5Mode(true);
|
||||
});
|
||||
|
||||
function api_call(method, url, data, callback_success, callback_fail) {
|
||||
if (method.toLowerCase() == "post") {
|
||||
data["csrf_token"] = $.cookie("csrf_token");
|
||||
}
|
||||
$.ajax({
|
||||
"type": method,
|
||||
"datatype": "json",
|
||||
"data": data,
|
||||
"url": url,
|
||||
"cache": false
|
||||
}).done(callback_success).fail(callback_fail);
|
||||
}
|
||||
|
||||
function permanent_message(containerId, alertType, message, callback) {
|
||||
$("#" + containerId).html("<div class=\"alert alert-" + alertType + "\" style=\"margin:0;\">" + message + "</div>");
|
||||
$("#" + containerId).hide().slideDown("fast", "swing");
|
||||
};
|
||||
|
||||
function display_message(containerId, alertType, message, callback) {
|
||||
$("#" + containerId).html("<div class=\"alert alert-" + alertType + "\">" + message + "</div>");
|
||||
$("#" + containerId).hide().slideDown("fast", "swing", function() {
|
||||
window.setTimeout(function () {
|
||||
$("#" + containerId).slideUp("fast", "swing", callback);
|
||||
}, message.length * 55);
|
||||
});
|
||||
};
|
||||
|
||||
app.controller("mainController", ["$scope", "$http", function($scope, $http) {
|
||||
$scope.config = { navbar: { } };
|
||||
$scope.timestamp = Date.now();
|
||||
$.get("/api/user/status", function(result) {
|
||||
api_call("GET", "/api/user/status", {}, function(result) {
|
||||
if (result["success"] == 1) {
|
||||
delete result["success"];
|
||||
$scope.config.navbar = result;
|
||||
|
@ -87,14 +114,15 @@ app.controller("mainController", ["$scope", "$http", function($scope, $http) {
|
|||
$scope.config.navbar.logged_in = false;
|
||||
}
|
||||
$scope.$apply();
|
||||
}).fail(function() {
|
||||
}, function() {
|
||||
$scope.config.navbar.logged_in = false;
|
||||
$scope.$apply();
|
||||
permanent_message("site-message", "danger", "<div class='container'>The EasyCTF API server is currently down. We're working to fix this error right away. Follow <a href='http://twitter.com/easyctf' target='_blank'>@easyctf</a> for status updates.</div>");
|
||||
});
|
||||
}]);
|
||||
|
||||
app.controller("logoutController", function() {
|
||||
$.get("/api/user/logout", function(result) {
|
||||
api_call("GET", "/api/user/logout", {}, function(result) {
|
||||
location.href = "/";
|
||||
});
|
||||
});
|
||||
|
@ -103,7 +131,7 @@ app.controller("profileController", ["$controller", "$scope", "$http", "$routePa
|
|||
var data = { };
|
||||
if ("username" in $routeParams) data["username"] = $routeParams["username"];
|
||||
$controller("mainController", { $scope: $scope });
|
||||
$.get("/api/user/info", data, function(result) {
|
||||
api_call("GET", "/api/user/info", data, function(result) {
|
||||
if (result["success"] == 1) {
|
||||
$scope.user = result["user"];
|
||||
}
|
||||
|
@ -129,7 +157,7 @@ app.controller("teamController", ["$controller", "$scope", "$http", "$routeParam
|
|||
} else {
|
||||
$controller("loginController", { $scope: $scope });
|
||||
}
|
||||
$.get("/api/team/info", data, function(result) {
|
||||
api_call("GET", "/api/team/info", data, function(result) {
|
||||
if (result["success"] == 1) {
|
||||
$scope.team = result["team"];
|
||||
}
|
||||
|
@ -155,7 +183,7 @@ app.controller("resetController", ["$controller", "$scope", "$http", "$routePara
|
|||
if ("token" in $routeParams) {
|
||||
$scope.token = true;
|
||||
token = $routeParams["token"];
|
||||
$.get("/api/user/forgot/" + token, data, function(data) {
|
||||
api_call("GET", "/api/user/forgot/" + token, data, function(data) {
|
||||
$scope.body = data["message"];
|
||||
$scope.success = data["success"]
|
||||
$scope.$apply();
|
||||
|
@ -181,9 +209,11 @@ app.controller("adminController", ["$controller", "$scope", "$http", function($c
|
|||
|
||||
app.controller("adminProblemsController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||
$controller("adminController", { $scope: $scope });
|
||||
$.get("/api/admin/problems/list", function(result) {
|
||||
api_call("GET", "/api/admin/problems/list", {}, function(result) {
|
||||
if (result["success"] == 1) {
|
||||
$scope.problems = result["problems"];
|
||||
} else {
|
||||
$scope.problems = [];
|
||||
}
|
||||
$scope.$apply();
|
||||
});
|
||||
|
@ -191,7 +221,7 @@ app.controller("adminProblemsController", ["$controller", "$scope", "$http", fun
|
|||
|
||||
app.controller("settingsController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||
$controller("loginController", { $scope: $scope });
|
||||
$.get("/api/user/info", {}, function(result) {
|
||||
api_call("GET", "/api/user/info", {}, function(result) {
|
||||
if (result["success"] == 1) {
|
||||
$scope.user = result["user"];
|
||||
}
|
||||
|
@ -199,27 +229,6 @@ app.controller("settingsController", ["$controller", "$scope", "$http", function
|
|||
});
|
||||
}]);
|
||||
|
||||
function display_message(containerId, alertType, message, callback) {
|
||||
$("#" + containerId).html("<div class=\"alert alert-" + alertType + "\">" + message + "</div>");
|
||||
$("#" + containerId).hide().slideDown("fast", "swing", function() {
|
||||
window.setTimeout(function () {
|
||||
$("#" + containerId).slideUp("fast", "swing", callback);
|
||||
}, message.length * 55);
|
||||
});
|
||||
};
|
||||
|
||||
function api_call(method, url, data, callback_success, callback_fail) {
|
||||
if (method.toLowerCase() == "post") {
|
||||
data["csrf_token"] = $.cookie("csrf_token");
|
||||
}
|
||||
$.ajax({
|
||||
"type": method,
|
||||
"datatype": "json",
|
||||
"data": data,
|
||||
"url": url
|
||||
}).done(callback_success).fail(callback_fail);
|
||||
}
|
||||
|
||||
$.fn.serializeObject = function() {
|
||||
var a, o;
|
||||
o = {};
|
||||
|
|
|
@ -12,147 +12,38 @@
|
|||
}
|
||||
</style>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#problems" aria-controls="problems" role="tab" data-toggle="tab">Problems</a></li>
|
||||
<li role="presentation"><a href="#filesystem" aria-controls="filesystem" role="tab" data-toggle="tab">Filesystem</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="problems">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<div class="panel-group" id="problems" role="tablist" aria-multiselectable="true">
|
||||
<div class="well" ng-show="problems.length==0">There are no problems to show!</div>
|
||||
<!-- <div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="heading-new">
|
||||
<h4 class="panel-title">
|
||||
<a class="NO_HOVER_UNDERLINE_DAMMIT" style="display:block;" data-toggle="collapse" data-parent="#accordion" href="#collapse-new" aria-expanded="true" aria-controls="collapse-new">
|
||||
<i class="fa fa-fw fa-plus"></i> Create New Problem
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapse-new" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="heading-new">
|
||||
<div class="panel-body">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-9">
|
||||
<div class="row">
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-pills nav-stacked col-md-3">
|
||||
<li class="active"><a data-target="#new" data-toggle="tab">New</a></li>
|
||||
<li ng-repeat="problem in problems"><a data-target="#problem_{{ problem['pid'] }}" data-toggle="tab">{{ problem["title"] }} ({{ problem["value"] }} points)</a></li>
|
||||
</ul>
|
||||
<div class="tab-content col-md-9">
|
||||
<div class="tab-pane active" id="new">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" role="tab" id="heading-new">
|
||||
<h4 class="panel-title">
|
||||
<i class="fa fa-fw fa-plus"></i> Create New Problem
|
||||
</h4>
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">New Problem</h4>
|
||||
</div>
|
||||
<div id="collapse-new" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="heading-new">
|
||||
<div class="panel-body">
|
||||
<form class="form-horizontal" onsubmit="create_problem(); return false;" id="create_problem">
|
||||
<fieldset>
|
||||
<div id="register_msg"></div>
|
||||
</fieldset>
|
||||
<fieldset class="container-fluid">
|
||||
<p>Be sure you are familiar with guidelines to writing problems before you submit! Problem unlocking and other options will be available after you submit your problem.</p>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 form-group">
|
||||
<label class="col-sm-12" for="name"><small>Title</small></label>
|
||||
<div class="col-sm-12">
|
||||
<input class="form-control" type="text" required name="name" placeholder="Title" autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 form-group">
|
||||
<label class="col-sm-12" for="category"><small>Category</small></label>
|
||||
<div class="col-sm-12">
|
||||
<select name="category" class="selectpicker" data-width="100%">
|
||||
<option value="Algorithm">Algorithm</option>
|
||||
<option value="Binary Exploitation">Binary Exploitation</option>
|
||||
<option value="Cryptography">Cryptography</option>
|
||||
<option value="Forensics">Forensics</option>
|
||||
<option value="Linux">Linux</option>
|
||||
<option value="Miscellaneous">Miscellaneous</option>
|
||||
<option value="Programming">Programming</option>
|
||||
<option value="Reconnaissance">Reconnaissance</option>
|
||||
<option value="Reverse Engineering">Reverse Engineering</option>
|
||||
<option value="Web Exploitation">Web Exploitation</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 form-group">
|
||||
<label class="col-sm-12" for="value"><small>Value</small></label>
|
||||
<div class="col-sm-12">
|
||||
<input class="form-control" type="number" required name="value" placeholder="Value" autocomplete="off" value="10" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 form-group">
|
||||
<label class="col-sm-12" for="description"><small>Description</small></label>
|
||||
<div class="col-sm-12">
|
||||
<div id="description"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 form-group">
|
||||
<label class="col-sm-12" for="hint"><small>Hint</small></label>
|
||||
<div class="col-sm-12">
|
||||
<input class="form-control" type="text" required name="hint" placeholder="Hint" autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 form-group">
|
||||
<label class="col-sm-12" for="grader"><small>Grader</small></label>
|
||||
<div class="col-sm-12">
|
||||
<div id="new_grader" class="ace_editor">def grade(key, tid):
|
||||
""" Grade this problem. """
|
||||
flag = "easyctf{a_flag}"
|
||||
case_sensitive = True
|
||||
if (key.find(flag) >= 0) if case_sensitive \
|
||||
else (key.lower().find(flag.lower()) >= 0):
|
||||
return { "success": 1, "message": "Correct!" }
|
||||
return { "success": 0, "message": "Sorry, try again." }</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 form-group">
|
||||
<center>
|
||||
<input type="submit" class="btn btn-primary btn-lg" value="Create" />
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="filesystem">
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-3">
|
||||
<div class="">
|
||||
<div class="tab-pane" ng-repeat="problem in problems" id="problem_{{ problem['pid'] }}">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">{{ problem["title"] }}</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-9">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("ul[role=tablist]").tab();
|
||||
$("a[role=tab]").click(function(e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
$(".selectpicker").selectpicker();
|
||||
/*$(".selectpicker").selectpicker();
|
||||
var config = {
|
||||
toolbar: [
|
||||
{ name: "basicstyles", items: [ "Bold", "Italic", "Underline" ] },
|
||||
|
@ -175,6 +66,6 @@
|
|||
}).load();
|
||||
var new_grader = ace.edit("new_grader");
|
||||
new_grader.setTheme("ace/theme/tomorrow");
|
||||
new_grader.getSession().setMode("ace/mode/python");
|
||||
new_grader.getSession().setMode("ace/mode/python");*/
|
||||
});
|
||||
</script>
|
|
@ -33,7 +33,7 @@
|
|||
<i class="fa fa-fw fa-flag"></i>
|
||||
I'm in the team!
|
||||
</div>
|
||||
<div class="label label-warning">
|
||||
<div class="label label-warning" ng-show="team['observer']==true">
|
||||
<i class="fa fa-fw fa-globe"></i>
|
||||
OBSERVER
|
||||
</div>
|
||||
|
@ -43,7 +43,7 @@
|
|||
<h1><span class="padded">{{ team['teamname'] }}</span></h1>
|
||||
<h4><i class="fa fa-fw fa-university"></i> <span class="padded">{{ team['school'] || 'Unknown Affiliation' }}</span></h4>
|
||||
<div class="row">
|
||||
<div class="label label-warning">
|
||||
<div class="label label-warning" ng-show="team['observer']==true">
|
||||
<i class="fa fa-fw fa-globe"></i>
|
||||
OBSERVER
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue