added profile page
This commit is contained in:
parent
80ca1377de
commit
df5d146e08
15 changed files with 288 additions and 139 deletions
|
@ -17,6 +17,10 @@ server {
|
|||
default_type text/html;
|
||||
try_files /index.html /index.html;
|
||||
}
|
||||
location ~^/admin/(problems)$ {
|
||||
default_type text/html;
|
||||
try_files /index.html /index.html;
|
||||
}
|
||||
|
||||
location ~ /api {
|
||||
proxy_set_header Host $host;
|
||||
|
|
2
deploy
2
deploy
|
@ -5,6 +5,8 @@ pkill gunicorn
|
|||
sudo service nginx stop
|
||||
tmux kill-session -t ctf 2> /dev/null
|
||||
|
||||
sudo cp /vagrant/ctf.nginx /etc/nginx/sites-enabled/ctf
|
||||
|
||||
echo "Starting the server..."
|
||||
cd /home/vagrant/server
|
||||
sudo service nginx start
|
||||
|
|
31
server/api.html
Normal file
31
server/api.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html><head><title>Python: package api</title>
|
||||
<meta charset="utf-8">
|
||||
</head><body bgcolor="#f0f0f8">
|
||||
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
||||
<tr bgcolor="#7799ee">
|
||||
<td valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>api</strong></big></big></font></td
|
||||
><td align=right valign=bottom
|
||||
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/vagrant/server/api/__init__.py">/home/vagrant/server/api/__init__.py</a></font></td></tr></table>
|
||||
<p></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Package Contents</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="api.admin.html">admin</a><br>
|
||||
<a href="api.api.html">api</a><br>
|
||||
<a href="api.decorators.html">decorators</a><br>
|
||||
</td><td width="25%" valign=top><a href="api.logger.html">logger</a><br>
|
||||
<a href="api.models.html">models</a><br>
|
||||
<a href="api.problem.html">problem</a><br>
|
||||
</td><td width="25%" valign=top><a href="api.schemas.html">schemas</a><br>
|
||||
<a href="api.user.html">user</a><br>
|
||||
<a href="api.utils.html">utils</a><br>
|
||||
</td><td width="25%" valign=top></td></tr></table></td></tr></table>
|
||||
</body></html>
|
|
@ -1,19 +1,26 @@
|
|||
from flask import Blueprint, jsonify
|
||||
from decorators import admins_only, api_wrapper, login_required
|
||||
from decorators import admins_only, api_wrapper
|
||||
from models import db, Problems, Files
|
||||
|
||||
import json
|
||||
|
||||
blueprint = Blueprint("admin", __name__)
|
||||
|
||||
@blueprint.route("/problem/data", methods=["POST"])
|
||||
#@api_wrapper # Disable atm due to json serialization issues: will fix
|
||||
@blueprint.route("/problems/list", methods=["POST"])
|
||||
@api_wrapper
|
||||
@admins_only
|
||||
@login_required
|
||||
def problem_data():
|
||||
problems = Problems.query.add_columns("pid", "name", "category", "description", "hint", "value", "solves", "disabled", "flag").order_by(Problems.value).all()
|
||||
jason = []
|
||||
|
||||
problems = Problems.query.order_by(Problems.value).all()
|
||||
problems_return = [ ]
|
||||
for problem in problems:
|
||||
problem_files = [ str(_file.location) for _file in Files.query.filter_by(pid=int(problem.pid)).all() ]
|
||||
jason.append({"pid": problem[1], "name": problem[2] ,"category": problem[3], "description": problem[4], "hint": problem[5], "value": problem[6], "solves": problem[7], "disabled": problem[8], "flag": problem[9], "files": problem_files})
|
||||
|
||||
return jsonify(data=jason)
|
||||
problems_return.append({
|
||||
"pid": problem.pid,
|
||||
"name": problem.name,
|
||||
"category": problem.category,
|
||||
"description": problem.description,
|
||||
"hint": problem.hint,
|
||||
"value": problem.value,
|
||||
"threshold": problem.threshold,
|
||||
"weightmap": json.loads(problem.weightmap)
|
||||
})
|
||||
return { "success": 1, "problems": problems_return }
|
|
@ -1,5 +1,5 @@
|
|||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
import datetime
|
||||
import time
|
||||
import utils
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
@ -14,6 +14,8 @@ class Users(db.Model):
|
|||
password = db.Column(db.String(128))
|
||||
admin = db.Column(db.Boolean)
|
||||
utype = db.Column(db.Integer)
|
||||
tid = db.Column(db.Integer)
|
||||
registertime = db.Column(db.Integer)
|
||||
|
||||
def __init__(self, name, username, email, password, utype=1):
|
||||
self.name = name
|
||||
|
@ -23,6 +25,7 @@ class Users(db.Model):
|
|||
self.password = utils.hash_password(password)
|
||||
self.utype = utype
|
||||
self.admin = False
|
||||
self.registertime = int(time.time())
|
||||
|
||||
class Teams(db.Model):
|
||||
tid = db.Column(db.Integer, primary_key=True)
|
||||
|
@ -90,11 +93,11 @@ class LoginTokens(db.Model):
|
|||
ua = db.Column(db.String(128))
|
||||
ip = db.Column(db.String(16))
|
||||
|
||||
def __init__(self, uid, username, expiry=datetime.datetime.utcnow(), active=True, ua=None, ip=None):
|
||||
def __init__(self, uid, username, expiry=int(time.time()), active=True, ua=None, ip=None):
|
||||
self.sid = utils.generate_string()
|
||||
self.uid = uid
|
||||
self.username = username
|
||||
self.issued = datetime.datetime.utcnow()
|
||||
self.issued = int(time.time())
|
||||
self.expiry = expiry
|
||||
self.active = active
|
||||
self.ua = ua
|
||||
|
|
|
@ -6,6 +6,7 @@ from models import db, LoginTokens, Users
|
|||
from decorators import api_wrapper, WebException
|
||||
from schemas import verify_to_schema, check
|
||||
|
||||
import datetime
|
||||
import logger
|
||||
import re
|
||||
import requests
|
||||
|
@ -43,7 +44,7 @@ def user_register():
|
|||
|
||||
return { "success": 1, "message": "Success!" }
|
||||
|
||||
@blueprint.route("/logout", methods=["GET", "POST"])
|
||||
@blueprint.route("/logout", methods=["POST"])
|
||||
@api_wrapper
|
||||
def user_logout():
|
||||
sid = session["sid"]
|
||||
|
@ -80,6 +81,36 @@ def user_status():
|
|||
}
|
||||
return result
|
||||
|
||||
@blueprint.route("/info", methods=["POST"])
|
||||
@api_wrapper
|
||||
def user_info():
|
||||
logged_in = is_logged_in()
|
||||
username = utils.flat_multi(request.form).get("username")
|
||||
if username is None:
|
||||
if logged_in:
|
||||
username = session["username"]
|
||||
if username is None:
|
||||
raise WebException("No user specified.")
|
||||
me = username.lower() == session["username"].lower()
|
||||
user = get_user(username_lower=username.lower()).first()
|
||||
if user is None:
|
||||
raise WebException("User not found.")
|
||||
|
||||
show_email = me if logged_in else False
|
||||
userdata = {
|
||||
"user_found": True,
|
||||
"name": user.name,
|
||||
"username": user.username,
|
||||
"type": ["Student", "Instructor", "Observer"][user.utype - 1],
|
||||
"admin": user.admin,
|
||||
"registertime": datetime.datetime.fromtimestamp(user.registertime).isoformat() + "Z",
|
||||
"me": me,
|
||||
"show_email": show_email
|
||||
}
|
||||
if show_email:
|
||||
userdata["email"] = user.email
|
||||
return { "success": 1, "user": userdata }
|
||||
|
||||
##################
|
||||
# USER FUNCTIONS #
|
||||
##################
|
||||
|
@ -148,7 +179,7 @@ def login_user(username, password):
|
|||
|
||||
session["sid"] = token.sid
|
||||
session["username"] = token.username
|
||||
session["admin"] = user.utype == 0
|
||||
session["admin"] = user.admin == True
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ import api
|
|||
import config
|
||||
import json
|
||||
|
||||
from api.decorators import api_wrapper
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
app.config["SQLALCHEMY_DATABASE_URI"] = config.SQLALCHEMY_DATABASE_URI
|
||||
|
@ -24,8 +26,9 @@ app.register_blueprint(api.problem.blueprint, url_prefix="/api/problem")
|
|||
api.logger.initialize_logs()
|
||||
|
||||
@app.route("/api")
|
||||
@api_wrapper
|
||||
def api_main():
|
||||
return json.dumps({ "success": 1, "message": "The API is online." })
|
||||
return { "success": 1, "message": "The API is online." }
|
||||
|
||||
if __name__ == "__main__":
|
||||
with app.app_context():
|
||||
|
|
|
@ -11,3 +11,13 @@
|
|||
* {
|
||||
font-family: "Proxima Nova";
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 10px;
|
||||
> .tab-pane {
|
||||
display: none;
|
||||
}
|
||||
> .active {
|
||||
display: block;
|
||||
}
|
||||
}
|
|
@ -39,6 +39,16 @@
|
|||
<div ng-show="config.navbar['competition_started']==true">
|
||||
</div>
|
||||
<li><a href="/chat">Chat</a></li>
|
||||
<li class="dropdown" ng-show="config.navbar['admin']==true">
|
||||
<a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Admin <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="/admin/stats">Statistics</a></li>
|
||||
<li><a href="/admin/problems">Problem Editor</a></li>
|
||||
<li><a href="/admin/teams">Team Management</a></li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><a href="/admin/settings">CTF Settings</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ config.navbar["username"] }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
|
@ -55,7 +65,7 @@
|
|||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="mainContent" class="ui container">
|
||||
<div id="mainContent" class="container">
|
||||
<div ng-view></div>
|
||||
</div>
|
||||
|
||||
|
@ -64,7 +74,8 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.min.js" integrity="sha384-r1y8TJcloKTvouxnYsi4PJAx+nHNr90ibsEn3zznzDzWBN9X3o3kbHLSgcIPtzAp" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-rc.0/angular-route.min.js" integrity="sha384-9MZDoFf10trgrfsQOs9GJhf/mP/sh5weVp3FDSi8h/4TEaV6dloEDkpxGTaOmAs6" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.9.3/js/bootstrap-select.min.js" integrity="sha384-1qZEXZBmj54fSiiWT8bZQGEpCumJWDrAoEqMdg6N5bTTLCkU5RXoNeUsKWekRYob" crossorigin="anonymous"></script>
|
||||
<script src="js/easyctf.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.4.3/jquery.timeago.min.js" integrity="sha384-Bap3DetwPgo4GEFvaIDVSIrz5G0mUAUsfCUcEsi+JrrNu7dyj3gBmuAG4hDIGg/4" crossorigin="anonymous"></script>
|
||||
<script src="/js/easyctf.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -16,6 +16,10 @@ app.config(function($routeProvider, $locationProvider) {
|
|||
templateUrl: "pages/learn.html",
|
||||
controller: "mainController"
|
||||
})
|
||||
.when("/chat", {
|
||||
templateUrl: "pages/chat.html",
|
||||
controller: "mainController"
|
||||
})
|
||||
.when("/register", {
|
||||
templateUrl: "pages/register.html",
|
||||
controller: "mainController"
|
||||
|
@ -26,11 +30,19 @@ app.config(function($routeProvider, $locationProvider) {
|
|||
})
|
||||
.when("/profile", {
|
||||
templateUrl: "pages/profile.html",
|
||||
controller: "mainController"
|
||||
controller: "profileController"
|
||||
})
|
||||
.when("/logout", {
|
||||
templateUrl: "pages/blank.html",
|
||||
controller: "logoutController"
|
||||
})
|
||||
.when("/admin/problems", {
|
||||
templateUrl: "pages/admin/problems.html",
|
||||
controller: "adminProblemsController"
|
||||
})
|
||||
.otherwise({
|
||||
templateUrl: "pages/404.html",
|
||||
controller: "mainController"
|
||||
});
|
||||
$locationProvider.html5Mode(true);
|
||||
});
|
||||
|
@ -41,11 +53,14 @@ app.controller("mainController", ["$scope", "$http", function($scope, $http) {
|
|||
if (result["success"] == 1) {
|
||||
$scope.config.navbar.logged_in = result["logged_in"];
|
||||
$scope.config.navbar.username = result["username"];
|
||||
$scope.config.navbar.admin = result["admin"];
|
||||
} else {
|
||||
$scope.config.navbar.logged_in = false;
|
||||
}
|
||||
$scope.$apply();
|
||||
}).fail(function() {
|
||||
$scope.config.navbar.logged_in = false;
|
||||
$scope.$apply();
|
||||
});
|
||||
}]);
|
||||
|
||||
|
@ -55,6 +70,21 @@ app.controller("logoutController", function() {
|
|||
});
|
||||
});
|
||||
|
||||
app.controller("profileController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||
$controller("mainController", { $scope: $scope });
|
||||
$.post("/api/user/info", function(result) {
|
||||
if (result["success"] == 1) {
|
||||
$scope.user = result["user"];
|
||||
}
|
||||
$scope.$apply();
|
||||
$(".timeago").timeago();
|
||||
});
|
||||
}]);
|
||||
|
||||
app.controller("adminProblemsController", ["$controller", "$scope", "$http", function($controller, $scope, $http) {
|
||||
$controller("mainController", { $scope: $scope });
|
||||
}]);
|
||||
|
||||
function display_message(containerId, alertType, message, callback) {
|
||||
$("#" + containerId).html("<div class=\"alert alert-" + alertType + "\">" + message + "</div>");
|
||||
$("#" + containerId).hide().slideDown("fast", "swing", function() {
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
function login_form(email, password) {
|
||||
$("#login").attr("disabled", "disabled");
|
||||
$.post("/api/user/login", {
|
||||
email: email,
|
||||
password: password
|
||||
}, function(data) {
|
||||
if (data.success == 1) {
|
||||
display_message("status", "success", "Success!", function() {
|
||||
$("#login").removeAttr("disabled");
|
||||
window.location = "#/account";
|
||||
});
|
||||
} else {
|
||||
display_message("status", "danger", data.message, function() {$("#login").removeAttr("disabled");});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
$("#registration-form").on("submit", function(e) {
|
||||
e.preventDefault();
|
||||
register($("#name").val(), $("#username").val(), $("#password").val(), $("#password_confirm").val(), $("#email").val(), $("#g-recaptcha-response").val());
|
||||
});
|
||||
|
||||
function register(name, username, password, password_confirm, email, captcha_response) {
|
||||
$("#register").attr("disabled", "disabled");
|
||||
$.post("/api/user/register", {
|
||||
name: name,
|
||||
username: username,
|
||||
password: password,
|
||||
password_confirm: password_confirm,
|
||||
email: email,
|
||||
captcha_response: captcha_response
|
||||
}, function(data) {
|
||||
$("#status").text(data.message);
|
||||
if (data.success == 1) {
|
||||
display_message("status", "success", "Success!", function() {
|
||||
$("#register").removeAttr("disabled");
|
||||
window.location = "#/login";
|
||||
});
|
||||
} else {
|
||||
display_message("status", "danger", data.message, function() {$("#register").removeAttr("disabled")});
|
||||
grecaptcha.reset();
|
||||
}
|
||||
});
|
||||
}
|
5
web/pages/404.html
Normal file
5
web/pages/404.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<div class="page-header">
|
||||
<h1>404: Page Not Found</h1>
|
||||
</div>
|
||||
|
||||
<p>Go away. Stop snooping around, you little creep.</p>
|
|
@ -0,0 +1,55 @@
|
|||
<div ng-show="user['user_found']==true">
|
||||
<div class="row">
|
||||
<div class="col-sm-3 col-xs-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<img src="//www.gravatar.com/avatar/?size=512" id="avatar" style="max-width:100%;" />
|
||||
<small style="display:block;text-align:right;" ng-show="user['me']==true"><a href="http://en.gravatar.com/emails/" target="_blank">Edit Picture</a></small>
|
||||
<h2 style="margin:0px;font-weight:bold;font-size:2em;">{{ user.name }}</h2>
|
||||
<small style="display:block;font-size:1.5em;">@{{ user.username }}</small>
|
||||
<hr>
|
||||
<div>
|
||||
<i class="fa fa-fw fa-user"></i>
|
||||
{{ user.type }}
|
||||
<div class="label label-info" ng-show="user['admin']==true">ADMIN</div>
|
||||
</div>
|
||||
<div ng-show="user['show_email']==true">
|
||||
<i class="fa fa-fw fa-envelope"></i>
|
||||
<a style="color:#666;" href="mailto:{{ user.email }}">
|
||||
<span id="email">{{ user.email }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<i class="fa fa-fw fa-clocK-o"></i>
|
||||
Joined <time class="timeago" datetime="{{ user.registertime }}"></time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-9 col-xs-12">
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li role="presentation" class="active"><a href="#profile" aria-controls="profile" role="tab" data-toggle="tab">Profile</a></li>
|
||||
<li role="presentation"><a href="#activity" aria-controls="activity" role="tab" data-toggle="tab">Activity</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div role="tabpanel" class="tab-pane active" id="profile">
|
||||
</div>
|
||||
<div role="tabpanel" class="tab-pane" id="activity">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-show="user['user_found']==false">
|
||||
<div class="page-header">
|
||||
<h1>User Not Found</h1>
|
||||
</div>
|
||||
|
||||
<p>The user you were looking for doesn't exist. Check to make sure you've spelled the name right.</p>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("ul[role=tablist]").tab();
|
||||
});
|
||||
</script>
|
Loading…
Reference in a new issue