Skip to content
This repository has been archived by the owner on Dec 21, 2019. It is now read-only.

Commit

Permalink
Extracted CTFd code
Browse files Browse the repository at this point in the history
  • Loading branch information
ColdHeat committed Aug 12, 2017
1 parent 660fb8f commit 69be11a
Show file tree
Hide file tree
Showing 7 changed files with 448 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# CTFd-Containers
# CTFd-Docker
Plugin to give CTFd the ability to manage Docker containers
82 changes: 82 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
from flask import current_app as app, render_template, request, redirect, jsonify, url_for, Blueprint
from CTFd.utils import admins_only, is_admin, cache
from CTFd.models import db
from .models import Containers

from . import utils

def load(app):
app.db.create_all()
admin_containers = Blueprint('admin_containers', __name__, template_folder='templates')


@admin_containers.route('/admin/containers', methods=['GET'])
@admins_only
def list_container():
containers = Containers.query.all()
for c in containers:
c.status = utils.container_status(c.name)
c.ports = ', '.join(utils.container_ports(c.name, verbose=True))
return render_template('containers.html', containers=containers)


@admin_containers.route('/admin/containers/<int:container_id>/stop', methods=['POST'])
@admins_only
def stop_container(container_id):
container = Containers.query.filter_by(id=container_id).first_or_404()
if utils.container_stop(container.name):
return '1'
else:
return '0'


@admin_containers.route('/admin/containers/<int:container_id>/start', methods=['POST'])
@admins_only
def run_container(container_id):
container = Containers.query.filter_by(id=container_id).first_or_404()
if utils.container_status(container.name) == 'missing':
if utils.run_image(container.name):
return '1'
else:
return '0'
else:
if utils.container_start(container.name):
return '1'
else:
return '0'


@admin_containers.route('/admin/containers/<int:container_id>/delete', methods=['POST'])
@admins_only
def delete_container(container_id):
container = Containers.query.filter_by(id=container_id).first_or_404()
if utils.delete_image(container.name):
db.session.delete(container)
db.session.commit()
db.session.close()
return '1'


@admin_containers.route('/admin/containers/new', methods=['POST'])
@admins_only
def new_container():
name = request.form.get('name')
if not set(name) <= set('abcdefghijklmnopqrstuvwxyz0123456789-_'):
return redirect(url_for('admin_containers.list_container'))
buildfile = request.form.get('buildfile')
files = request.files.getlist('files[]')
utils.create_image(name=name, buildfile=buildfile, files=files)
utils.run_image(name)
return redirect(url_for('admin_containers.list_container'))


@admin_containers.route('/admin/containers/import', methods=['POST'])
@admins_only
def import_container():
name = request.form.get('name')
if not set(name) <= set('abcdefghijklmnopqrstuvwxyz0123456789-_'):
return redirect(url_for('admin_containers.list_container'))
utils.import_image(name=name)
return redirect(url_for('admin_containers.list_container'))

app.register_blueprint(admin_containers)
10 changes: 10 additions & 0 deletions config.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% extends "admin/base.html" %}

{% block content %}
{% endblock %}

{% block scripts %}
<script type="text/javascript">
window.location = script_root + "/admin/containers"
</script>
{% endblock %}
14 changes: 14 additions & 0 deletions models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from CTFd.models import db


class Containers(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80))
buildfile = db.Column(db.Text)

def __init__(self, name, buildfile):
self.name = name
self.buildfile = buildfile

def __repr__(self):
return "<Container ID:(0) {1}>".format(self.id, self.name)
Empty file added requirements.txt
Empty file.
190 changes: 190 additions & 0 deletions templates/containers.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
{% extends "admin/base.html" %}

{% block content %}
<div class="modal fade" id="create-container-modal" tabindex="-1" role="dialog" aria-labelledby="container-modal-label">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="container-modal-label">Create Container</h4>
</div>
<form method="POST" action="{{ request.script_root }}/admin/containers/new" enctype="multipart/form-data">
<div class="modal-body">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" name="name" placeholder="Enter container name">
</div>
<div class="form-group">
<label for="buildfile-editor" class="control-label">Build File</label>
<textarea id="buildfile-editor" class="form-control" name="buildfile" rows="10" placeholder="Enter container build file"></textarea>
</div>
<div class="form-group">
<label for="container-files">Associated Files
<i class="fa fa-question-circle gray-text" data-toggle="tooltip" data-placement="right" title="These files are uploaded alongside your buildfile"></i>
</label>
<input type="file" name="files[]" id="container-files" multiple>
<sub class="help-block">Attach multiple files using Control+Click or Cmd+Click.</sub>
</div>
<input type="hidden" value="{{ nonce }}" name="nonce" id="nonce">
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Create</button>
</div>
</form>
</div>
</div>
</div>

