def test_update_check_identifies_update(fake_get_request): """Update checks properly identify new versions""" app = create_ctfd() with app.app_context(): app.config['UPDATE_CHECK'] = True fake_response = Mock() fake_get_request.return_value = fake_response fake_response.json = lambda: { u'resource': { u'html_url': u'https://github.com/CTFd/CTFd/releases/tag/9.9.9', u'download_url': u'https://api.github.com/repos/CTFd/CTFd/zipball/9.9.9', u'published_at': u'Wed, 25 Oct 2017 19:39:42 -0000', u'tag': u'9.9.9', u'prerelease': False, u'id': 6, u'latest': True } } update_check() assert get_config( 'version_latest' ) == 'https://github.com/CTFd/CTFd/releases/tag/9.9.9' destroy_ctfd(app)
def test_update_check_ignores_downgrades(fake_get_request): """Update checks do nothing on old or same versions""" app = create_ctfd() with app.app_context(): app.config['UPDATE_CHECK'] = True fake_response = Mock() fake_get_request.return_value = fake_response fake_response.json = lambda: { u'resource': { u'html_url': u'https://github.com/CTFd/CTFd/releases/tag/0.0.1', u'download_url': u'https://api.github.com/repos/CTFd/CTFd/zipball/0.0.1', u'published_at': u'Wed, 25 Oct 2017 19:39:42 -0000', u'tag': u'0.0.1', u'prerelease': False, u'id': 6, u'latest': True } } update_check() assert get_config('version_latest') is None fake_response = Mock() fake_get_request.return_value = fake_response fake_response.json = lambda: { u'resource': { u'html_url': u'https://github.com/CTFd/CTFd/releases/tag/{}'.format( app.VERSION), u'download_url': u'https://api.github.com/repos/CTFd/CTFd/zipball/{}'.format( app.VERSION), u'published_at': u'Wed, 25 Oct 2017 19:39:42 -0000', u'tag': u'{}'.format(app.VERSION), u'prerelease': False, u'id': 6, u'latest': True } } update_check() assert get_config('version_latest') is None destroy_ctfd(app)
def admin_stats(): update_check() teams_registered = db.session.query(db.func.count(Teams.id)).first()[0] wrong_count = WrongKeys.query.join( Teams, WrongKeys.teamid == Teams.id).filter(Teams.banned == False).count() solve_count = Solves.query.join( Teams, Solves.teamid == Teams.id).filter(Teams.banned == False).count() challenge_count = db.session.query(db.func.count(Challenges.id)).first()[0] ip_count = db.session.query(db.func.count( Tracking.ip.distinct())).first()[0] solves_sub = db.session.query(Solves.chalid, db.func.count(Solves.chalid).label('solves_cnt')) \ .join(Teams, Solves.teamid == Teams.id).filter(Teams.banned == False) \ .group_by(Solves.chalid).subquery() solves = db.session.query(solves_sub.columns.chalid, solves_sub.columns.solves_cnt, Challenges.name) \ .join(Challenges, solves_sub.columns.chalid == Challenges.id).all() solve_data = {} for chal, count, name in solves: solve_data[name] = count most_solved = None least_solved = None if len(solve_data): most_solved = max(solve_data, key=solve_data.get) least_solved = min(solve_data, key=solve_data.get) db.session.expunge_all() db.session.commit() db.session.close() return render_template('admin/statistics.html', team_count=teams_registered, ip_count=ip_count, wrong_count=wrong_count, solve_count=solve_count, challenge_count=challenge_count, solve_data=solve_data, most_solved=most_solved, least_solved=least_solved)
def create_app(config='CTFd.config.Config'): app = Flask(__name__) with app.app_context(): app.config.from_object(config) app.jinja_loader = ThemeLoader(os.path.join(app.root_path, 'themes'), followlinks=True) from CTFd.models import db, Teams, Solves, Challenges, WrongKeys, Keys, Tags, Files, Tracking url = make_url(app.config['SQLALCHEMY_DATABASE_URI']) if url.drivername == 'postgres': url.drivername = 'postgresql' if url.drivername.startswith('mysql'): url.query['charset'] = 'utf8mb4' # Creates database if the database database does not exist if not database_exists(url): if url.drivername.startswith('mysql'): create_database(url, encoding='utf8mb4') else: create_database(url) # This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in # This is mostly so we can force MySQL's charset app.config['SQLALCHEMY_DATABASE_URI'] = str(url) # Register database db.init_app(app) # Register Flask-Migrate migrate.init_app(app, db) # Alembic sqlite support is lacking so we should just create_all anyway if url.drivername.startswith('sqlite'): db.create_all() else: if len(db.engine.table_names()) == 0: # This creates tables instead of db.create_all() # Allows migrations to happen properly migrate_upgrade() elif 'alembic_version' not in db.engine.table_names(): # There is no alembic_version because CTFd is from before it had migrations # Stamp it to the base migration if confirm_upgrade(): migrate_stamp(revision='cb3cfcc47e2f') run_upgrade() else: exit() app.db = db app.VERSION = __version__ cache.init_app(app) app.cache = cache update_check() version = utils.get_config('ctf_version') # Upgrading from an older version of CTFd if version and (StrictVersion(version) < StrictVersion(__version__)): if confirm_upgrade(): run_upgrade() else: exit() if not utils.get_config('ctf_theme'): utils.set_config('ctf_theme', 'core') from CTFd.views import views from CTFd.challenges import challenges from CTFd.scoreboard import scoreboard from CTFd.auth import auth from CTFd.admin import admin, admin_statistics, admin_challenges, admin_pages, admin_scoreboard, admin_keys, admin_teams from CTFd.utils import init_utils, init_errors, init_logs init_utils(app) init_errors(app) init_logs(app) app.register_blueprint(views) app.register_blueprint(challenges) app.register_blueprint(scoreboard) app.register_blueprint(auth) app.register_blueprint(admin) app.register_blueprint(admin_statistics) app.register_blueprint(admin_challenges) app.register_blueprint(admin_teams) app.register_blueprint(admin_scoreboard) app.register_blueprint(admin_keys) app.register_blueprint(admin_pages) from CTFd.plugins import init_plugins init_plugins(app) return app