mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-17 14:04:20 +01:00
* Pages now support Markdown * Pages now have a preview tab * Adding a media library to Pages
265 lines
10 KiB
Python
265 lines
10 KiB
Python
import os
|
|
import re
|
|
|
|
from flask import current_app as app, render_template, request, redirect, abort, jsonify, url_for, session, Blueprint, Response, send_file
|
|
from jinja2.exceptions import TemplateNotFound
|
|
from passlib.hash import bcrypt_sha256
|
|
|
|
from CTFd.models import db, Teams, Solves, Awards, Files, Pages
|
|
from CTFd.utils import cache, markdown
|
|
from CTFd import utils
|
|
|
|
views = Blueprint('views', __name__)
|
|
|
|
|
|
@views.before_request
|
|
def redirect_setup():
|
|
if request.path.startswith("/static"):
|
|
return
|
|
if not utils.is_setup() and request.path != "/setup":
|
|
return redirect(url_for('views.setup'))
|
|
|
|
|
|
@views.route('/setup', methods=['GET', 'POST'])
|
|
def setup():
|
|
# with app.app_context():
|
|
# admin = Teams.query.filter_by(admin=True).first()
|
|
|
|
if not utils.is_setup():
|
|
if not session.get('nonce'):
|
|
session['nonce'] = utils.sha512(os.urandom(10))
|
|
if request.method == 'POST':
|
|
ctf_name = request.form['ctf_name']
|
|
ctf_name = utils.set_config('ctf_name', ctf_name)
|
|
|
|
# CSS
|
|
css = utils.set_config('start', '')
|
|
|
|
# Admin user
|
|
name = request.form['name']
|
|
email = request.form['email']
|
|
password = request.form['password']
|
|
admin = Teams(name, email, password)
|
|
admin.admin = True
|
|
admin.banned = True
|
|
|
|
# Index page
|
|
page = Pages('index', """<div class="container main-container">
|
|
<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>
|
|
<a href="https://twitter.com/ctfdio"><i class="fa fa-twitter fa-2x" aria-hidden="true"></i></a>
|
|
<a href="https://facebook.com/ctfdio"><i class="fa fa-facebook-official fa-2x" aria-hidden="true"></i></a>
|
|
<a href="https://github.com/ctfd"><i class="fa fa-github fa-2x" aria-hidden="true"></i></a>
|
|
</h3>
|
|
<br>
|
|
<h4 class="text-center">
|
|
<a href="admin">Click here</a> to login and setup your CTF
|
|
</h4>
|
|
</div>""".format(request.script_root))
|
|
|
|
# max attempts per challenge
|
|
max_tries = utils.set_config('max_tries', 0)
|
|
|
|
# Start time
|
|
start = utils.set_config('start', None)
|
|
end = utils.set_config('end', None)
|
|
freeze = utils.set_config('freeze', None)
|
|
|
|
# Challenges cannot be viewed by unregistered users
|
|
view_challenges_unregistered = utils.set_config('view_challenges_unregistered', None)
|
|
|
|
# Allow/Disallow registration
|
|
prevent_registration = utils.set_config('prevent_registration', None)
|
|
|
|
# Verify emails
|
|
verify_emails = utils.set_config('verify_emails', None)
|
|
|
|
mail_server = utils.set_config('mail_server', None)
|
|
mail_port = utils.set_config('mail_port', None)
|
|
mail_tls = utils.set_config('mail_tls', None)
|
|
mail_ssl = utils.set_config('mail_ssl', None)
|
|
mail_username = utils.set_config('mail_username', None)
|
|
mail_password = utils.set_config('mail_password', None)
|
|
|
|
setup = utils.set_config('setup', True)
|
|
|
|
db.session.add(page)
|
|
db.session.add(admin)
|
|
db.session.commit()
|
|
|
|
session['username'] = admin.name
|
|
session['id'] = admin.id
|
|
session['admin'] = admin.admin
|
|
session['nonce'] = utils.sha512(os.urandom(10))
|
|
|
|
db.session.close()
|
|
app.setup = False
|
|
with app.app_context():
|
|
cache.clear()
|
|
|
|
return redirect(url_for('views.static_html'))
|
|
return render_template('setup.html', nonce=session.get('nonce'))
|
|
return redirect(url_for('views.static_html'))
|
|
|
|
|
|
# Custom CSS handler
|
|
@views.route('/static/user.css')
|
|
def custom_css():
|
|
return Response(utils.get_config('css'), mimetype='text/css')
|
|
|
|
|
|
# Static HTML files
|
|
@views.route("/", defaults={'template': 'index'})
|
|
@views.route("/<template>")
|
|
def static_html(template):
|
|
try:
|
|
return render_template('%s.html' % template)
|
|
except TemplateNotFound:
|
|
page = Pages.query.filter_by(route=template).first_or_404()
|
|
return render_template('page.html', content=markdown(page.html))
|
|
|
|
|
|
@views.route('/teams', defaults={'page': '1'})
|
|
@views.route('/teams/<int:page>')
|
|
def teams(page):
|
|
page = abs(int(page))
|
|
results_per_page = 50
|
|
page_start = results_per_page * (page - 1)
|
|
page_end = results_per_page * (page - 1) + results_per_page
|
|
|
|
if utils.get_config('verify_emails'):
|
|
count = Teams.query.filter_by(verified=True, banned=False).count()
|
|
teams = Teams.query.filter_by(verified=True, banned=False).slice(page_start, page_end).all()
|
|
else:
|
|
count = Teams.query.filter_by(banned=False).count()
|
|
teams = Teams.query.filter_by(banned=False).slice(page_start, page_end).all()
|
|
pages = int(count / results_per_page) + (count % results_per_page > 0)
|
|
return render_template('teams.html', teams=teams, team_pages=pages, curr_page=page)
|
|
|
|
|
|
@views.route('/team/<int:teamid>', methods=['GET', 'POST'])
|
|
def team(teamid):
|
|
if utils.get_config('view_scoreboard_if_utils.authed') and not utils.authed():
|
|
return redirect(url_for('auth.login', next=request.path))
|
|
errors = []
|
|
freeze = utils.get_config('freeze')
|
|
user = Teams.query.filter_by(id=teamid).first_or_404()
|
|
solves = Solves.query.filter_by(teamid=teamid)
|
|
awards = Awards.query.filter_by(teamid=teamid)
|
|
|
|
place = user.place()
|
|
score = user.score()
|
|
|
|
if freeze:
|
|
freeze = utils.unix_time_to_utc(freeze)
|
|
if teamid != session.get('id'):
|
|
solves = solves.filter(Solves.date < freeze)
|
|
awards = awards.filter(Awards.date < freeze)
|
|
|
|
solves = solves.all()
|
|
awards = awards.all()
|
|
|
|
db.session.close()
|
|
|
|
if utils.hide_scores() and teamid != session.get('id'):
|
|
errors.append('Scores are currently hidden')
|
|
|
|
if errors:
|
|
return render_template('team.html', team=user, errors=errors)
|
|
|
|
if request.method == 'GET':
|
|
return render_template('team.html', solves=solves, awards=awards, team=user, score=score, place=place, score_frozen=utils.is_scoreboard_frozen())
|
|
elif request.method == 'POST':
|
|
json = {'solves': []}
|
|
for x in solves:
|
|
json['solves'].append({'id': x.id, 'chal': x.chalid, 'team': x.teamid})
|
|
return jsonify(json)
|
|
|
|
|
|
@views.route('/profile', methods=['POST', 'GET'])
|
|
def profile():
|
|
if utils.authed():
|
|
if request.method == "POST":
|
|
errors = []
|
|
|
|
name = request.form.get('name')
|
|
email = request.form.get('email')
|
|
website = request.form.get('website')
|
|
affiliation = request.form.get('affiliation')
|
|
country = request.form.get('country')
|
|
|
|
user = Teams.query.filter_by(id=session['id']).first()
|
|
|
|
if not utils.get_config('prevent_name_change'):
|
|
names = Teams.query.filter_by(name=name).first()
|
|
name_len = len(request.form['name']) == 0
|
|
|
|
emails = Teams.query.filter_by(email=email).first()
|
|
valid_email = re.match(r"(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", email)
|
|
|
|
if ('password' in request.form.keys() and not len(request.form['password']) == 0) and \
|
|
(not bcrypt_sha256.verify(request.form.get('confirm').strip(), user.password)):
|
|
errors.append("Your old password doesn't match what we have.")
|
|
if not valid_email:
|
|
errors.append("That email doesn't look right")
|
|
if not utils.get_config('prevent_name_change') and names and name != session['username']:
|
|
errors.append('That team name is already taken')
|
|
if emails and emails.id != session['id']:
|
|
errors.append('That email has already been used')
|
|
if not utils.get_config('prevent_name_change') and name_len:
|
|
errors.append('Pick a longer team name')
|
|
if website.strip() and not utils.validate_url(website):
|
|
errors.append("That doesn't look like a valid URL")
|
|
|
|
if len(errors) > 0:
|
|
return render_template('profile.html', name=name, email=email, website=website,
|
|
affiliation=affiliation, country=country, errors=errors)
|
|
else:
|
|
team = Teams.query.filter_by(id=session['id']).first()
|
|
if not utils.get_config('prevent_name_change'):
|
|
team.name = name
|
|
if team.email != email.lower():
|
|
team.email = email.lower()
|
|
if utils.get_config('verify_emails'):
|
|
team.verified = False
|
|
session['username'] = team.name
|
|
|
|
if 'password' in request.form.keys() and not len(request.form['password']) == 0:
|
|
team.password = bcrypt_sha256.encrypt(request.form.get('password'))
|
|
team.website = website
|
|
team.affiliation = affiliation
|
|
team.country = country
|
|
db.session.commit()
|
|
db.session.close()
|
|
return redirect(url_for('views.profile'))
|
|
else:
|
|
user = Teams.query.filter_by(id=session['id']).first()
|
|
name = user.name
|
|
email = user.email
|
|
website = user.website
|
|
affiliation = user.affiliation
|
|
country = user.country
|
|
prevent_name_change = utils.get_config('prevent_name_change')
|
|
confirm_email = utils.get_config('verify_emails') and not user.verified
|
|
return render_template('profile.html', name=name, email=email, website=website, affiliation=affiliation,
|
|
country=country, prevent_name_change=prevent_name_change, confirm_email=confirm_email)
|
|
else:
|
|
return redirect(url_for('auth.login'))
|
|
|
|
|
|
@views.route('/files', defaults={'path': ''})
|
|
@views.route('/files/<path:path>')
|
|
def file_handler(path):
|
|
f = Files.query.filter_by(location=path).first_or_404()
|
|
if f.chal:
|
|
if not utils.is_admin():
|
|
if not utils.ctftime():
|
|
if utils.view_after_ctf() and utils.ctf_started():
|
|
pass
|
|
else:
|
|
abort(403)
|
|
upload_folder = os.path.join(app.root_path, app.config['UPLOAD_FOLDER'])
|
|
return send_file(os.path.join(upload_folder, f.location))
|