<div class="modal fade" id="import-container-modal" tabindex="-1" role="dialog" aria-labelledby="container-modal-label">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="container-modal-label">Import Image</h4>
</div>
<form method="POST" action="{{ request.script_root }}/admin/containers/import" enctype="multipart/form-data">
<div class="modal-body">
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" name="name" placeholder="Enter image name">
</div>
<input type="hidden" value="{{ nonce }}" name="nonce" id="nonce">
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Create</button>
</div>
</form>
</div>
</div>
</div>


<div id="confirm" class="modal fade" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h2 class="text-center"><span id="confirm-container-title"></span> Container</h2>
</div>
<div class="modal-body" style="height:110px">
<div class="row-fluid">
<div class="col-md-12">
<form method="POST">
<input id="nonce" type="hidden" name="nonce" value="{{ nonce }}">
<div class="small-6 small-centered text-center columns">
<p>Are you sure you want to <span id="confirm-container-method"></span> <strong id="confirm-container-name"></strong>?</p>
<button type="button" data-dismiss="modal" class="btn btn-theme btn-outlined">No</button>
<button type="button" id="confirm-container" class="btn btn-theme btn-outlined">Yes</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>


<div class="row">
<br>
<div style="text-align:center">
<h1>Containers</h1>
<button class="btn btn-theme btn-outlined create-challenge" data-toggle="modal" data-target="#create-container-modal">
New Container
</button>
<button class="btn btn-theme btn-outlined create-challenge" data-toggle="modal" data-target="#import-container-modal">
Import Image
</button>
</div>
<br>
{% if containers %}
<table id="teamsboard">
<thead>
<tr>
<td class="text-center"><strong>Status</strong>
</td>
<td class="text-center"><strong>Name</strong>
</td>
<td class="text-center"><strong>Ports</strong>
</td>
<td class="text-center"><strong>Settings</strong>
</td>
</tr>
</thead>
<tbody>
{% for c in containers %}
<tr>
<td class="text-center">{{ c.status }}</td>
<td class="text-center container_item" id="{{ c.id }}">{{ c.name }}</td>
<td class="text-center">{{ c.ports }}</td>
<td class="text-center">
<span>
{% if c.status != 'running' %}
<i class="fa fa-play"></i>
{% else %}
<i class="fa fa-stop"></i>
{% endif %}
<i class="fa fa-times"></i>
</span>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
{% endblock %}

{% block scripts %}
<script>

function load_confirm_modal(title, url, container_name){
var modal = $('#confirm')
modal.find('#confirm-container-name').text(container_name)
modal.find('#confirm-container-title').text(title)
modal.find('#confirm-container-method').text(title.toLowerCase())
$('#confirm form').attr('action', url);
$('#confirm').modal('show');
}

$('#confirm-container').click(function(e){
e.preventDefault();
var id = $('#confirm input[name="id"]').val()
var user_data = $('#confirm form').serializeArray()
$.post($('#confirm form').attr('action'), $('#confirm form').serialize(), function(data){
var data = $.parseJSON(JSON.stringify(data))
if (data == "1"){
location.reload()
}
})
});


$('.fa-times').click(function(){
var elem = $(this).parent().parent().parent().find('.container_item');
var container = elem.attr('id');
var container_name = elem.text().trim();
load_confirm_modal('Delete', '/admin/containers/'+container+'/delete', container_name)
});

$('.fa-play').click(function(){
var elem = $(this).parent().parent().parent().find('.container_item');
var container = elem.attr('id');
var container_name = elem.text().trim();
load_confirm_modal('Start', '/admin/containers/'+container+'/start', container_name)
});

$('.fa-stop').click(function(){
var elem = $(this).parent().parent().parent().find('.container_item');
var container = elem.attr('id');
var container_name = elem.text().trim();
load_confirm_modal('Stop', '/admin/containers/'+container+'/stop', container_name)
});

$(document).ready(function(){
$('[data-toggle="tooltip"]').tooltip();
});

</script>
{% endblock %}
Loading

0 comments on commit 69be11a

Please sign in to comment.