login/register system with tokens

This commit is contained in:
Michael Zhang 2016-01-06 00:15:57 -06:00
parent 500462ec00
commit 252ed8ab9b
17 changed files with 547 additions and 353 deletions

View file

@ -13,7 +13,7 @@ server {
# }
# Put all the pages here so Angular doesn't fail.
location ~^/(about|login|register|scoreboard|chat|updates|problems|programming|shell|rules|admin/problems)$ {
location ~^/(about|chat|help|learn|login|profile|register|scoreboard|settings|team)$ {
default_type text/html;
try_files /index.html /index.html;
}

View file

@ -4,3 +4,4 @@ Flask-SQLAlchemy
SQLAlchemy
gunicorn
requests
voluptuous

View file

@ -19,7 +19,7 @@ def api_wrapper(f):
except Exception as error:
response = 200
traceback.print_exc()
web_result = { "success": 0, "message": "Something went wrong! Please notify us about this immediately. %s: %s" % (error, traceback.format_exc()) }
web_result = { "success": 0, "message": "Something went wrong! Please notify us about this immediately.", "error": [ str(error), traceback.format_exc() ] }
return json.dumps(web_result), response, { "Content-Type": "application/json; charset=utf-8" }
return wrapper

View file

@ -1,73 +1,99 @@
from flask.ext.sqlalchemy import SQLAlchemy
import datetime
import utils
db = SQLAlchemy()
class Users(db.Model):
uid = db.Column(db.Integer, primary_key=True)
tid = db.Column(db.Integer)
name = db.Column(db.String(64))
username = db.Column(db.String(64), unique=True)
username_lower = db.Column(db.String(64), unique=True)
email = db.Column(db.String(64), unique=True)
password = db.Column(db.String(128))
admin = db.Column(db.Boolean, default=False)
uid = db.Column(db.Integer, unique=True, primary_key=True)
tid = db.Column(db.Integer)
name = db.Column(db.String(64))
username = db.Column(db.String(64), unique=True)
username_lower = db.Column(db.String(64), unique=True)
email = db.Column(db.String(64), unique=True)
password = db.Column(db.String(128))
utype = db.Column(db.Integer)
def __init__(self, name, username, email, password):
self.name = name
self.username = username
self.username_lower = username.lower()
self.email = email.lower()
self.password = utils.hash_password(password)
def __init__(self, name, username, email, password, utype=1):
self.name = name
self.username = username
self.username_lower = username.lower()
self.email = email.lower()
self.password = utils.hash_password(password)
self.utype = utype
class Teams(db.Model):
tid = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
join_code = db.Column(db.String(128), unique=True)
school = db.Column(db.Text)
size = db.Column(db.Integer)
score = db.Column(db.Integer)
observer = db.Column(db.Boolean)
owner = db.Column(db.Integer)
tid = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
join_code = db.Column(db.String(128), unique=True)
school = db.Column(db.Text)
size = db.Column(db.Integer)
score = db.Column(db.Integer)
observer = db.Column(db.Boolean)
owner = db.Column(db.Integer)
def __init__(self, name, school):
self.name = name
self.school = school
def __init__(self, name, school):
self.name = name
self.school = school
class Problems(db.Model):
pid = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
category = db.Column(db.String(128))
description = db.Column(db.Text)
hint = db.Column(db.Text)
flag = db.Column(db.Text)
disabled = db.Column(db.Boolean, default=False)
value = db.Column(db.Integer)
solves = db.Column(db.Integer, default=0)
pid = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
category = db.Column(db.String(128))
description = db.Column(db.Text)
hint = db.Column(db.Text)
flag = db.Column(db.Text)
disabled = db.Column(db.Boolean, default=False)
value = db.Column(db.Integer)
solves = db.Column(db.Integer, default=0)
def __init__(self, name, category, description, hint, flag, value):
self.name = name
self.category = category
self.description = description
self.hint = hint
self.flag = flag
self.value = value
def __init__(self, name, category, description, hint, flag, value):
self.name = name
self.category = category
self.description = description
self.hint = hint
self.flag = flag
self.value = value
class Files(db.Model):
fid = db.Column(db.Integer, primary_key=True)
pid = db.Column(db.Integer)
location = db.Column(db.Text)
fid = db.Column(db.Integer, primary_key=True)
pid = db.Column(db.Integer)
location = db.Column(db.Text)
def __init__(self, pid, location):
self.pid = pid
self.location = location
def __init__(self, pid, location):
self.pid = pid
self.location = location
class Solves(db.Model):
sid = db.Column(db.Integer, primary_key=True)
pid = db.Column(db.Integer)
tid = db.Column(db.Integer)
date = db.Column(db.Integer, default=utils.get_time_since_epoch())
sid = db.Column(db.Integer, primary_key=True)
pid = db.Column(db.Integer)
tid = db.Column(db.Integer)
date = db.Column(db.Integer, default=utils.get_time_since_epoch())
def __init__(self, pid, tid):
self.pid = pid
self.tid = tid
def __init__(self, pid, tid):
self.pid = pid
self.tid = tid
##########
# TOKENS #
##########
class LoginTokens(db.Model):
sid = db.Column(db.String(64), unique=True, primary_key=True)
uid = db.Column(db.Integer)
username = db.Column(db.String(32))
active = db.Column(db.Boolean)
issued = db.Column(db.Integer)
expiry = db.Column(db.Integer)
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):
self.sid = utils.generate_string()
self.uid = uid
self.username = username
self.issued = datetime.datetime.utcnow()
self.expiry = expiry
self.active = active
self.ua = ua
self.ip = ip

24
server/api/schemas.py Normal file
View file

@ -0,0 +1,24 @@
import api
import re
from voluptuous import Required, Length, Schema, Invalid, MultipleInvalid
from decorators import WebException
def check(*callback_tuples):
def v(value):
for callbacks, msg in callback_tuples:
for callback in callbacks:
try:
result = callback(value)
if not result and type(result) == bool:
raise Invalid(msg)
except Exception:
raise WebException(msg)
return value
return v
def verify_to_schema(schema, data):
try:
schema(data)
except MultipleInvalid as error:
raise WebException(str(error))

View file

@ -1,96 +1,170 @@
from flask import Blueprint, session, request, redirect, url_for
from flask import current_app as app
from voluptuous import Schema, Length, Required
from models import db, Users
from decorators import api_wrapper
from models import db, LoginTokens, Users
from decorators import api_wrapper, WebException
from schemas import verify_to_schema, check
import logger
import re
import requests
import utils
###############
# USER ROUTES #
###############
blueprint = Blueprint("user", __name__)
@blueprint.route("/register", methods=["POST"])
@api_wrapper
def user_register():
# if not validate_captcha(request.form):
# return { "success": 0, "message": "Please do the captcha." }
params = utils.flat_multi(request.form)
name = request.form["name"]
username = request.form["username"]
password = request.form["password"]
password_confirm = request.form["password_confirm"]
email = request.form["email"]
name = params.get("name")
email = params.get("email")
username = params.get("username")
password = params.get("password")
password_confirm = params.get("password_confirm")
utype = int(params.get("type"))
username_exists = Users.query.add_columns("name", "uid").filter_by(username_lower=username.lower()).first()
email_exists = Users.query.add_columns("name", "uid").filter_by(email=email.lower()).first()
if password != password_confirm:
raise WebException("Passwords do not match.")
verify_to_schema(UserSchema, params)
if password != password_confirm:
return { "success": 0, "message": "Passwords do not match." }
if len(password) > 128:
return { "success": 0, "message": "Password is too long." }
if len(password) == 0:
return { "success": 0, "message": "Password is too short." }
if len(username) > 64:
return { "success": 0, "message": "Username is too long." }
if username_exists:
return { "success": 0, "message": "Username is already taken." }
if email_exists:
return { "success": 0, "message": "Email has already been used." }
user = Users(name, username, email, password, utype)
with app.app_context():
db.session.add(user)
db.session.commit()
add_user(name, username, email, password)
logger.log("registrations", logger.INFO, "%s registered with %s" % (name.encode("utf-8"), email.encode("utf-8")))
logger.log("registrations", logger.INFO, "%s registered with %s" % (name.encode("utf-8"), email.encode("utf-8")))
login_user(username, password)
return { "success": 1, "message": "Success!" }
return { "success": 1, "message": "Success!" }
@blueprint.route("/logout", methods=["POST"])
@blueprint.route("/logout", methods=["GET", "POST"])
@api_wrapper
def user_logout():
session.clear()
sid = session["sid"]
username = session["username"]
LoginTokens.query.filter_by(sid=sid, username=username).delete()
session.clear()
@blueprint.route("/login", methods=["POST"])
@api_wrapper
def user_login():
email = request.form["email"]
password = request.form["password"]
user = Users.query.filter_by(email=email).first()
if user is None:
return { "success": 0, "message": "Invalid credentials." }
params = utils.flat_multi(request.form)
if utils.check_password(user.password, password):
session["username"] = user.username
if user.admin:
session["admin"] = True
session["logged_in"] = True
return { "success": 1, "message": "Success!" }
else:
return { "success": 0, "message": "Invalid credentials." }
username = params.get("username")
password = params.get("password")
result = login_user(username, password)
if result != True:
raise WebException("Please check if your username/password are correct.")
return { "success": 1, "message": "Success!" }
@blueprint.route("/status", methods=["POST"])
@api_wrapper
def user_status():
status = {
"logged_in": is_logged_in(),
"admin": is_admin(),
"username": session["username"] if is_logged_in() else "",
}
return status
logged_in = is_logged_in()
result = {
"success": 1,
"logged_in": logged_in,
"admin": is_admin(),
"username": session["username"] if logged_in else "",
}
return result
##################
# USER FUNCTIONS #
##################
__check_email_format = lambda email: re.match(".+@.+\..{2,}", email) is not None
__check_ascii = lambda s: all(ord(c) < 128 for c in s)
__check_username = lambda username: get_user(username_lower=username.lower()).first() is None
__check_email = lambda email: get_user(email=email.lower()).first() is None
UserSchema = Schema({
Required("email"): check(
([str, Length(min=4, max=128)], "Your email should be between 4 and 128 characters long."),
([__check_email], "Someone already registered this email."),
([__check_email_format], "Please enter a legit email.")
),
Required("name"): check(
([str, Length(min=4, max=128)], "Your name should be between 4 and 128 characters long.")
),
Required("username"): check(
([str, Length(min=4, max=32)], "Your username should be between 4 and 32 characters long."),
([__check_ascii], "Please only use ASCII characters in your username."),
([__check_username], "This username is taken, did you forget your password?")
),
Required("password"): check(
([str, Length(min=4, max=64)], "Your password should be between 4 and 64 characters long."),
([__check_ascii], "Please only use ASCII characters in your password."),
),
Required("type"): check(
([str, lambda x: x.isdigit()], "Please use the online form.")
),
"notify": str
}, extra=True)
def get_user(username=None, username_lower=None, email=None, uid=None):
with app.app_context():
match = {}
if username != None:
match.update({ "username": username })
elif username_lower != None:
match.update({ "username_lower": username_lower })
elif uid != None:
match.update({ "uid": uid })
elif email != None:
match.update({ "email": email })
# elif api.auth.is_logged_in():
# match.update({ "uid": api.auth.get_uid() })
result = Users.query.filter_by(**match)
return result
def login_user(username, password):
user = get_user(username_lower=username.lower()).first()
if user is None: return False
correct = utils.check_password(user.password, password)
if not correct: return False
useragent = request.headers.get("User-Agent")
ip = request.remote_addr
token = LoginTokens(user.uid, user.username, ua=useragent, ip=ip)
with app.app_context():
db.session.add(token)
db.session.commit()
session["sid"] = token.sid
session["username"] = token.username
session["admin"] = user.utype == 0
return True
def is_logged_in():
return "logged_in" in session and session["logged_in"]
sid = session["sid"]
username = session["username"]
token = LoginTokens.query.filter_by(sid=sid).first()
if token is None: return False
useragent = request.headers.get("User-Agent")
ip = request.remote_addr
if token.username != username: return False
if token.ua != useragent: return False
return True
def is_admin():
return "admin" in session and session["admin"]
def add_user(name, username, email, password):
user = Users(name, username, email, password)
db.session.add(user)
db.session.commit()
return is_logged_in() and "admin" in session and session["admin"]
def validate_captcha(form):
if "captcha_response" not in form:
return False
captcha_response = form["captcha_response"]
data = {"secret": "6Lc4xhMTAAAAACFaG2NyuKoMdZQtSa_1LI76BCEu", "response": captcha_response}
response = requests.post("https://www.google.com/recaptcha/api/siteverify", data=data)
return response.json()["success"]
if "captcha_response" not in form:
return False
captcha_response = form["captcha_response"]
data = {"secret": "6Lc4xhMTAAAAACFaG2NyuKoMdZQtSa_1LI76BCEu", "response": captcha_response}
response = requests.post("https://www.google.com/recaptcha/api/siteverify", data=data)
return response.json()["success"]

View file

@ -3,6 +3,7 @@ import json
import random
import string
import traceback
import unicodedata
from functools import wraps
from werkzeug.security import generate_password_hash, check_password_hash
@ -13,8 +14,8 @@ def hash_password(s):
def check_password(hashed_password, try_password):
return check_password_hash(hashed_password, try_password)
def generate_string(length):
return "".join([random.choice(string.letters + string.digits) for x in range(length)])
def generate_string(length=32, alpha=string.hexdigits):
return "".join([random.choice(alpha) for x in range(length)])
def unix_time_millis(dt):
epoch = datetime.datetime.utcfromtimestamp(0)
@ -22,3 +23,10 @@ def unix_time_millis(dt):
def get_time_since_epoch():
return unix_time_millis(datetime.datetime.now())
def flat_multi(multidict):
flat = {}
for key, values in multidict.items():
value = values[0] if type(values) == list and len(values) == 1 else values
flat[key] = unicodedata.normalize("NFKD", value).encode("ascii", "ignore")
return flat

13
web/css/easyctf.css Normal file
View file

@ -0,0 +1,13 @@
@font-face {
font-family: "Proxima Nova";
src: url("/fonts/ProximaNova.woff2");
}
@font-face {
font-family: "Proxima Nova";
src: url("/fonts/ProximaNovaBold.woff2");
font-weight: bold;
}
* {
font-family: "Proxima Nova";
}

BIN
web/fonts/ProximaNova.woff2 Normal file

Binary file not shown.

Binary file not shown.

View file

@ -8,15 +8,16 @@
<script src="js/d3.v3.min.js" charset="utf-8"></script>
<script src="js/c3.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
<link type="text/css" rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" integrity="sha384-yNuQMX46Gcak2eQsUzmBYgJ3eBeWYNKhnjyiBqLd1vvtE9kuMtgw6bjwN8J0JauQ" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.9.3/css/bootstrap-select.css" integrity="sha384-X6BCz/5YN8CudtAN21w+htVJP5lJNqXP0VBfbGzrX7A/FhjpPIoMtiRRHxRYiKU6" crossorigin="anonymous">
<link rel="stylesheet" href="/css/easyctf.css" />
</head>
<body ng-controller="mainController" class="mainbody">
<nav class="navbar navbar-default navbar-fixed-top">
<div id="style1" class="container-fluid">
<body ng-controller="mainController">
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="main-navbar" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
@ -24,90 +25,45 @@
</button>
<a class="navbar-brand" href="/">EasyCTF</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav navbar-fixed">
<li>
<a href="/about">
<span class="fa fa-info-circle"></span>&nbsp;&nbsp;About</a>
</li>
<li>
<a href="/rules">
<span class="fa fa-book"></span>&nbsp;&nbsp;Rules</a>
</li>
<li>
<a href="/updates">
<span class="fa fa-bullhorn"></span>&nbsp;&nbsp;Updates</a>
</li>
<li>
<a href="/chat">
<span class="fa fa-comments"></span>&nbsp;&nbsp;Chat</a>
</li>
<li>
<a href="/scoreboard">
<span class="fa fa-trophy"></span>&nbsp;&nbsp;Scoreboard</a>
</li>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/about">About</a></li>
<li><a href="/scoreboard">Scoreboard</a></li>
<li><a href="/learn">Learn</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-pencil"></span>&nbsp;&nbsp;Problems <span class="caret"></span></a>
<ul class="dropdown-menu" id="menubox">
<li>
<a href="/problems">
<span class="fa fa-pencil"></span>&nbsp;&nbsp;Problems</a>
</li>
<li>
<a href="/programming">
<span class="fa fa-code"></span>&nbsp;&nbsp;Programming</a>
</li>
<li>
<a href="/shell">
<span class="fa fa-terminal"></span>&nbsp;&nbsp;Shell</a>
</li>
</ul>
</li>
<li id="login_link" style="display:none">
<a href="/login">
<span class="fa fa-sign-in"></span>&nbsp;&nbsp;Login</a>
</li>
<li id="register_link" style="display:none">
<a href="/register">
<span class="fa fa-pencil"></span>&nbsp;&nbsp;Register</a>
</li>
<li id="account_link" style="display:none">
<a href="/account">
<span class="fa fa-pencil"></span>&nbsp;&nbsp;Account</a>
</li>
<li id="logout" style="display:none">
<a href="/api/user/logout">
<span class="fa fa-pencil"></span>&nbsp;&nbsp;Logout</a>
</li>
<li id="admin_dropdown" style="display:none">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><span class="glyphicon glyphicon-pencil"></span>&nbsp;&nbsp;Admin<span class="caret"></span></a>
<ul class="dropdown-menu" id="menubox">
<li>
<a href="/admin/problems">
<span class="fa fa-pencil"></span>&nbsp;&nbsp;Problems</a>
</li>
<li>
<a href="/admin/teams">
<span class="fa fa-code"></span>&nbsp;&nbsp;Teams</a>
</li>
<ul class="nav navbar-nav navbar-right" ng-show="config.navbar['logged_in']==false">
<li><a href="/register">Register</a></li>
<li><a href="/login">Login</a></li>
</ul>
<ul class="nav navbar-nav navbar-right" ng-show="config.navbar['logged_in']==true">
<div ng-show="config.navbar['competition_started']==true">
</div>
<li><a href="/chat">Chat</a></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">
<li><a href="/profile">Profile</a></li>
<li><a href="/team">Team</a></li>
<li><a href="/help">Help</a></li>
<li role="separator" class="divider"></li>
<li><a href="/settings">Settings</a></li>
<li><a href="/logout">Logout</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<br>
<br>
<div id="mainContent" class="ui container">
<div ng-view></div>
</div>
<script src="https://code.jquery.com/jquery-2.1.4.min.js" integrity="sha384-R4/ztc4ZlRqWjqIuvf6RX5yb/v90qNGx6fS48N0tRxiGkqveZETq72KgDVJCp2TC" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js" integrity="sha384-0mSbJDEHialfmuBBQP6A4Qrprq5OVfW37PRR3j5ELqxss1yVqOtnepnHVP9aJ7xS" crossorigin="anonymous"></script>
<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>
</body>

View file

@ -1,91 +1,110 @@
var app = angular.module("easyctf", [ "ngRoute" ]);
app.config(function($routeProvider, $locationProvider) {
$routeProvider.when("/", {
templateUrl: "pages/home.html",
controller: "mainController"
})
.when("/about", {
templateUrl: "pages/about.html",
controller: "mainController"
})
.when("/register", {
templateUrl: "pages/register.html",
controller: "mainController"
})
.when("/login", {
templateUrl: "pages/login.html",
controller: "mainController"
})
.when("/chat", {
templateUrl: "pages/chat.html",
controller: "mainController"
})
.when("/updates", {
templateUrl: "pages/updates.html",
controller: "mainController"
})
.when("/problems", {
templateUrl: "pages/problems.html",
controller: "mainController"
})
.when("/programming", {
templateUrl: "pages/programming.html",
controller: "mainController"
})
.when("/shell", {
templateUrl: "pages/shell.html",
controller: "mainController"
})
.when("/rules", {
templateUrl: "pages/rules.html",
controller: "mainController"
})
.when("/scoreboard", {
templateUrl: "pages/scoreboard.html",
controller: "mainController"
})
.when("/admin/problems", {
templateUrl: "pages/admin/problems.html",
controller: "mainController"
});
$locationProvider.html5Mode(true);
$routeProvider.when("/", {
templateUrl: "pages/home.html",
controller: "mainController"
})
.when("/about", {
templateUrl: "pages/about.html",
controller: "mainController"
})
.when("/scoreboard", {
templateUrl: "pages/scoreboard.html",
controller: "mainController"
})
.when("/learn", {
templateUrl: "pages/learn.html",
controller: "mainController"
})
.when("/register", {
templateUrl: "pages/register.html",
controller: "mainController"
})
.when("/login", {
templateUrl: "pages/login.html",
controller: "mainController"
})
.when("/profile", {
templateUrl: "pages/profile.html",
controller: "mainController"
})
.when("/logout", {
templateUrl: "pages/blank.html",
controller: "logoutController"
});
$locationProvider.html5Mode(true);
});
app.controller("mainController", function($scope) {
app.controller("mainController", ["$scope", "$http", function($scope, $http) {
$scope.config = { navbar: { } };
$.post("/api/user/status", function(result) {
if (result["success"] == 1) {
$scope.config.navbar.logged_in = result["logged_in"];
$scope.config.navbar.username = result["username"];
} else {
$scope.config.navbar.logged_in = false;
}
}).fail(function() {
$scope.config.navbar.logged_in = false;
});
}]);
app.controller("logoutController", function() {
$.post("/api/user/logout", function(result) {
location.href = "/";
});
});
function display_message(containerId, alertType, message, callback) {
$("#" + containerId).html('<div class="alert alert-' + alertType + '">' + message + '</div>');
$("#" + containerId).hide().slideDown("fast", "swing", function() {
window.setTimeout(function () {
$("#" + containerId).slideUp("fast", "swing", callback);
}, message.length * 75);
});
}
$("#" + containerId).html("<div class=\"alert alert-" + alertType + "\">" + message + "</div>");
$("#" + containerId).hide().slideDown("fast", "swing", function() {
window.setTimeout(function () {
$("#" + containerId).slideUp("fast", "swing", callback);
}, message.length * 75);
});
};
function load_navbar() {
$.post("/api/user/status", {
},
function(data) {
if (data.logged_in) {
$("#logout").show();
$("#account_link").show();
} else {
$("#login_link").show();
$("#register_link").show();
}
if (data.admin) {
$("#admin_dropdown").show();
}
});
}
$.fn.serializeObject = function() {
var a, o;
o = {};
a = this.serializeArray();
$.each(a, function() {
if (o[this.name]) {
if (!o[this.name].push) {
o[this.name] = [o[this.name]];
}
return o[this.name].push(this.value || "");
} else {
return o[this.name] = this.value || "";
}
});
return o;
};
$("#logout").click(function(e) {
e.preventDefault();
$.post("/api/user/logout", {
}, function (data) {
window.location = "/";
});
})
// register page
$(document).ready( load_navbar() );
var register_form = function() {
var input = "#register_form input";
var data = $("#register_form").serializeObject();
$.post("/api/user/register", data, function(result) {
if (result["success"] == 1) {
location.href = "/profile";
} else {
display_message("register_msg", "danger", result["message"])
}
});
};
// login page
var login_form = function() {
var input = "#login_form input";
var data = $("#login_form").serializeObject();
$.post("/api/user/login", data, function(result) {
if (result["success"] == 1) {
location.href = "/profile";
} else {
display_message("login_msg", "danger", result["message"])
}
});
};

View file

@ -1,9 +1,4 @@
$("#login-form").on("submit", function(e) {
e.preventDefault();
login($("#email").val(), $("#password").val());
});
function login(email, password) {
function login_form(email, password) {
$("#login").attr("disabled", "disabled");
$.post("/api/user/login", {
email: email,

0
web/pages/blank.html Normal file
View file

View file

@ -1,14 +1,53 @@
<div class="fade_in text-center">
<h1 class="heading1">Log-in</h1>
<div class="input-group">
<form id="login-form">
<input type="text" class="form-control" placeholder="Email" id="email">
<input type="password" id="password" name="password" placeholder="Password" class="form-control">
<input id="login" type="submit" class="btn btn-lg btn-success" value="Login">
</form>
<div class="container">
<p>&nbsp;</p>
<div class="row">
<div class="col-md-6 col-md-offset-3 col-sm-10 col-sm-offset-1">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">Login</h2>
</div>
<div class="panel-body">
<form class="form-horizontal" onsubmit="login_form(); return false;" id="login_form">
<fieldset>
<div id="login_msg"></div>
</fieldset>
<fieldset class="container-fluid">
<div class="row">
<div class="col-sm-12 form-group">
<label class="col-sm-12" for="username"><small>Username</small></label>
<div class="col-sm-12">
<input class="form-control" type="text" required name="username" id="username" placeholder="Username" autocomplete="off" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 form-group">
<label class="col-sm-12" for="password"><small>Password</small></label>
<div class="col-sm-12">
<input class="form-control" type="password" required name="password" id="password" placeholder="Password" autocomplete="off" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 form-group">
<center>
<input type="submit" class="btn btn-success btn-lg" value="Login" />
</center>
</div>
</div>
<div class="row">
<div class="col-sm-12 form-group">
<center>
<small>
<a href="/forgot">Forgot Password?</a>
</small>
</center>
</div>
</div>
</fieldset>
</form>
</div>
</div>
</div>
</div>
<div id="status"></div>
<a href="#register" class="item">Register</a> |
<a href="#forgot_password" class="item">Forgot Password</a>
</div>
<script src="js/login.js"></script>
</div>

0
web/pages/profile.html Normal file
View file

View file

@ -1,64 +1,103 @@
<div class="fade_in text-center">
<h1 class="heading1">Register</h1>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<script src="js/register.js"></script>
<div class="input-group">
<form id="registration-form">
<div class="row">
<div class="col-md-6">
<br>
<label>Name</label>
<br>
<input type="text" name="name" id="name" autocomplete="off" autofocus placeholder="Name" class="form-control">
<div class="container">
<p>&nbsp;</p>
<div class="row">
<div class="col-md-6 col-md-offset-3 col-sm-10 col-sm-offset-1">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">Register</h2>
</div>
<div class="col-md-6">
<br>
<label>Email</label>
<br>
<input type="email" name="email" id="email" autocomplete="off" placeholder="Email" class="form-control">
<div class="panel-body">
<form class="form-horizontal" onsubmit="register_form(); return false;" id="register_form">
<fieldset>
<div id="register_msg"></div>
</fieldset>
<fieldset class="container-fluid">
<p>Register your individual account here. Make sure that members of your team also register teams. You'll be able to create teams and add members after you register!</p>
<div class="row">
<div class="col-sm-12 form-group">
<label class="col-sm-12" for="name"><small>Your Name</small></label>
<div class="col-sm-12">
<input class="form-control" type="text" required name="name" id="name" placeholder="Your Name" autocomplete="off" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 form-group">
<label class="col-sm-12" for="email"><small>Email</small></label>
<div class="col-sm-12">
<input class="form-control" type="email" required name="email" id="email" placeholder="michael@example.com" autocomplete="off" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 form-group">
<label class="col-sm-12" for="username"><small>Username</small></label>
<div class="col-sm-12">
<input class="form-control" type="text" required name="username" id="username" placeholder="Username" autocomplete="off" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 form-group">
<label class="col-sm-12" for="password"><small>Password</small></label>
<div class="col-sm-6">
<input class="form-control" type="password" required name="password" id="password" placeholder="Password" autocomplete="off" />
</div>
<div class="col-sm-6">
<input class="form-control" type="password" required name="password_confirm" id="password_confirm" placeholder="Confirm Password" autocomplete="off" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 form-group">
<label class="col-sm-12" for="type"><small>Who are you?</small></label>
<div class="col-sm-12">
<select name="type" id="type" class="selectpicker" data-width="100%">
<option value="1">US Middle/High School Student</option>
<option value="2">Middle/High School Teacher</option>
<option value="3">Non-US/Non-Student/Observer</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 form-group">
<label class="col-sm-12"><small>Receive email notifications about future CTFs?</small></label>
<div class="col-sm-12">
<div class="btn-group" data-toggle="buttons">
<label class="btn btn-default active" id="notify_btn">
<input type="checkbox" autocomplete="off" checked name="notify" id="notify"><span id="notify_box" class="fa fa-fw fa-check-square"></span>&nbsp;Get notified about future CTFs
</label>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12 form-group">
<center>
<input type="submit" class="btn btn-primary btn-lg" value="Register" />
</center>
</div>
</div>
</fieldset>
</form>
</div>
<div class="col-md-6">
<br>
<label>Username</label>
<br>
<input type="text" name="username" id="username" autocomplete="off" placeholder="Username" class="form-control">
</div>
<div class="col-md-6">
<br>
<label>Password</label>
<br>
<input type="password" name="password" id="password" autocomplete="off" placeholder="Password" class="form-control">
</div>
<div class="col-md-12">
<br>
<label>Confirm Password</label>
<br>
<input type="password" name="password_confirm" id="password_confirm" autocomplete="off" placeholder="Confirm Password" class="form-control">
</div>
<br>
<div class="col-md-12">
<br>
<label>Captcha</label>
<br>
<div class="g-recaptcha" data-sitekey="6Lc4xhMTAAAAAIaiF3yEWGbHRaGgMg4FHor61p1G"></div>
<br>
</div>
<label>I have read and I agree to <a href="/rules" target="_blank">EasyCTF Rules</a>.</label>
<br>
<br>
<input class="style2" type="checkbox" class="form-control" value="didRead">
<br>
<input id="register" class="style3" type="submit" class="btn btn-lg btn-success" value="Register">
</div>
</form>
<div id="status"></div>
</div>
</div>
</div>
<script type="text/javascript">
$(".selectpicker").selectpicker();
var checkedClass = "fa fa-check-square";
var uncheckedClass = "fa fa-square";
$("#notify_btn").click(function() {
if($("#notify").is(":checked")) {
$("#notify_box").removeClass(checkedClass);
$("#notify_box").addClass(uncheckedClass);
} else {
$("#notify_box").removeClass(uncheckedClass);
$("#notify_box").addClass(checkedClass);
}
});
</script>