mirror of
https://github.com/avatao-content/test-tutorial-framework
synced 2024-11-15 02:37:17 +00:00
Strip old sqli example app and replace it with a simple login service
This commit is contained in:
parent
54b299e7ef
commit
1a295d5fd6
@ -1,19 +1,26 @@
|
|||||||
FROM eu.gcr.io/avatao-challengestore/tutorial-framework
|
FROM eu.gcr.io/avatao-challengestore/tutorial-framework
|
||||||
|
|
||||||
|
# Define variables to use later
|
||||||
ENV TFW_SERVER_DIR="/srv/.tfw" \
|
ENV TFW_SERVER_DIR="/srv/.tfw" \
|
||||||
TFW_LOGIN_APP_DIR="/tmp/source_code_server" \
|
TFW_LOGIN_SERVICE_DIR="/srv/login_service" \
|
||||||
TFW_IDE_WD="/home/${AVATAO_USER}/workdir" \
|
TFW_IDE_WD="/home/${AVATAO_USER}/workdir" \
|
||||||
TFW_TERMINADO_WD="/home/${AVATAO_USER}/workdir"
|
TFW_TERMINADO_WD="/home/${AVATAO_USER}/workdir"
|
||||||
|
|
||||||
|
# Copy TFW related stuff to a dedicated directory
|
||||||
COPY solvable/src ${TFW_SERVER_DIR}/
|
COPY solvable/src ${TFW_SERVER_DIR}/
|
||||||
COPY solvable/src/source_code_server/server.py ${TFW_LOGIN_APP_DIR}/
|
|
||||||
COPY solvable/src/source_code_server/users.db ${TFW_LOGIN_APP_DIR}/
|
|
||||||
COPY solvable/src/source_code_server/login_component.py ${TFW_IDE_WD}/
|
|
||||||
|
|
||||||
RUN chown -R ${AVATAO_USER} ${TFW_IDE_WD} &&\
|
# Copy webservice to a dedicated directory
|
||||||
chmod -R 755 ${TFW_IDE_WD} &&\
|
COPY solvable/src/webservice/ ${TFW_LOGIN_SERVICE_DIR}/
|
||||||
chown -R root ${TFW_SERVER_DIR} &&\
|
ADD solvable/src/webservice/frontend-deps.tar ${TFW_LOGIN_SERVICE_DIR}/static
|
||||||
chmod -R 700 ${TFW_SERVER_DIR}
|
|
||||||
|
|
||||||
|
# Create IDE directory, add a file to it and give proper permissions to AVATAO_USER
|
||||||
|
RUN mkdir -p ${TFW_IDE_WD} &&\
|
||||||
|
echo "This is a file in ${TFW_IDE_WD}" > ${TFW_IDE_WD}/text.txt &&\
|
||||||
|
chown -R ${AVATAO_USER}: ${TFW_IDE_WD} && chmod -R 755 ${TFW_IDE_WD}
|
||||||
|
|
||||||
|
# Hide TFW related code from user
|
||||||
|
RUN chown -R root:root ${TFW_SERVER_DIR} && chmod -R 700 ${TFW_SERVER_DIR}
|
||||||
|
|
||||||
|
# Make AVATAO_USER's home writeable and set it as WORKDIR
|
||||||
VOLUME ["/home/${AVATAO_USER}"]
|
VOLUME ["/home/${AVATAO_USER}"]
|
||||||
WORKDIR /home/${AVATAO_USER}
|
WORKDIR /home/${AVATAO_USER}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
location = /login {
|
|
||||||
proxy_pass http://127.0.0.1:${TFW_LOGIN_APP_PORT};
|
|
||||||
}
|
|
3
solvable/nginx/webservice.conf
Normal file
3
solvable/nginx/webservice.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
location /webservice/ {
|
||||||
|
proxy_pass http://127.0.0.1:6666/;
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
import sqlite3
|
|
||||||
|
|
||||||
|
|
||||||
def get_db():
|
|
||||||
return sqlite3.connect('users.db')
|
|
||||||
|
|
||||||
|
|
||||||
def authorize_login(email, password):
|
|
||||||
"""
|
|
||||||
This method checks if a user is authorized and has admin privileges.
|
|
||||||
:param email: The email address of the user.
|
|
||||||
:param password: The password of the user.
|
|
||||||
:return: A tuple, the first element is the email address if the user exists,
|
|
||||||
and None if they don't; the second element is a boolean, which is True if
|
|
||||||
the user has admin privileges.
|
|
||||||
"""
|
|
||||||
conn = get_db()
|
|
||||||
sql_statement = '''SELECT email, is_admin FROM users
|
|
||||||
WHERE email="{}" AND password="{}"'''
|
|
||||||
# The problem with this approach is that it substitutes any value received
|
|
||||||
# from the user, even if it is a valid SQL statement!
|
|
||||||
result = conn.execute(sql_statement.format(email, password)).fetchone()
|
|
||||||
if result is None:
|
|
||||||
return None, False
|
|
||||||
else:
|
|
||||||
email, is_admin = result
|
|
||||||
return email, is_admin == 1
|
|
@ -1,27 +0,0 @@
|
|||||||
import json, sys
|
|
||||||
from tornado.ioloop import IOLoop
|
|
||||||
from tornado.web import RequestHandler, Application
|
|
||||||
|
|
||||||
from tfw.config import TFWENV
|
|
||||||
|
|
||||||
sys.path.append(TFWENV.IDE_WD)
|
|
||||||
from login_component import authorize_login
|
|
||||||
|
|
||||||
|
|
||||||
class LoginHandler(RequestHandler):
|
|
||||||
def post(self, *args, **kwargs):
|
|
||||||
request = json.loads(self.request.body)
|
|
||||||
email, is_admin = authorize_login(
|
|
||||||
request['email'],
|
|
||||||
request['password']
|
|
||||||
)
|
|
||||||
self.write({
|
|
||||||
'email': email,
|
|
||||||
'is_admin': is_admin
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
application = Application([(r'/login', LoginHandler)])
|
|
||||||
application.listen(TFWENV.LOGIN_APP_PORT)
|
|
||||||
IOLoop.instance().start()
|
|
Binary file not shown.
BIN
solvable/src/webservice/frontend-deps.tar
Normal file
BIN
solvable/src/webservice/frontend-deps.tar
Normal file
Binary file not shown.
120
solvable/src/webservice/server.py
Normal file
120
solvable/src/webservice/server.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
from os import urandom, getenv
|
||||||
|
from os.path import exists, join, dirname, realpath
|
||||||
|
from hashlib import sha512
|
||||||
|
|
||||||
|
import sqlite3
|
||||||
|
from flask import Flask, render_template, request, g, session, url_for
|
||||||
|
|
||||||
|
|
||||||
|
def setup_db(filename):
|
||||||
|
connection = sqlite3.connect(filename)
|
||||||
|
cur = connection.cursor()
|
||||||
|
cur.execute('''CREATE TABLE users
|
||||||
|
(
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
username TEXT NOT NULL,
|
||||||
|
pwhash TEXT NOT NULL
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
connection.commit()
|
||||||
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
|
BASEURL = getenv('BASEURL', '')
|
||||||
|
DBFILE = join(dirname(realpath(__file__)), '.db.db')
|
||||||
|
SALT = 'justsomerandombytes'.encode()
|
||||||
|
if not exists(DBFILE):
|
||||||
|
setup_db(DBFILE)
|
||||||
|
app = Flask(__name__)
|
||||||
|
app.secret_key = urandom(32)
|
||||||
|
|
||||||
|
|
||||||
|
def get_url(endpoint):
|
||||||
|
return f'{BASEURL}{url_for(endpoint)}'
|
||||||
|
app.jinja_env.globals.update(get_url=get_url)
|
||||||
|
|
||||||
|
|
||||||
|
@app.before_request
|
||||||
|
def init_database():
|
||||||
|
g.db = sqlite3.connect(DBFILE)
|
||||||
|
g.db.row_factory = sqlite3.Row
|
||||||
|
|
||||||
|
|
||||||
|
@app.teardown_request
|
||||||
|
def close_database(exception):
|
||||||
|
db = getattr(g, 'db', None)
|
||||||
|
if db is not None:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/', methods=['GET', 'POST'])
|
||||||
|
def index():
|
||||||
|
if request.method == 'POST':
|
||||||
|
cur = g.db.cursor()
|
||||||
|
cur.execute('SELECT * FROM users WHERE username=? AND pwhash=?',
|
||||||
|
[request.form['username'], sha512(request.form['password'].encode()+SALT).hexdigest()])
|
||||||
|
query = cur.fetchone()
|
||||||
|
if not query:
|
||||||
|
return render_template('login.html', alert='Invalid credentials!')
|
||||||
|
else:
|
||||||
|
session['logged_in'] = True
|
||||||
|
session['username'] = request.form['username']
|
||||||
|
return render_template('internal.html')
|
||||||
|
|
||||||
|
if session.get('logged_in'):
|
||||||
|
return render_template('internal.html')
|
||||||
|
return render_template('login.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
|
def register():
|
||||||
|
if request.method == 'POST':
|
||||||
|
cur = g.db.cursor()
|
||||||
|
|
||||||
|
if not request.form['username'] or not request.form['password'] or not request.form['passwordconfirm']:
|
||||||
|
return render_template('register.html', alert='You need to fill everything.')
|
||||||
|
if request.form['password'] != request.form['passwordconfirm']:
|
||||||
|
return render_template('register.html', alert='Passwords do not match! Please try again.')
|
||||||
|
|
||||||
|
cur.execute('SELECT * FROM users WHERE username=?', [request.form['username']])
|
||||||
|
if cur.fetchone() is not None:
|
||||||
|
return render_template('register.html', alert='Username already in use.')
|
||||||
|
|
||||||
|
cur.execute('INSERT INTO users(username, pwhash) VALUES(?,?)',
|
||||||
|
[request.form['username'],
|
||||||
|
sha512(request.form['password'].encode()+SALT).hexdigest()])
|
||||||
|
g.db.commit()
|
||||||
|
|
||||||
|
return render_template('login.html', success='Account "{}" successfully registered. You can log in now!'.format(request.form['username']))
|
||||||
|
|
||||||
|
return render_template('register.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/logout')
|
||||||
|
def logout():
|
||||||
|
try:
|
||||||
|
session.pop('logged_in')
|
||||||
|
session.pop('username')
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return render_template('login.html')
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(401)
|
||||||
|
@app.errorhandler(404)
|
||||||
|
@app.route('/error')
|
||||||
|
def error(error):
|
||||||
|
return render_template('error.html', error=error), error.code
|
||||||
|
|
||||||
|
|
||||||
|
# 500 needs a separate handler, as Flask would print
|
||||||
|
# the actual piece of code that caused the exception
|
||||||
|
# for some bizarre reason
|
||||||
|
@app.errorhandler(500)
|
||||||
|
@app.route('/error')
|
||||||
|
def servererror(error):
|
||||||
|
return render_template('error.html', error='500: Internal server error'), 500
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='127.0.0.1', debug=False, port=6666)
|
44
solvable/src/webservice/templates/base.html
Normal file
44
solvable/src/webservice/templates/base.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="static/bootstrap-3.3.7/css/bootstrap.min.css">
|
||||||
|
<script src="static/jquery-3.2.1.min.js"></script>
|
||||||
|
<script src="static/bootstrap-3.3.7/js/bootstrap.min.js"></script>
|
||||||
|
<script>
|
||||||
|
{% block script %}
|
||||||
|
{% endblock %}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.jumbotron h1 a
|
||||||
|
{
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
{% block style %}
|
||||||
|
{% endblock %}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="jumbotron text-center">
|
||||||
|
<h1><a href="{{get_url('index')}}">SomeSite</a></h1>
|
||||||
|
<p>We provide stuff!</p>
|
||||||
|
</div>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 col-md-offset-3 col-sm-12">
|
||||||
|
{% if alert or success %}
|
||||||
|
<div class="alert alert-{{'success' if success else 'danger'}} alert-dismissable fade in center-block">
|
||||||
|
<a href="#" class="close" data-dismiss="alert" aria-label="close">×</a>
|
||||||
|
<div class="message">{{alert}}{{success}}</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
6
solvable/src/webservice/templates/error.html
Normal file
6
solvable/src/webservice/templates/error.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h3>Oops, something went wrong! :(</h3>
|
||||||
|
<h2>{{error}}</h2>
|
||||||
|
{% endblock %}
|
9
solvable/src/webservice/templates/internal.html
Normal file
9
solvable/src/webservice/templates/internal.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Welcome, {{session.username}}</h2>
|
||||||
|
<p>You have successfully logged in</p>
|
||||||
|
<a href="{{get_url('logout')}}" class="btn btn-default" >
|
||||||
|
<span class="glyphicon glyphicon-log-out"></span> Logout
|
||||||
|
</a>
|
||||||
|
{% endblock %}
|
27
solvable/src/webservice/templates/login.html
Normal file
27
solvable/src/webservice/templates/login.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
{% block style %}
|
||||||
|
{% endblock %}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
{% block script %}
|
||||||
|
{% endblock %}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Login</h2>
|
||||||
|
<form method="post" action="{{get_url('index')}}">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
|
||||||
|
<input type="text" name="username" class="form-control" placeholder="Username" autofocus required />
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
|
||||||
|
<input type="password" name="password" class="form-control" placeholder="Password" required />
|
||||||
|
</div>
|
||||||
|
<div class="well">Not a member yet? <a href="{{get_url('register')}}">Register here</a>.</div>
|
||||||
|
<input type="submit" class="btn btn-success" value="Login!" />
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
20
solvable/src/webservice/templates/register.html
Normal file
20
solvable/src/webservice/templates/register.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>You can register using this form.</h2>
|
||||||
|
<form method="post" action="{{get_url('register')}}">
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span>
|
||||||
|
<input type="text" name="username" class="form-control" placeholder="Username" autofocus required />
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
|
||||||
|
<input type="password" name="password" class="form-control" placeholder="Password" required />
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span>
|
||||||
|
<input type="password" name="passwordconfirm" class="form-control" placeholder="Confirm password" required />
|
||||||
|
</div>
|
||||||
|
<input type="submit" class="btn btn-success" value="Register!" />
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
@ -1,4 +0,0 @@
|
|||||||
[program:login]
|
|
||||||
directory=%(ENV_TFW_LOGIN_APP_DIR)s
|
|
||||||
command=python3 server.py
|
|
||||||
autostart=false
|
|
5
solvable/supervisor/webservice.conf
Normal file
5
solvable/supervisor/webservice.conf
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[program:webservice]
|
||||||
|
directory=%(ENV_TFW_LOGIN_SERVICE_DIR)s
|
||||||
|
environment=BASEURL="/webservice"
|
||||||
|
command=python3 server.py
|
||||||
|
autostart=true
|
Loading…
Reference in New Issue
Block a user