Use strings for key type (#409)

* Store key_type as string in the database
* Give keys plugin the ability to know where the modals are stored and pass this information to the client
selenium-screenshot-testing
Kevin Chung 2017-10-14 16:37:41 -04:00 committed by GitHub
parent b4bdef966c
commit 6117699260
16 changed files with 115 additions and 27 deletions

View File

@ -199,11 +199,13 @@ def admin_get_values(chalid, prop):
chal_keys = Keys.query.filter_by(chal=challenge.id).all() chal_keys = Keys.query.filter_by(chal=challenge.id).all()
json_data = {'keys': []} json_data = {'keys': []}
for x in chal_keys: for x in chal_keys:
key_class = get_key_class(x.key_type)
json_data['keys'].append({ json_data['keys'].append({
'id': x.id, 'id': x.id,
'key': x.flag, 'key': x.flag,
'type': x.key_type, 'type': x.key_type,
'type_name': get_key_class(x.key_type).name 'type_name': key_class.name,
'templates': key_class.templates,
}) })
return jsonify(json_data) return jsonify(json_data)
elif prop == 'tags': elif prop == 'tags':
@ -257,7 +259,7 @@ def admin_create_chal():
db.session.add(chal) db.session.add(chal)
db.session.flush() db.session.flush()
flag = Keys(chal.id, request.form['key'], int(request.form['key_type[0]'])) flag = Keys(chal.id, request.form['key'], request.form['key_type[0]'])
if request.form.get('keydata'): if request.form.get('keydata'):
flag.data = request.form.get('keydata') flag.data = request.form.get('keydata')
db.session.add(flag) db.session.add(flag)

View File

@ -9,13 +9,23 @@ admin_keys = Blueprint('admin_keys', __name__)
@admin_keys.route('/admin/key_types', methods=['GET']) @admin_keys.route('/admin/key_types', methods=['GET'])
@admin_keys.route('/admin/key_types/<key_id>', methods=['GET'])
@admins_only @admins_only
def admin_key_types(): def admin_key_types(key_id=None):
data = {} if key_id is None:
for class_id in KEY_CLASSES: data = {}
data[class_id] = KEY_CLASSES.get(class_id).name for class_id in KEY_CLASSES:
data[class_id] = KEY_CLASSES.get(class_id).name
return jsonify(data) return jsonify(data)
else:
key_class = get_key_class(key_id)
data = {
'id': key_class.id,
'name': key_class.name,
'templates': key_class.templates
}
return jsonify(data)
@admin_keys.route('/admin/keys', defaults={'keyid': None}, methods=['POST', 'GET']) @admin_keys.route('/admin/keys', defaults={'keyid': None}, methods=['POST', 'GET'])
@ -25,14 +35,15 @@ def admin_keys_view(keyid):
if request.method == 'GET': if request.method == 'GET':
if keyid: if keyid:
saved_key = Keys.query.filter_by(id=keyid).first_or_404() saved_key = Keys.query.filter_by(id=keyid).first_or_404()
key_class = get_key_class(saved_key.key_type)
json_data = { json_data = {
'id': saved_key.id, 'id': saved_key.id,
'key': saved_key.flag, 'key': saved_key.flag,
'data': saved_key.data, 'data': saved_key.data,
'chal': saved_key.chal, 'chal': saved_key.chal,
'type': saved_key.key_type, 'type': saved_key.key_type,
'type_name': get_key_class(saved_key.key_type).name 'type_name': key_class.name,
'templates': key_class.templates,
} }
return jsonify(json_data) return jsonify(json_data)
@ -40,7 +51,7 @@ def admin_keys_view(keyid):
chal = request.form.get('chal') chal = request.form.get('chal')
flag = request.form.get('key') flag = request.form.get('key')
data = request.form.get('keydata') data = request.form.get('keydata')
key_type = int(request.form.get('key_type')) key_type = request.form.get('key_type')
if not keyid: if not keyid:
k = Keys(chal, flag, key_type) k = Keys(chal, flag, key_type)
k.data = data k.data = data

View File

@ -127,7 +127,7 @@ class Files(db.Model):
class Keys(db.Model): class Keys(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
chal = db.Column(db.Integer, db.ForeignKey('challenges.id')) chal = db.Column(db.Integer, db.ForeignKey('challenges.id'))
key_type = db.Column(db.Integer) key_type = db.Column(db.String(80))
flag = db.Column(db.Text) flag = db.Column(db.Text)
data = db.Column(db.Text) data = db.Column(db.Text)

View File

@ -57,7 +57,7 @@ def init_plugins(app):
:return: :return:
""" """
modules = glob.glob(os.path.dirname(__file__) + "/*") modules = glob.glob(os.path.dirname(__file__) + "/*")
blacklist = {'keys', '__pycache__'} blacklist = {'__pycache__'}
for module in modules: for module in modules:
module_name = os.path.basename(module) module_name = os.path.basename(module)
if os.path.isdir(module) and module_name not in blacklist: if os.path.isdir(module) and module_name not in blacklist:

View File

@ -48,13 +48,13 @@
<div class="col-md-2"> <div class="col-md-2">
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="key_type[0]" value="0" checked> <input type="radio" name="key_type[0]" value="static" checked>
Static Static
</label> </label>
</div> </div>
<div class="radio"> <div class="radio">
<label> <label>
<input type="radio" name="key_type[0]" value="1"> <input type="radio" name="key_type[0]" value="regex">
Regex Regex
</label> </label>
</div> </div>

View File

@ -317,11 +317,13 @@ $('#create-key').click(function(e){
$('#create-keys-select').change(function(){ $('#create-keys-select').change(function(){
var key_type_name = $(this).find("option:selected").text(); var key_type_name = $(this).find("option:selected").text();
$.get(script_root + '/themes/admin/static/js/templates/keys/'+key_type_name +'/'+key_type_name+'.hbs', function(template_data){ $.get(script_root + '/admin/key_types/' + key_type_name, function(key_data){
var template = Handlebars.compile(template_data); $.get(script_root + key_data.templates.create, function(template_data){
$("#create-keys-entry-div").html(template()); var template = Handlebars.compile(template_data);
$("#create-keys-button-div").show(); $("#create-keys-entry-div").html(template());
}); $("#create-keys-button-div").show();
});
})
}); });

View File

@ -1,3 +1,5 @@
from CTFd.plugins import register_plugin_assets_directory
import re import re
import string import string
import hmac import hmac
@ -6,6 +8,7 @@ import hmac
class BaseKey(object): class BaseKey(object):
id = None id = None
name = None name = None
templates = {}
@staticmethod @staticmethod
def compare(self, saved, provided): def compare(self, saved, provided):
@ -15,6 +18,10 @@ class BaseKey(object):
class CTFdStaticKey(BaseKey): class CTFdStaticKey(BaseKey):
id = 0 id = 0
name = "static" name = "static"
templates = { # Handlebars templates used for key editing & viewing
'create': '/plugins/keys/assets/static/create-static-modal.hbs',
'update': '/plugins/keys/assets/static/edit-static-modal.hbs',
}
@staticmethod @staticmethod
def compare(saved, provided): def compare(saved, provided):
@ -29,6 +36,10 @@ class CTFdStaticKey(BaseKey):
class CTFdRegexKey(BaseKey): class CTFdRegexKey(BaseKey):
id = 1 id = 1
name = "regex" name = "regex"
templates = { # Handlebars templates used for key editing & viewing
'create': '/plugins/keys/assets/regex/create-regex-modal.hbs',
'update': '/plugins/keys/assets/regex/edit-regex-modal.hbs',
}
@staticmethod @staticmethod
def compare(saved, provided): def compare(saved, provided):
@ -37,8 +48,8 @@ class CTFdRegexKey(BaseKey):
KEY_CLASSES = { KEY_CLASSES = {
0: CTFdStaticKey, 'static': CTFdStaticKey,
1: CTFdRegexKey 'regex': CTFdRegexKey
} }
@ -47,3 +58,7 @@ def get_key_class(class_id):
if cls is None: if cls is None:
raise KeyError raise KeyError
return cls return cls
def load(app):
register_plugin_assets_directory(app, base_path='/plugins/keys/assets/')

View File

@ -6,7 +6,7 @@
<div class="modal-body"> <div class="modal-body">
<form method="POST" action="{{ script_root }}/admin/keys/{{id}}" style="text-align:center"> <form method="POST" action="{{ script_root }}/admin/keys/{{id}}" style="text-align:center">
<input type="text" id="key-data" class="form-control" name="key" value="{{key}}" placeholder="Enter regex key data"> <input type="text" id="key-data" class="form-control" name="key" value="{{key}}" placeholder="Enter regex key data">
<input type="hidden" id="key-type" name="key_type" value="1"> <input type="hidden" id="key-type" name="key_type" value="regex">
<input type="hidden" id="key-id"> <input type="hidden" id="key-id">
<hr> <hr>
<div class="row"> <div class="row">

View File

@ -6,7 +6,7 @@
<div class="modal-body"> <div class="modal-body">
<form method="POST" action="{{ script_root }}/admin/keys/{{id}}" style="text-align:center"> <form method="POST" action="{{ script_root }}/admin/keys/{{id}}" style="text-align:center">
<input type="text" id="key-data" class="form-control" name="key" value="{{key}}" placeholder="Enter static key data"> <input type="text" id="key-data" class="form-control" name="key" value="{{key}}" placeholder="Enter static key data">
<input type="hidden" id="key-type" name="key_type" value="0"> <input type="hidden" id="key-type" name="key_type" value="static">
<input type="hidden" id="key-id"> <input type="hidden" id="key-id">
<hr> <hr>
<div class="row"> <div class="row">

View File

@ -22,8 +22,8 @@ String.prototype.hashCode = function() {
}; };
function load_edit_key_modal(key_id, key_type_name) { function load_edit_key_modal(key_id, key_type_name) {
$.get(script_root + '/themes/admin/static/js/templates/keys/'+key_type_name+'/edit-'+key_type_name+'-modal.hbs', function(template_data){ $.get(script_root + '/admin/keys/' + key_id, function(key_data){
$.get(script_root + '/admin/keys/' + key_id, function(key_data){ $.get(script_root + key_data.templates.update, function(template_data){
$('#edit-keys').empty(); $('#edit-keys').empty();
var template = Handlebars.compile(template_data); var template = Handlebars.compile(template_data);
key_data['script_root'] = script_root; key_data['script_root'] = script_root;

BIN
export.zip Normal file

Binary file not shown.

View File

@ -0,0 +1,58 @@
"""Use strings for key types
Revision ID: e62fd69bd417
Revises: 7e9efd084c5a
Create Date: 2017-10-14 14:56:14.215349
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e62fd69bd417'
down_revision = '7e9efd084c5a'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
bind = op.get_bind()
url = str(bind.engine.url)
if url.startswith('mysql'):
op.alter_column('keys', 'key_type',
existing_type=sa.INTEGER(),
type_=sa.String(length=80),
existing_nullable=True)
op.execute("UPDATE `keys` set key_type='static' WHERE key_type=0")
op.execute("UPDATE `keys` set key_type='regex' WHERE key_type=1")
elif url.startswith('postgres'):
op.alter_column('keys', 'key_type',
existing_type=sa.INTEGER(),
type_=sa.String(length=80),
existing_nullable=True,
postgresql_using="CASE WHEN key_type=0 THEN 'static' WHEN key_type=1 THEN 'regex' ELSE NULL END"
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
bind = op.get_bind()
url = str(bind.engine.url)
if url.startswith('mysql'):
op.execute("UPDATE `keys` set key_type=0 WHERE key_type='static'")
op.execute("UPDATE `keys` set key_type=1 WHERE key_type='regex'")
op.alter_column('keys', 'key_type',
existing_type=sa.String(length=80),
type_=sa.INTEGER(),
existing_nullable=True)
elif url.startswith('postgres'):
op.alter_column('keys', 'key_type',
existing_type=sa.String(length=80),
type_=sa.INTEGER(),
existing_nullable=True,
postgresql_using="CASE WHEN key_type='static' THEN 0 WHEN key_type='regex' THEN 1 ELSE NULL END"
)
# ### end Alembic commands ###

View File

@ -222,7 +222,7 @@ if __name__ == '__main__':
word = gen_word() word = gen_word()
db.session.add(Challenges(word, gen_sentence(), gen_value(), gen_category())) db.session.add(Challenges(word, gen_sentence(), gen_value(), gen_category()))
db.session.commit() db.session.commit()
db.session.add(Keys(x + 1, word, 0)) db.session.add(Keys(x + 1, word, 'static'))
db.session.commit() db.session.commit()
# Generating Files # Generating Files

View File

@ -109,7 +109,7 @@ def gen_file():
pass pass
def gen_flag(db, chal, flag='flag', key_type=0): def gen_flag(db, chal, flag='flag', key_type='static'):
key = Keys(chal, flag, key_type) key = Keys(chal, flag, key_type)
db.session.add(key) db.session.add(key)
db.session.commit() db.session.commit()