mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-17 05:54:19 +01:00
General fixes and PEP8 enforcement (#258)
* Fixing index page links when you deploy on a subdirectory * Updating travis for pep8 * autopep8 with just formatting changes
This commit is contained in:
@@ -5,4 +5,5 @@ python:
|
||||
install:
|
||||
- pip install -r development.txt
|
||||
script:
|
||||
- pep8 --ignore E501,E712 CTFd/ tests/
|
||||
- nosetests
|
||||
|
||||
@@ -13,6 +13,7 @@ from CTFd import utils
|
||||
|
||||
__version__ = '1.0.2'
|
||||
|
||||
|
||||
class ThemeLoader(FileSystemLoader):
|
||||
def get_source(self, environment, template):
|
||||
if template.startswith('admin/'):
|
||||
@@ -34,21 +35,21 @@ def create_app(config='CTFd.config.Config'):
|
||||
if url.drivername == 'postgres':
|
||||
url.drivername = 'postgresql'
|
||||
|
||||
## Creates database if the database database does not exist
|
||||
# Creates database if the database database does not exist
|
||||
if not database_exists(url):
|
||||
create_database(url)
|
||||
|
||||
## Register database
|
||||
# Register database
|
||||
db.init_app(app)
|
||||
|
||||
## Register Flask-Migrate
|
||||
# Register Flask-Migrate
|
||||
migrate.init_app(app, db)
|
||||
|
||||
## This creates tables instead of db.create_all()
|
||||
## Allows migrations to happen properly
|
||||
# This creates tables instead of db.create_all()
|
||||
# Allows migrations to happen properly
|
||||
migrate_upgrade()
|
||||
|
||||
## Alembic sqlite support is lacking so we should just create_all anyway
|
||||
# Alembic sqlite support is lacking so we should just create_all anyway
|
||||
if url.drivername.startswith('sqlite'):
|
||||
db.create_all()
|
||||
|
||||
@@ -59,10 +60,10 @@ def create_app(config='CTFd.config.Config'):
|
||||
|
||||
version = utils.get_config('ctf_version')
|
||||
|
||||
if not version: ## Upgrading from an unversioned CTFd
|
||||
if not version: # Upgrading from an unversioned CTFd
|
||||
utils.set_config('ctf_version', __version__)
|
||||
|
||||
if version and (StrictVersion(version) < StrictVersion(__version__)): ## Upgrading from an older version of CTFd
|
||||
if version and (StrictVersion(version) < StrictVersion(__version__)): # Upgrading from an older version of CTFd
|
||||
print("/*\\ CTFd has updated and must update the database! /*\\")
|
||||
print("/*\\ Please backup your database before proceeding! /*\\")
|
||||
print("/*\\ CTFd maintainers are not responsible for any data loss! /*\\")
|
||||
|
||||
@@ -142,12 +142,12 @@ def admin_hints(hintid):
|
||||
db.session.add(hint)
|
||||
db.session.commit()
|
||||
json_data = {
|
||||
'hint': hint.hint,
|
||||
'type': hint.type,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost,
|
||||
'id': hint.id
|
||||
}
|
||||
'hint': hint.hint,
|
||||
'type': hint.type,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost,
|
||||
'id': hint.id
|
||||
}
|
||||
db.session.close()
|
||||
return jsonify(json_data)
|
||||
|
||||
@@ -192,7 +192,7 @@ def admin_get_values(chalid, prop):
|
||||
'key': x.flag,
|
||||
'type': x.key_type,
|
||||
'type_name': get_key_class(x.key_type).name
|
||||
})
|
||||
})
|
||||
return jsonify(json_data)
|
||||
elif prop == 'tags':
|
||||
tags = Tags.query.filter_by(chal=chalid).all()
|
||||
@@ -202,19 +202,19 @@ def admin_get_values(chalid, prop):
|
||||
'id': x.id,
|
||||
'chal': x.chal,
|
||||
'tag': x.tag
|
||||
})
|
||||
})
|
||||
return jsonify(json_data)
|
||||
elif prop == 'hints':
|
||||
hints = Hints.query.filter_by(chal=chalid)
|
||||
json_data = {'hints': []}
|
||||
for hint in hints:
|
||||
json_data['hints'].append({
|
||||
'hint': hint.hint,
|
||||
'type': hint.type,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost,
|
||||
'id': hint.id
|
||||
})
|
||||
'hint': hint.hint,
|
||||
'type': hint.type,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost,
|
||||
'id': hint.id
|
||||
})
|
||||
return jsonify(json_data)
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from CTFd import utils
|
||||
|
||||
admin_containers = Blueprint('admin_containers', __name__)
|
||||
|
||||
|
||||
@admin_containers.route('/admin/containers', methods=['GET'])
|
||||
@admins_only
|
||||
def list_container():
|
||||
|
||||
@@ -7,6 +7,7 @@ from CTFd import utils
|
||||
|
||||
admin_keys = Blueprint('admin_keys', __name__)
|
||||
|
||||
|
||||
@admin_keys.route('/admin/key_types', methods=['GET'])
|
||||
@admins_only
|
||||
def admin_key_types():
|
||||
@@ -16,6 +17,7 @@ def admin_key_types():
|
||||
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@admin_keys.route('/admin/keys', defaults={'keyid': None}, methods=['POST', 'GET'])
|
||||
@admin_keys.route('/admin/keys/<int:keyid>', methods=['POST', 'GET'])
|
||||
@admins_only
|
||||
@@ -53,7 +55,6 @@ def admin_keys_view(keyid):
|
||||
return '1'
|
||||
|
||||
|
||||
|
||||
@admin_keys.route('/admin/keys/<int:keyid>/delete', methods=['POST'])
|
||||
@admins_only
|
||||
def admin_delete_keys(keyid):
|
||||
|
||||
@@ -6,6 +6,7 @@ from CTFd import utils
|
||||
|
||||
admin_pages = Blueprint('admin_pages', __name__)
|
||||
|
||||
|
||||
@admin_pages.route('/admin/css', methods=['GET', 'POST'])
|
||||
@admins_only
|
||||
def admin_css():
|
||||
|
||||
@@ -7,6 +7,7 @@ from CTFd import utils
|
||||
|
||||
admin_scoreboard = Blueprint('admin_scoreboard', __name__)
|
||||
|
||||
|
||||
@admin_scoreboard.route('/admin/scoreboard')
|
||||
@admins_only
|
||||
def admin_scoreboard_view():
|
||||
|
||||
@@ -6,11 +6,13 @@ from CTFd import utils
|
||||
|
||||
admin_statistics = Blueprint('admin_statistics', __name__)
|
||||
|
||||
|
||||
@admin_statistics.route('/admin/graphs')
|
||||
@admins_only
|
||||
def admin_graphs():
|
||||
return render_template('admin/graphs.html')
|
||||
|
||||
|
||||
@admin_statistics.route('/admin/graphs/<graph_type>')
|
||||
@admins_only
|
||||
def admin_graph(graph_type):
|
||||
@@ -31,6 +33,7 @@ def admin_graph(graph_type):
|
||||
json_data[name] = count
|
||||
return jsonify(json_data)
|
||||
|
||||
|
||||
@admin_statistics.route('/admin/statistics', methods=['GET'])
|
||||
@admins_only
|
||||
def admin_stats():
|
||||
@@ -66,6 +69,7 @@ def admin_stats():
|
||||
most_solved=most_solved,
|
||||
least_solved=least_solved)
|
||||
|
||||
|
||||
@admin_statistics.route('/admin/wrong_keys', defaults={'page': '1'}, methods=['GET'])
|
||||
@admin_statistics.route('/admin/wrong_keys/<int:page>', methods=['GET'])
|
||||
@admins_only
|
||||
@@ -77,11 +81,11 @@ def admin_wrong_key(page):
|
||||
|
||||
wrong_keys = WrongKeys.query.add_columns(WrongKeys.id, WrongKeys.chalid, WrongKeys.flag, WrongKeys.teamid, WrongKeys.date,
|
||||
Challenges.name.label('chal_name'), Teams.name.label('team_name')) \
|
||||
.join(Challenges) \
|
||||
.join(Teams) \
|
||||
.order_by(WrongKeys.date.desc()) \
|
||||
.slice(page_start, page_end) \
|
||||
.all()
|
||||
.join(Challenges) \
|
||||
.join(Teams) \
|
||||
.order_by(WrongKeys.date.desc()) \
|
||||
.slice(page_start, page_end) \
|
||||
.all()
|
||||
|
||||
wrong_count = db.session.query(db.func.count(WrongKeys.id)).first()[0]
|
||||
pages = int(wrong_count / results_per_page) + (wrong_count % results_per_page > 0)
|
||||
@@ -100,11 +104,11 @@ def admin_correct_key(page):
|
||||
|
||||
solves = Solves.query.add_columns(Solves.id, Solves.chalid, Solves.teamid, Solves.date, Solves.flag,
|
||||
Challenges.name.label('chal_name'), Teams.name.label('team_name')) \
|
||||
.join(Challenges) \
|
||||
.join(Teams) \
|
||||
.order_by(Solves.date.desc()) \
|
||||
.slice(page_start, page_end) \
|
||||
.all()
|
||||
.join(Challenges) \
|
||||
.join(Teams) \
|
||||
.order_by(Solves.date.desc()) \
|
||||
.slice(page_start, page_end) \
|
||||
.all()
|
||||
|
||||
solve_count = db.session.query(db.func.count(Solves.id)).first()[0]
|
||||
pages = int(solve_count / results_per_page) + (solve_count % results_per_page > 0)
|
||||
|
||||
@@ -8,6 +8,7 @@ from CTFd import utils
|
||||
|
||||
admin_teams = Blueprint('admin_teams', __name__)
|
||||
|
||||
|
||||
@admin_teams.route('/admin/teams', defaults={'page': '1'})
|
||||
@admin_teams.route('/admin/teams/<int:page>')
|
||||
@admins_only
|
||||
@@ -225,6 +226,7 @@ def delete_wrong_key(keyid):
|
||||
db.session.close()
|
||||
return '1'
|
||||
|
||||
|
||||
@admin_teams.route('/admin/awards/<int:award_id>/delete', methods=['POST'])
|
||||
@admins_only
|
||||
def delete_award(award_id):
|
||||
@@ -234,6 +236,7 @@ def delete_award(award_id):
|
||||
db.session.close()
|
||||
return '1'
|
||||
|
||||
|
||||
@admin_teams.route('/admin/teams/<int:teamid>/awards', methods=['GET'])
|
||||
@admins_only
|
||||
def admin_awards(teamid):
|
||||
|
||||
16
CTFd/auth.py
16
CTFd/auth.py
@@ -19,7 +19,7 @@ auth = Blueprint('auth', __name__)
|
||||
def confirm_user(data=None):
|
||||
if not utils.get_config('verify_emails'):
|
||||
return redirect(url_for('challenges.challenges_view'))
|
||||
if data and request.method == "GET": # User is confirming email account
|
||||
if data and request.method == "GET": # User is confirming email account
|
||||
try:
|
||||
s = Signer(app.config['SECRET_KEY'])
|
||||
email = s.unsign(urllib.unquote_plus(data.decode('base64')))
|
||||
@@ -36,7 +36,7 @@ def confirm_user(data=None):
|
||||
if utils.authed():
|
||||
return redirect(url_for('challenges.challenges_view'))
|
||||
return redirect(url_for('auth.login'))
|
||||
if not data and request.method == "GET": # User has been directed to the confirm page because his account is not verified
|
||||
if not data and request.method == "GET": # User has been directed to the confirm page because his account is not verified
|
||||
if not utils.authed():
|
||||
return redirect(url_for('auth.login'))
|
||||
team = Teams.query.filter_by(id=session['id']).first_or_404()
|
||||
@@ -130,15 +130,15 @@ def register():
|
||||
session['admin'] = team.admin
|
||||
session['nonce'] = utils.sha512(os.urandom(10))
|
||||
|
||||
if utils.can_send_mail() and utils.get_config('verify_emails'): # Confirming users is enabled and we can send email.
|
||||
if utils.can_send_mail() and utils.get_config('verify_emails'): # Confirming users is enabled and we can send email.
|
||||
db.session.close()
|
||||
logger = logging.getLogger('regs')
|
||||
logger.warn("[{0}] {1} registered (UNCONFIRMED) with {2}".format(time.strftime("%m/%d/%Y %X"),
|
||||
request.form['name'].encode('utf-8'),
|
||||
request.form['email'].encode('utf-8')))
|
||||
return redirect(url_for('auth.confirm_user'))
|
||||
else: # Don't care about confirming users
|
||||
if utils.can_send_mail(): # We want to notify the user that they have registered.
|
||||
else: # Don't care about confirming users
|
||||
if utils.can_send_mail(): # We want to notify the user that they have registered.
|
||||
utils.sendmail(request.form['email'], "You've successfully registered for {}".format(utils.get_config('ctf_name')))
|
||||
|
||||
db.session.close()
|
||||
@@ -159,9 +159,9 @@ def login():
|
||||
if team:
|
||||
if team and bcrypt_sha256.verify(request.form['password'], team.password):
|
||||
try:
|
||||
session.regenerate() # NO SESSION FIXATION FOR YOU
|
||||
session.regenerate() # NO SESSION FIXATION FOR YOU
|
||||
except:
|
||||
pass # TODO: Some session objects don't implement regenerate :(
|
||||
pass # TODO: Some session objects don't implement regenerate :(
|
||||
session['username'] = team.name
|
||||
session['id'] = team.id
|
||||
session['admin'] = team.admin
|
||||
@@ -174,7 +174,7 @@ def login():
|
||||
if request.args.get('next') and utils.is_safe_url(request.args.get('next')):
|
||||
return redirect(request.args.get('next'))
|
||||
return redirect(url_for('challenges.challenges_view'))
|
||||
else: # This user exists but the password is wrong
|
||||
else: # This user exists but the password is wrong
|
||||
errors.append("Your username or password is incorrect")
|
||||
db.session.close()
|
||||
return render_template('login.html', errors=errors)
|
||||
|
||||
@@ -14,6 +14,7 @@ from CTFd import utils
|
||||
|
||||
challenges = Blueprint('challenges', __name__)
|
||||
|
||||
|
||||
@challenges.route('/hints/<int:hintid>', methods=['GET', 'POST'])
|
||||
def hints_view(hintid):
|
||||
hint = Hints.query.filter_by(id=hintid).first_or_404()
|
||||
@@ -22,15 +23,15 @@ def hints_view(hintid):
|
||||
if request.method == 'GET':
|
||||
if unlock:
|
||||
return jsonify({
|
||||
'hint': hint.hint,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost
|
||||
})
|
||||
'hint': hint.hint,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost
|
||||
})
|
||||
else:
|
||||
return jsonify({
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost
|
||||
})
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost
|
||||
})
|
||||
elif request.method == 'POST':
|
||||
if not unlock:
|
||||
team = Teams.query.filter_by(id=session['id']).first()
|
||||
@@ -42,18 +43,18 @@ def hints_view(hintid):
|
||||
db.session.add(award)
|
||||
db.session.commit()
|
||||
json_data = {
|
||||
'hint': hint.hint,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost
|
||||
}
|
||||
'hint': hint.hint,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost
|
||||
}
|
||||
db.session.close()
|
||||
return jsonify(json_data)
|
||||
else:
|
||||
json_data = {
|
||||
'hint': hint.hint,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost
|
||||
}
|
||||
'hint': hint.hint,
|
||||
'chal': hint.chal,
|
||||
'cost': hint.cost
|
||||
}
|
||||
db.session.close()
|
||||
return jsonify(json_data)
|
||||
|
||||
@@ -63,10 +64,10 @@ def challenges_view():
|
||||
errors = []
|
||||
start = utils.get_config('start') or 0
|
||||
end = utils.get_config('end') or 0
|
||||
if not utils.is_admin(): # User is not an admin
|
||||
if not utils.is_admin(): # User is not an admin
|
||||
if not utils.ctftime():
|
||||
# It is not CTF time
|
||||
if utils.view_after_ctf(): # But we are allowed to view after the CTF ends
|
||||
if utils.view_after_ctf(): # But we are allowed to view after the CTF ends
|
||||
pass
|
||||
else: # We are NOT allowed to view after the CTF ends
|
||||
if utils.get_config('start') and not utils.ctf_started():
|
||||
@@ -74,9 +75,9 @@ def challenges_view():
|
||||
if (utils.get_config('end') and utils.ctf_ended()) and not utils.view_after_ctf():
|
||||
errors.append('{} has ended'.format(utils.ctf_name()))
|
||||
return render_template('chals.html', errors=errors, start=int(start), end=int(end))
|
||||
if utils.get_config('verify_emails') and not utils.is_verified(): # User is not confirmed
|
||||
if utils.get_config('verify_emails') and not utils.is_verified(): # User is not confirmed
|
||||
return redirect(url_for('auth.confirm_user'))
|
||||
if utils.user_can_view_challenges(): # Do we allow unauthenticated users?
|
||||
if utils.user_can_view_challenges(): # Do we allow unauthenticated users?
|
||||
if utils.get_config('start') and not utils.ctf_started():
|
||||
errors.append('{} has not started yet'.format(utils.ctf_name()))
|
||||
if (utils.get_config('end') and utils.ctf_ended()) and not utils.view_after_ctf():
|
||||
@@ -104,9 +105,9 @@ def chals():
|
||||
hints = []
|
||||
for hint in Hints.query.filter_by(chal=x.id).all():
|
||||
if hint.id in unlocked_hints:
|
||||
hints.append({'id':hint.id, 'cost':hint.cost, 'hint':hint.hint})
|
||||
hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint})
|
||||
else:
|
||||
hints.append({'id':hint.id, 'cost':hint.cost})
|
||||
hints.append({'id': hint.id, 'cost': hint.cost})
|
||||
# hints = [{'id':hint.id, 'cost':hint.cost} for hint in Hints.query.filter_by(chal=x.id).all()]
|
||||
chal_type = get_chal_class(x.type)
|
||||
json['game'].append({
|
||||
@@ -293,7 +294,7 @@ def chal(chalid):
|
||||
logger.info("[{0}] {1} submitted {2} with kpm {3} [WRONG]".format(*data))
|
||||
# return '0' # key was wrong
|
||||
if max_tries:
|
||||
attempts_left = max_tries - fails - 1 ## Off by one since fails has changed since it was gotten
|
||||
attempts_left = max_tries - fails - 1 # Off by one since fails has changed since it was gotten
|
||||
tries_str = 'tries'
|
||||
if attempts_left == 1:
|
||||
tries_str = 'try'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
##### GENERATE SECRET KEY #####
|
||||
''' GENERATE SECRET KEY '''
|
||||
|
||||
with open('.ctfd_secret_key', 'a+b') as secret:
|
||||
secret.seek(0) # Seek to beginning of file since a+ mode leaves you at the end and w+ deletes the file
|
||||
@@ -10,7 +10,8 @@ with open('.ctfd_secret_key', 'a+b') as secret:
|
||||
secret.write(key)
|
||||
secret.flush()
|
||||
|
||||
##### SERVER SETTINGS #####
|
||||
''' SERVER SETTINGS '''
|
||||
|
||||
|
||||
class Config(object):
|
||||
'''
|
||||
@@ -25,7 +26,6 @@ class Config(object):
|
||||
'''
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY') or key
|
||||
|
||||
|
||||
'''
|
||||
SQLALCHEMY_DATABASE_URI is the URI that specifies the username, password, hostname, port, and database of the server
|
||||
used to hold the CTFd database.
|
||||
@@ -34,52 +34,44 @@ class Config(object):
|
||||
'''
|
||||
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///{}/ctfd.db'.format(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
'''
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS is automatically disabled to suppress warnings and save memory. You should only enable
|
||||
this if you need it.
|
||||
'''
|
||||
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||
|
||||
|
||||
'''
|
||||
SESSION_TYPE is a configuration value used for Flask-Session. It is currently unused in CTFd.
|
||||
http://pythonhosted.org/Flask-Session/#configuration
|
||||
'''
|
||||
SESSION_TYPE = "filesystem"
|
||||
|
||||
|
||||
'''
|
||||
SESSION_FILE_DIR is a configuration value used for Flask-Session. It is currently unused in CTFd.
|
||||
http://pythonhosted.org/Flask-Session/#configuration
|
||||
'''
|
||||
SESSION_FILE_DIR = "/tmp/flask_session"
|
||||
|
||||
|
||||
'''
|
||||
SESSION_COOKIE_HTTPONLY controls if cookies should be set with the HttpOnly flag.
|
||||
'''
|
||||
SESSION_COOKIE_HTTPONLY = True
|
||||
|
||||
|
||||
'''
|
||||
PERMANENT_SESSION_LIFETIME is the lifetime of a session.
|
||||
'''
|
||||
PERMANENT_SESSION_LIFETIME = 604800 # 7 days in seconds
|
||||
|
||||
PERMANENT_SESSION_LIFETIME = 604800 # 7 days in seconds
|
||||
|
||||
'''
|
||||
HOST specifies the hostname where the CTFd instance will exist. It is currently unused.
|
||||
'''
|
||||
HOST = ".ctfd.io"
|
||||
|
||||
|
||||
'''
|
||||
MAILFROM_ADDR is the email address that emails are sent from if not overridden in the configuration panel.
|
||||
'''
|
||||
MAILFROM_ADDR = "noreply@ctfd.io"
|
||||
|
||||
|
||||
'''
|
||||
UPLOAD_FOLDER is the location where files are uploaded.
|
||||
The default destination is the CTFd/uploads folder. If you need Amazon S3 files
|
||||
@@ -87,14 +79,12 @@ class Config(object):
|
||||
'''
|
||||
UPLOAD_FOLDER = os.path.join(os.path.dirname(__file__), 'uploads')
|
||||
|
||||
|
||||
'''
|
||||
TEMPLATES_AUTO_RELOAD specifies whether Flask should check for modifications to templates and
|
||||
reload them automatically
|
||||
'''
|
||||
TEMPLATES_AUTO_RELOAD = True
|
||||
|
||||
|
||||
'''
|
||||
TRUSTED_PROXIES defines a set of regular expressions used for finding a user's IP address if the CTFd instance
|
||||
is behind a proxy. If you are running a CTF and users are on the same network as you, you may choose to remove
|
||||
@@ -114,7 +104,6 @@ class Config(object):
|
||||
'^192\.168\.'
|
||||
]
|
||||
|
||||
|
||||
'''
|
||||
CACHE_TYPE specifies how CTFd should cache configuration values. If CACHE_TYPE is set to 'redis', CTFd will make use
|
||||
of the REDIS_URL specified in environment variables. You can also choose to hardcode the REDIS_URL here.
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
from CTFd.plugins.keys import get_key_class
|
||||
from CTFd.models import db, Keys
|
||||
|
||||
|
||||
class BaseChallenge(object):
|
||||
id = None
|
||||
name = None
|
||||
|
||||
|
||||
class CTFdStandardChallenge(BaseChallenge):
|
||||
id = 0
|
||||
name = "standard"
|
||||
@@ -19,9 +21,10 @@ class CTFdStandardChallenge(BaseChallenge):
|
||||
|
||||
|
||||
CHALLENGE_CLASSES = {
|
||||
0 : CTFdStandardChallenge
|
||||
0: CTFdStandardChallenge
|
||||
}
|
||||
|
||||
|
||||
def get_chal_class(class_id):
|
||||
cls = CHALLENGE_CLASSES.get(class_id)
|
||||
if cls is None:
|
||||
|
||||
@@ -11,6 +11,7 @@ class BaseKey(object):
|
||||
def compare(self, saved, provided):
|
||||
return True
|
||||
|
||||
|
||||
class CTFdStaticKey(BaseKey):
|
||||
id = 0
|
||||
name = "static"
|
||||
@@ -36,8 +37,8 @@ class CTFdRegexKey(BaseKey):
|
||||
|
||||
|
||||
KEY_CLASSES = {
|
||||
0 : CTFdStaticKey,
|
||||
1 : CTFdRegexKey
|
||||
0: CTFdStaticKey,
|
||||
1: CTFdRegexKey
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,16 +10,16 @@ scoreboard = Blueprint('scoreboard', __name__)
|
||||
|
||||
def get_standings(admin=False, count=None):
|
||||
scores = db.session.query(
|
||||
Solves.teamid.label('teamid'),
|
||||
db.func.sum(Challenges.value).label('score'),
|
||||
db.func.max(Solves.date).label('date')
|
||||
).join(Challenges).group_by(Solves.teamid)
|
||||
Solves.teamid.label('teamid'),
|
||||
db.func.sum(Challenges.value).label('score'),
|
||||
db.func.max(Solves.date).label('date')
|
||||
).join(Challenges).group_by(Solves.teamid)
|
||||
|
||||
awards = db.session.query(
|
||||
Awards.teamid.label('teamid'),
|
||||
db.func.sum(Awards.value).label('score'),
|
||||
db.func.max(Awards.date).label('date')
|
||||
).group_by(Awards.teamid)
|
||||
Awards.teamid.label('teamid'),
|
||||
db.func.sum(Awards.value).label('score'),
|
||||
db.func.max(Awards.date).label('date')
|
||||
).group_by(Awards.teamid)
|
||||
|
||||
freeze = utils.get_config('freeze')
|
||||
if not admin and freeze:
|
||||
@@ -29,28 +29,28 @@ def get_standings(admin=False, count=None):
|
||||
results = union_all(scores, awards).alias('results')
|
||||
|
||||
sumscores = db.session.query(
|
||||
results.columns.teamid,
|
||||
db.func.sum(results.columns.score).label('score'),
|
||||
db.func.max(results.columns.date).label('date')
|
||||
).group_by(results.columns.teamid).subquery()
|
||||
results.columns.teamid,
|
||||
db.func.sum(results.columns.score).label('score'),
|
||||
db.func.max(results.columns.date).label('date')
|
||||
).group_by(results.columns.teamid).subquery()
|
||||
|
||||
if admin:
|
||||
standings_query = db.session.query(
|
||||
Teams.id.label('teamid'),
|
||||
Teams.name.label('name'),
|
||||
Teams.banned, sumscores.columns.score
|
||||
)\
|
||||
.join(sumscores, Teams.id == sumscores.columns.teamid) \
|
||||
.order_by(sumscores.columns.score.desc(), sumscores.columns.date)
|
||||
Teams.id.label('teamid'),
|
||||
Teams.name.label('name'),
|
||||
Teams.banned, sumscores.columns.score
|
||||
)\
|
||||
.join(sumscores, Teams.id == sumscores.columns.teamid) \
|
||||
.order_by(sumscores.columns.score.desc(), sumscores.columns.date)
|
||||
else:
|
||||
standings_query = db.session.query(
|
||||
Teams.id.label('teamid'),
|
||||
Teams.name.label('name'),
|
||||
sumscores.columns.score
|
||||
)\
|
||||
.join(sumscores, Teams.id == sumscores.columns.teamid) \
|
||||
.filter(Teams.banned == False) \
|
||||
.order_by(sumscores.columns.score.desc(), sumscores.columns.date)
|
||||
Teams.id.label('teamid'),
|
||||
Teams.name.label('name'),
|
||||
sumscores.columns.score
|
||||
)\
|
||||
.join(sumscores, Teams.id == sumscores.columns.teamid) \
|
||||
.filter(Teams.banned == False) \
|
||||
.order_by(sumscores.columns.score.desc(), sumscores.columns.date)
|
||||
|
||||
if count is None:
|
||||
standings = standings_query.all()
|
||||
@@ -102,7 +102,6 @@ def topteams(count):
|
||||
solves = Solves.query.filter_by(teamid=team.teamid)
|
||||
awards = Awards.query.filter_by(teamid=team.teamid)
|
||||
|
||||
|
||||
freeze = utils.get_config('freeze')
|
||||
|
||||
if freeze:
|
||||
@@ -112,7 +111,6 @@ def topteams(count):
|
||||
solves = solves.all()
|
||||
awards = awards.all()
|
||||
|
||||
|
||||
json['scores'][team.name] = []
|
||||
for x in solves:
|
||||
json['scores'][team.name].append({
|
||||
|
||||
@@ -226,7 +226,6 @@ def is_scoreboard_frozen():
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def ctftime():
|
||||
""" Checks whether it's CTF time or not. """
|
||||
|
||||
@@ -308,7 +307,7 @@ def get_ip():
|
||||
combined = "(" + ")|(".join(trusted_proxies) + ")"
|
||||
route = request.access_route + [request.remote_addr]
|
||||
for addr in reversed(route):
|
||||
if not re.match(combined, addr): # IP is not trusted but we trust the proxies
|
||||
if not re.match(combined, addr): # IP is not trusted but we trust the proxies
|
||||
remote_addr = addr
|
||||
break
|
||||
else:
|
||||
@@ -316,7 +315,7 @@ def get_ip():
|
||||
return remote_addr
|
||||
|
||||
|
||||
def get_kpm(teamid): # keys per minute
|
||||
def get_kpm(teamid): # keys per minute
|
||||
one_min_ago = datetime.datetime.utcnow() + datetime.timedelta(minutes=-1)
|
||||
return len(db.session.query(WrongKeys).filter(WrongKeys.teamid == teamid, WrongKeys.date >= one_min_ago).all())
|
||||
|
||||
@@ -355,7 +354,7 @@ def upload_file(file, chalid):
|
||||
def delete_file(file_id):
|
||||
f = Files.query.filter_by(id=file_id).first_or_404()
|
||||
upload_folder = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'])
|
||||
if os.path.exists(os.path.join(upload_folder, f.location)): # Some kind of os.path.isfile issue on Windows...
|
||||
if os.path.exists(os.path.join(upload_folder, f.location)): # Some kind of os.path.isfile issue on Windows...
|
||||
os.unlink(os.path.join(upload_folder, f.location))
|
||||
db.session.delete(f)
|
||||
db.session.commit()
|
||||
@@ -669,7 +668,7 @@ def export_ctf(segments=None):
|
||||
]
|
||||
}
|
||||
|
||||
## Backup database
|
||||
# Backup database
|
||||
backup = io.BytesIO()
|
||||
backup_zip = zipfile.ZipFile(backup, 'w')
|
||||
|
||||
@@ -682,7 +681,7 @@ def export_ctf(segments=None):
|
||||
result_file.seek(0)
|
||||
backup_zip.writestr('db/{}.json'.format(item), result_file.read())
|
||||
|
||||
## Backup uploads
|
||||
# Backup uploads
|
||||
upload_folder = os.path.join(os.path.normpath(app.root_path), get_config('UPLOAD_FOLDER'))
|
||||
for root, dirs, files in os.walk(upload_folder):
|
||||
for file in files:
|
||||
@@ -730,7 +729,7 @@ def import_ctf(backup, segments=None, erase=False):
|
||||
]
|
||||
}
|
||||
|
||||
## Need special handling of metadata
|
||||
# Need special handling of metadata
|
||||
if 'metadata' in segments:
|
||||
meta = groups['metadata']
|
||||
segments.remove('metadata')
|
||||
@@ -741,7 +740,7 @@ def import_ctf(backup, segments=None, erase=False):
|
||||
path = "db/{}.json".format(item)
|
||||
data = backup.open(path).read()
|
||||
|
||||
## Some JSON files will be empty
|
||||
# Some JSON files will be empty
|
||||
if data:
|
||||
if item == 'config':
|
||||
saved = json.loads(data)
|
||||
@@ -772,11 +771,10 @@ def import_ctf(backup, segments=None, erase=False):
|
||||
if container:
|
||||
container.buildfile = buildfile
|
||||
else:
|
||||
container = Containers(name, buildfile)
|
||||
container = Containers(name, buildfile)
|
||||
db.session.add(container)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
for segment in segments:
|
||||
group = groups[segment]
|
||||
for item in group:
|
||||
@@ -791,20 +789,20 @@ def import_ctf(backup, segments=None, erase=False):
|
||||
else:
|
||||
continue
|
||||
|
||||
## Extracting files
|
||||
# Extracting files
|
||||
files = [f for f in backup.namelist() if f.startswith('uploads/')]
|
||||
upload_folder = app.config.get('UPLOAD_FOLDER')
|
||||
for f in files:
|
||||
filename = f.split(os.sep, 1)
|
||||
|
||||
if len(filename) < 2: ## just an empty uploads directory (e.g. uploads/)
|
||||
if len(filename) < 2: # just an empty uploads directory (e.g. uploads/)
|
||||
continue
|
||||
|
||||
filename = filename[1] ## Get the second entry in the list (the actual filename)
|
||||
filename = filename[1] # Get the second entry in the list (the actual filename)
|
||||
full_path = os.path.join(upload_folder, filename)
|
||||
dirname = os.path.dirname(full_path)
|
||||
|
||||
## Create any parent directories for the file
|
||||
# Create any parent directories for the file
|
||||
if not os.path.exists(dirname):
|
||||
os.makedirs(dirname)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ def setup():
|
||||
|
||||
# Index page
|
||||
page = Pages('index', """<div class="container main-container">
|
||||
<img class="logo" src="{0}/static/original/img/logo.png" />
|
||||
<img class="logo" src="static/original/img/logo.png" />
|
||||
<h3 class="text-center">
|
||||
<p>A cool CTF platform from <a href="https://ctfd.io">ctfd.io</a></p>
|
||||
<p>Follow us on social media:</p>
|
||||
@@ -55,7 +55,7 @@ def setup():
|
||||
</h3>
|
||||
<br>
|
||||
<h4 class="text-center">
|
||||
<a href="{0}/admin">Click here</a> to login and setup your CTF
|
||||
<a href="admin">Click here</a> to login and setup your CTF
|
||||
</h4>
|
||||
</div>""".format(request.script_root))
|
||||
|
||||
|
||||
@@ -3,3 +3,4 @@ coverage>=4.1
|
||||
mock>=2.0.0
|
||||
nose>=1.3.7
|
||||
rednose>=1.1.1
|
||||
pep8==1.7.0
|
||||
|
||||
@@ -106,6 +106,7 @@ def gen_solve(db, chalid, teamid, ip='127.0.0.1', flag='rightkey'):
|
||||
db.session.commit()
|
||||
return solve
|
||||
|
||||
|
||||
def gen_wrongkey(db, teamid, chalid, flag='wrongkey'):
|
||||
wrongkey = WrongKeys(teamid, chalid, flag)
|
||||
db.session.add(wrongkey)
|
||||
|
||||
@@ -22,7 +22,7 @@ def test_register_user():
|
||||
|
||||
|
||||
def test_register_duplicate_teamname():
|
||||
"""A user shouldn't be able to use and already registered team name"""
|
||||
"""A user shouldn't be able to use an already registered team name"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app, name="user1", email="user1@ctfd.io", password="password")
|
||||
@@ -74,7 +74,7 @@ def test_user_isnt_admin():
|
||||
|
||||
|
||||
def test_user_get_teams():
|
||||
"""Can a registered user can load /teams"""
|
||||
"""Can a registered user load /teams"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
@@ -84,7 +84,7 @@ def test_user_get_teams():
|
||||
|
||||
|
||||
def test_user_get_scoreboard():
|
||||
"""Can a registered user can load /scoreboard"""
|
||||
"""Can a registered user load /scoreboard"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
@@ -94,7 +94,7 @@ def test_user_get_scoreboard():
|
||||
|
||||
|
||||
def test_user_get_scores():
|
||||
"""Can a registered user can load /scores"""
|
||||
"""Can a registered user load /scores"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
@@ -104,7 +104,7 @@ def test_user_get_scores():
|
||||
|
||||
|
||||
def test_user_get_topteams():
|
||||
"""Can a registered user can load /top/10"""
|
||||
"""Can a registered user load /top/10"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
@@ -114,7 +114,7 @@ def test_user_get_topteams():
|
||||
|
||||
|
||||
def test_user_get_challenges():
|
||||
"""Can a registered user can load /challenges"""
|
||||
"""Can a registered user load /challenges"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
@@ -124,7 +124,7 @@ def test_user_get_challenges():
|
||||
|
||||
|
||||
def test_user_get_chals():
|
||||
"""Can a registered user can load /chals"""
|
||||
"""Can a registered user load /chals"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
@@ -134,7 +134,7 @@ def test_user_get_chals():
|
||||
|
||||
|
||||
def test_user_get_solves_per_chal():
|
||||
"""Can a registered user can load /chals/solves"""
|
||||
"""Can a registered user load /chals/solves"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
@@ -144,7 +144,7 @@ def test_user_get_solves_per_chal():
|
||||
|
||||
|
||||
def test_user_get_solves():
|
||||
"""Can a registered user can load /solves"""
|
||||
"""Can a registered user load /solves"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
@@ -154,7 +154,7 @@ def test_user_get_solves():
|
||||
|
||||
|
||||
def test_user_get_team_page():
|
||||
"""Can a registered user can load their public profile (/team/2)"""
|
||||
"""Can a registered user load their public profile (/team/2)"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
@@ -164,7 +164,7 @@ def test_user_get_team_page():
|
||||
|
||||
|
||||
def test_user_get_profile():
|
||||
"""Can a registered user can load their private profile (/profile)"""
|
||||
"""Can a registered user load their private profile (/profile)"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
|
||||
Reference in New Issue
Block a user