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.
|
# 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;
|
default_type text/html;
|
||||||
try_files /index.html /index.html;
|
try_files /index.html /index.html;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
"title": "Cancer",
|
"title": "Cancer",
|
||||||
"hint": "No hint!",
|
"hint": "No hint!",
|
||||||
"category": "Miscellaneous",
|
"category": "Miscellaneous",
|
||||||
"autogen": false,
|
"autogen": true,
|
||||||
"programming": false,
|
"programming": false,
|
||||||
"value": 20,
|
"value": 100,
|
||||||
"bonus": 0,
|
"bonus": 0,
|
||||||
"threshold": 0,
|
"threshold": 0,
|
||||||
"weightmap": { }
|
"weightmap": { }
|
||||||
|
|
|
@ -6,3 +6,4 @@ gunicorn
|
||||||
requests
|
requests
|
||||||
voluptuous
|
voluptuous
|
||||||
PIL
|
PIL
|
||||||
|
markdown2
|
|
@ -3,7 +3,7 @@ from decorators import admins_only, api_wrapper
|
||||||
from models import db, Problems, Files
|
from models import db, Problems, Files
|
||||||
from schemas import verify_to_schema, check
|
from schemas import verify_to_schema, check
|
||||||
|
|
||||||
import json
|
import cPickle as pickle
|
||||||
|
|
||||||
blueprint = Blueprint("admin", __name__)
|
blueprint = Blueprint("admin", __name__)
|
||||||
|
|
||||||
|
@ -16,14 +16,15 @@ def problem_data():
|
||||||
for problem in problems:
|
for problem in problems:
|
||||||
problems_return.append({
|
problems_return.append({
|
||||||
"pid": problem.pid,
|
"pid": problem.pid,
|
||||||
"name": problem.name,
|
"title": problem.title,
|
||||||
"category": problem.category,
|
"category": problem.category,
|
||||||
"description": problem.description,
|
"description": problem.description,
|
||||||
"hint": problem.hint,
|
"hint": problem.hint,
|
||||||
"value": problem.value,
|
"value": problem.value,
|
||||||
"threshold": problem.threshold,
|
"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 }
|
return { "success": 1, "problems": problems_return }
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -7,6 +7,7 @@ import traceback
|
||||||
import utils
|
import utils
|
||||||
|
|
||||||
class WebException(Exception): pass
|
class WebException(Exception): pass
|
||||||
|
class InternalException(Exception): pass
|
||||||
response_header = { "Content-Type": "application/json; charset=utf-8" }
|
response_header = { "Content-Type": "application/json; charset=utf-8" }
|
||||||
|
|
||||||
def api_wrapper(f):
|
def api_wrapper(f):
|
||||||
|
|
|
@ -28,7 +28,7 @@ def initialize_logs():
|
||||||
for importer, modname, ispkg in pkgutil.walk_packages(path="../api"):
|
for importer, modname, ispkg in pkgutil.walk_packages(path="../api"):
|
||||||
create_logger(modname)
|
create_logger(modname)
|
||||||
|
|
||||||
def log(logname, level, message):
|
def log(logname, message, level=INFO):
|
||||||
logger = logging.getLogger(logname)
|
logger = logging.getLogger(logname)
|
||||||
message = "[%s] %s" % (datetime.datetime.now().strftime("%m/%d/%Y %X"), message)
|
message = "[%s] %s" % (datetime.datetime.now().strftime("%m/%d/%Y %X"), message)
|
||||||
logger.log(level, message)
|
logger.log(level, message)
|
|
@ -3,6 +3,7 @@ from flask.ext.sqlalchemy import SQLAlchemy
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
import utils
|
import utils
|
||||||
|
import cPickle as pickle
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
|
|
||||||
|
@ -70,17 +71,14 @@ class Teams(db.Model):
|
||||||
return members
|
return members
|
||||||
|
|
||||||
def points(self):
|
def points(self):
|
||||||
score = db.func.sum(Problems.value).label("score")
|
""" TODO: Implement scoring with Bonus Points """
|
||||||
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
|
return 0
|
||||||
|
|
||||||
def place(self, ranked=True):
|
def place(self, ranked=True):
|
||||||
score = db.func.sum(Problems.value).label("score")
|
# score = db.func.sum(Problems.value).label("score")
|
||||||
quickest = db.func.max(Solves.date).label("quickest")
|
# 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 = db.session.query(Solves.tid).join(Teams).join(Problems).filter().group_by(Solves.tid).order_by(score.desc(), quickest).all()
|
||||||
|
teams = [ self.tid ]
|
||||||
try:
|
try:
|
||||||
i = teams.index((self.tid,)) + 1
|
i = teams.index((self.tid,)) + 1
|
||||||
k = i % 10
|
k = i % 10
|
||||||
|
@ -134,23 +132,28 @@ class Teams(db.Model):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
class Problems(db.Model):
|
class Problems(db.Model):
|
||||||
pid = db.Column(db.Integer, primary_key=True)
|
pid = db.Column(db.String(128), primary_key=True, autoincrement=False)
|
||||||
name = db.Column(db.String(128))
|
title = db.Column(db.String(128))
|
||||||
category = db.Column(db.String(128))
|
category = db.Column(db.String(128))
|
||||||
description = db.Column(db.Text)
|
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)
|
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):
|
def __init__(self, pid, title, category, description, value, hint="", autogen=False, bonus=0, threshold=0, weightmap={}):
|
||||||
self.name = name
|
self.pid = pid
|
||||||
|
self.title = title
|
||||||
self.category = category
|
self.category = category
|
||||||
self.description = description
|
self.description = description
|
||||||
self.hint = hint
|
|
||||||
self.flag = flag
|
|
||||||
self.value = value
|
self.value = value
|
||||||
|
self.hint = hint
|
||||||
|
self.autogen = autogen
|
||||||
|
self.bonus = bonus
|
||||||
|
self.threshold = threshold
|
||||||
|
self.weightmap = weightmap
|
||||||
|
|
||||||
class Files(db.Model):
|
class Files(db.Model):
|
||||||
fid = db.Column(db.Integer, primary_key=True)
|
fid = db.Column(db.Integer, primary_key=True)
|
||||||
|
@ -162,12 +165,11 @@ class Files(db.Model):
|
||||||
self.location = location
|
self.location = location
|
||||||
|
|
||||||
class Solves(db.Model):
|
class Solves(db.Model):
|
||||||
|
__table_args__ = (db.UniqueConstraint("pid", "tid"), {})
|
||||||
sid = db.Column(db.Integer, primary_key=True)
|
sid = db.Column(db.Integer, primary_key=True)
|
||||||
pid = db.Column(db.Integer, db.ForeignKey("problems.pid"))
|
pid = db.Column(db.Integer)
|
||||||
tid = db.Column(db.Integer, db.ForeignKey("teams.tid"))
|
tid = db.Column(db.Integer)
|
||||||
date = db.Column(db.Integer, default=utils.get_time_since_epoch())
|
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)
|
correct = db.Column(db.Boolean)
|
||||||
flag = db.Column(db.Text)
|
flag = db.Column(db.Text)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ from flask import current_app as app
|
||||||
from werkzeug import secure_filename
|
from werkzeug import secure_filename
|
||||||
|
|
||||||
from models import db, Files, Problems, Solves, Teams
|
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__)
|
blueprint = Blueprint("problem", __name__)
|
||||||
|
|
||||||
|
@ -131,6 +131,33 @@ def problem_data():
|
||||||
|
|
||||||
return jsonify(data=jason)
|
return jsonify(data=jason)
|
||||||
|
|
||||||
def insert_problem(data):
|
def insert_problem(data, force=False):
|
||||||
print data
|
with app.app_context():
|
||||||
pass
|
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,6 +35,7 @@ def team_create():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
Users.query.filter_by(uid=_user.uid).update({ "tid": team.tid })
|
Users.query.filter_by(uid=_user.uid).update({ "tid": team.tid })
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
db.session.close()
|
||||||
|
|
||||||
session["tid"] = team.tid
|
session["tid"] = team.tid
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
|
@ -49,14 +50,13 @@ def team_delete():
|
||||||
usr = Users.query.filter_by(username=username).first()
|
usr = Users.query.filter_by(username=username).first()
|
||||||
owner = team.owner
|
owner = team.owner
|
||||||
if usr.uid == owner or usr.admin:
|
if usr.uid == owner or usr.admin:
|
||||||
|
with app.app_context():
|
||||||
for member in Users.query.filter_by(tid=tid).all():
|
for member in Users.query.filter_by(tid=tid).all():
|
||||||
member.tid = -1
|
member.tid = -1
|
||||||
with app.app_context():
|
|
||||||
db.session.add(member)
|
db.session.add(member)
|
||||||
|
|
||||||
with app.app_context():
|
|
||||||
db.session.delete(team)
|
db.session.delete(team)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
db.session.close()
|
||||||
session.pop("tid")
|
session.pop("tid")
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
else:
|
else:
|
||||||
|
@ -78,6 +78,7 @@ def team_remove_member():
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.session.add(user_to_remove)
|
db.session.add(user_to_remove)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
db.session.close()
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
else:
|
else:
|
||||||
raise WebException("Not authorized.")
|
raise WebException("Not authorized.")
|
||||||
|
@ -110,6 +111,7 @@ def team_invite():
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.session.add(req)
|
db.session.add(req)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
db.session.close()
|
||||||
|
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
|
|
||||||
|
@ -135,6 +137,7 @@ def team_invite_rescind():
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.session.delete(invitation)
|
db.session.delete(invitation)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
db.session.close()
|
||||||
|
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
|
|
||||||
|
@ -159,6 +162,7 @@ def team_invite_request():
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.session.add(req)
|
db.session.add(req)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
db.session.close()
|
||||||
|
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
|
|
||||||
|
@ -190,6 +194,7 @@ def team_accept_invite():
|
||||||
if invitation2 is not None:
|
if invitation2 is not None:
|
||||||
db.session.delete(invitation2)
|
db.session.delete(invitation2)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
db.session.close()
|
||||||
|
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
|
|
||||||
|
@ -225,6 +230,7 @@ def team_accept_invite_request():
|
||||||
if invitation2 is not None:
|
if invitation2 is not None:
|
||||||
db.session.delete(invitation2)
|
db.session.delete(invitation2)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
db.session.close()
|
||||||
|
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
|
|
||||||
|
@ -280,7 +286,7 @@ TeamSchema = Schema({
|
||||||
([__check_teamname], "This teamname is taken, did you forget your password?")
|
([__check_teamname], "This teamname is taken, did you forget your password?")
|
||||||
),
|
),
|
||||||
Required("school"): check(
|
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."),
|
([utils.__check_ascii], "Please only use ASCII characters in your school name."),
|
||||||
),
|
),
|
||||||
}, extra=True)
|
}, extra=True)
|
||||||
|
|
|
@ -8,7 +8,7 @@ from decorators import api_wrapper, WebException
|
||||||
from schemas import verify_to_schema, check
|
from schemas import verify_to_schema, check
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import logger
|
import logger, logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
@ -94,7 +94,7 @@ def user_register():
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
utils.generate_identicon(email, user.uid)
|
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)
|
login_user(username, password)
|
||||||
|
|
||||||
return { "success": 1, "message": "Success!" }
|
return { "success": 1, "message": "Success!" }
|
||||||
|
|
|
@ -10,6 +10,7 @@ import config
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import traceback
|
||||||
|
|
||||||
from api.decorators import api_wrapper
|
from api.decorators import api_wrapper
|
||||||
|
|
||||||
|
@ -23,6 +24,8 @@ with app.app_context():
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
|
||||||
|
app.db = db
|
||||||
|
|
||||||
app.secret_key = config.SECRET_KEY
|
app.secret_key = config.SECRET_KEY
|
||||||
|
|
||||||
app.register_blueprint(api.admin.blueprint, url_prefix="/api/admin")
|
app.register_blueprint(api.admin.blueprint, url_prefix="/api/admin")
|
||||||
|
@ -39,27 +42,29 @@ def api_main():
|
||||||
|
|
||||||
def run(args):
|
def run(args):
|
||||||
with app.app_context():
|
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)
|
app.run(host="0.0.0.0", port=8000)
|
||||||
|
|
||||||
def load_problems(args):
|
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):
|
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
|
return
|
||||||
|
|
||||||
for (dirpath, dirnames, filenames) in os.walk(config.PROBLEM_DIR):
|
for (dirpath, dirnames, filenames) in os.walk(config.PROBLEM_DIR):
|
||||||
if "problem.json" in filenames:
|
if "problem.json" in filenames:
|
||||||
json_file = os.path.join(dirpath, "problem.json")
|
json_file = os.path.join(dirpath, "problem.json")
|
||||||
contents = open(json_file).read()
|
contents = open(json_file).read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = json.loads(contents)
|
data = json.loads(contents)
|
||||||
except ValueError as e:
|
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
|
continue
|
||||||
|
|
||||||
if not isinstance(data, dict):
|
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
|
continue
|
||||||
|
|
||||||
missing_keys = []
|
missing_keys = []
|
||||||
|
@ -67,24 +72,29 @@ def load_problems(args):
|
||||||
if key not in data:
|
if key not in data:
|
||||||
missing_keys.append(key)
|
missing_keys.append(key)
|
||||||
if len(missing_keys) > 0:
|
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
|
continue
|
||||||
|
|
||||||
relative_path = os.path.relpath(dirpath, config.PROBLEM_DIR)
|
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:
|
try:
|
||||||
api.problem.insert_problem(data)
|
api.problem.insert_problem(data, force=force)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.warning("Problem '{}' was not added to the database. Error: {}".format(data["title"], 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()))
|
||||||
|
|
||||||
if __name__ == "__main__":
|
api.logger.log("api.problem.log", "Finished.")
|
||||||
|
|
||||||
|
def main():
|
||||||
parser = ArgumentParser(description="EasyCTF Server Management")
|
parser = ArgumentParser(description="EasyCTF Server Management")
|
||||||
|
|
||||||
subparser = parser.add_subparsers(help="Select one of the following actions.")
|
subparser = parser.add_subparsers(help="Select one of the following actions.")
|
||||||
parser_problems = subparser.add_parser("problems", help="Manage problems.")
|
parser_problems = subparser.add_parser("problems", help="Manage problems.")
|
||||||
subparser_problems = parser_problems.add_subparsers(help="Select one of the following actions.")
|
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 = 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_problems_load.set_defaults(func=load_problems)
|
||||||
|
|
||||||
parser_run = subparser.add_parser("run", help="Run the server.")
|
parser_run = subparser.add_parser("run", help="Run the server.")
|
||||||
|
@ -92,10 +102,10 @@ if __name__ == "__main__":
|
||||||
parser_run.set_defaults(func=run)
|
parser_run.set_defaults(func=run)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
keyword_args, _ = dict(args._get_kwargs()), args._get_args()
|
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
|
||||||
|
|
||||||
if "func" in args:
|
if "func" in args:
|
||||||
args.func(args)
|
args.func(args)
|
||||||
else:
|
else:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
|
|
||||||
|
main()
|
|
@ -27,6 +27,7 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body ng-controller="mainController">
|
<body ng-controller="mainController">
|
||||||
|
<div id="site-message" style="margin: 0"></div>
|
||||||
<nav class="navbar navbar-default">
|
<nav class="navbar navbar-default">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
|
|
|
@ -75,10 +75,37 @@ app.config(function($routeProvider, $locationProvider) {
|
||||||
$locationProvider.html5Mode(true);
|
$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) {
|
app.controller("mainController", ["$scope", "$http", function($scope, $http) {
|
||||||
$scope.config = { navbar: { } };
|
$scope.config = { navbar: { } };
|
||||||
$scope.timestamp = Date.now();
|
$scope.timestamp = Date.now();
|
||||||
$.get("/api/user/status", function(result) {
|
api_call("GET", "/api/user/status", {}, function(result) {
|
||||||
if (result["success"] == 1) {
|
if (result["success"] == 1) {
|
||||||
delete result["success"];
|
delete result["success"];
|
||||||
$scope.config.navbar = result;
|
$scope.config.navbar = result;
|
||||||
|
@ -87,14 +114,15 @@ app.controller("mainController", ["$scope", "$http", function($scope, $http) {
|
||||||
$scope.config.navbar.logged_in = false;
|
$scope.config.navbar.logged_in = false;
|
||||||
}
|
}
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
}).fail(function() {
|
}, function() {
|
||||||
$scope.config.navbar.logged_in = false;
|
$scope.config.navbar.logged_in = false;
|
||||||
$scope.$apply();
|
$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() {
|
app.controller("logoutController", function() {
|
||||||
$.get("/api/user/logout", function(result) {
|
api_call("GET", "/api/user/logout", {}, function(result) {
|
||||||
location.href = "/";
|
location.href = "/";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -103,7 +131,7 @@ app.controller("profileController", ["$controller", "$scope", "$http", "$routePa
|
||||||
var data = { };
|
var data = { };
|
||||||
if ("username" in $routeParams) data["username"] = $routeParams["username"];
|
if ("username" in $routeParams) data["username"] = $routeParams["username"];
|
||||||
$controller("mainController", { $scope: $scope });
|
$controller("mainController", { $scope: $scope });
|
||||||
$.get("/api/user/info", data, function(result) {
|
api_call("GET", "/api/user/info", data, function(result) {
|
||||||
if (result["success"] == 1) {
|
if (result["success"] == 1) {
|
||||||
$scope.user = result["user"];
|
$scope.user = result["user"];
|
||||||
}
|
}
|
||||||
|
@ -129,7 +157,7 @@ app.controller("teamController", ["$controller", "$scope", "$http", "$routeParam
|
||||||
} else {
|
} else {
|
||||||
$controller("loginController", { $scope: $scope });
|
$controller("loginController", { $scope: $scope });
|
||||||
}
|
}
|
||||||
$.get("/api/team/info", data, function(result) {
|
api_call("GET", "/api/team/info", data, function(result) {
|
||||||
if (result["success"] == 1) {
|
if (result["success"] == 1) {
|
||||||
$scope.team = result["team"];
|
$scope.team = result["team"];
|
||||||
}
|
}
|
||||||
|
@ -155,7 +183,7 @@ app.controller("resetController", ["$controller", "$scope", "$http", "$routePara
|
||||||
if ("token" in $routeParams) {
|
if ("token" in $routeParams) {
|
||||||
$scope.token = true;
|
$scope.token = true;
|
||||||
token = $routeParams["token"];
|
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.body = data["message"];
|
||||||
$scope.success = data["success"]
|
$scope.success = data["success"]
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
|
@ -181,9 +209,11 @@ app.controller("adminController", ["$controller", "$scope", "$http", function($c
|
||||||
|
|
||||||
app.controller("adminProblemsController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
app.controller("adminProblemsController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||||
$controller("adminController", { $scope: $scope });
|
$controller("adminController", { $scope: $scope });
|
||||||
$.get("/api/admin/problems/list", function(result) {
|
api_call("GET", "/api/admin/problems/list", {}, function(result) {
|
||||||
if (result["success"] == 1) {
|
if (result["success"] == 1) {
|
||||||
$scope.problems = result["problems"];
|
$scope.problems = result["problems"];
|
||||||
|
} else {
|
||||||
|
$scope.problems = [];
|
||||||
}
|
}
|
||||||
$scope.$apply();
|
$scope.$apply();
|
||||||
});
|
});
|
||||||
|
@ -191,7 +221,7 @@ app.controller("adminProblemsController", ["$controller", "$scope", "$http", fun
|
||||||
|
|
||||||
app.controller("settingsController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
app.controller("settingsController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||||
$controller("loginController", { $scope: $scope });
|
$controller("loginController", { $scope: $scope });
|
||||||
$.get("/api/user/info", {}, function(result) {
|
api_call("GET", "/api/user/info", {}, function(result) {
|
||||||
if (result["success"] == 1) {
|
if (result["success"] == 1) {
|
||||||
$scope.user = result["user"];
|
$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() {
|
$.fn.serializeObject = function() {
|
||||||
var a, o;
|
var a, o;
|
||||||
o = {};
|
o = {};
|
||||||
|
|
|
@ -12,147 +12,38 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<div class="row">
|
||||||
<li role="presentation" class="active"><a href="#problems" aria-controls="problems" role="tab" data-toggle="tab">Problems</a></li>
|
<div class="tabbable">
|
||||||
<li role="presentation"><a href="#filesystem" aria-controls="filesystem" role="tab" data-toggle="tab">Filesystem</a></li>
|
<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>
|
</ul>
|
||||||
<div class="tab-content">
|
<div class="tab-content col-md-9">
|
||||||
<div role="tabpanel" class="tab-pane active" id="problems">
|
<div class="tab-pane active" id="new">
|
||||||
<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="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading" role="tab" id="heading-new">
|
<div class="panel-heading">
|
||||||
<h4 class="panel-title">
|
<h4 class="panel-title">New Problem</h4>
|
||||||
<i class="fa fa-fw fa-plus"></i> Create New Problem
|
|
||||||
</h4>
|
|
||||||
</div>
|
</div>
|
||||||
<div id="collapse-new" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="heading-new">
|
|
||||||
<div class="panel-body">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="tab-pane" ng-repeat="problem in problems" id="problem_{{ problem['pid'] }}">
|
||||||
<div class="col-sm-12 form-group">
|
<div class="panel panel-default">
|
||||||
<label class="col-sm-12" for="category"><small>Category</small></label>
|
<div class="panel-heading">
|
||||||
<div class="col-sm-12">
|
<h4 class="panel-title">{{ problem["title"] }}</h4>
|
||||||
<select name="category" class="selectpicker" data-width="100%">
|
</div>
|
||||||
<option value="Algorithm">Algorithm</option>
|
<div class="panel-body">
|
||||||
<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>
|
||||||
</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>
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-12 col-md-9">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$("ul[role=tablist]").tab();
|
/*$(".selectpicker").selectpicker();
|
||||||
$("a[role=tab]").click(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
});
|
|
||||||
$(".selectpicker").selectpicker();
|
|
||||||
var config = {
|
var config = {
|
||||||
toolbar: [
|
toolbar: [
|
||||||
{ name: "basicstyles", items: [ "Bold", "Italic", "Underline" ] },
|
{ name: "basicstyles", items: [ "Bold", "Italic", "Underline" ] },
|
||||||
|
@ -175,6 +66,6 @@
|
||||||
}).load();
|
}).load();
|
||||||
var new_grader = ace.edit("new_grader");
|
var new_grader = ace.edit("new_grader");
|
||||||
new_grader.setTheme("ace/theme/tomorrow");
|
new_grader.setTheme("ace/theme/tomorrow");
|
||||||
new_grader.getSession().setMode("ace/mode/python");
|
new_grader.getSession().setMode("ace/mode/python");*/
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
|
@ -33,7 +33,7 @@
|
||||||
<i class="fa fa-fw fa-flag"></i>
|
<i class="fa fa-fw fa-flag"></i>
|
||||||
I'm in the team!
|
I'm in the team!
|
||||||
</div>
|
</div>
|
||||||
<div class="label label-warning">
|
<div class="label label-warning" ng-show="team['observer']==true">
|
||||||
<i class="fa fa-fw fa-globe"></i>
|
<i class="fa fa-fw fa-globe"></i>
|
||||||
OBSERVER
|
OBSERVER
|
||||||
</div>
|
</div>
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
<h1><span class="padded">{{ team['teamname'] }}</span></h1>
|
<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>
|
<h4><i class="fa fa-fw fa-university"></i> <span class="padded">{{ team['school'] || 'Unknown Affiliation' }}</span></h4>
|
||||||
<div class="row">
|
<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>
|
<i class="fa fa-fw fa-globe"></i>
|
||||||
OBSERVER
|
OBSERVER
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue