"Session management"

This commit is contained in:
Doug Le Tough 2017-11-26 02:10:09 +01:00
parent 4c7b3eb090
commit f087a270e4
5 changed files with 139 additions and 20 deletions

View File

@ -48,6 +48,16 @@ function valid_input(obj) {
, 2000); , 2000);
} }
function verify_login() {
// Verify login inputs
login = document.getElementById('login').value;
password = document.getElementById('password').value;
if (login.length > 0 && password.length > 0) {
return true;
}
return false;
}
/* ************************************************************************************** /* **************************************************************************************
* AJAX * AJAX
* **************************************************************************************/ * **************************************************************************************/

View File

@ -85,14 +85,16 @@ main > article {
display: block; display: block;
} }
main > article.error, main > article.error > p { main > article.error,
main > article.error > p {
padding: 10px; padding: 10px;
color: var(--text-color); color: var(--text-color);
display: block; display: block;
text-align: center; text-align: center;
} }
main > article > h3, main > section.inline > article > h3 { main > article > h3,
main > section.inline > article > h3 {
font-size: 30px; font-size: 30px;
color: var(--text-color); color: var(--text-color);
} }
@ -161,12 +163,17 @@ footer {
border-top-width: 1px; border-top-width: 1px;
} }
header, footer { header,
footer {
background-color: var(--coloured-bg); background-color: var(--coloured-bg);
color: var(--white); color: var(--white);
} }
input[type="text"], textarea, select, pre { input[type="text"],
input[type="password"],
textarea,
select,
pre {
border-color: var(--dark-border); border-color: var(--dark-border);
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
@ -181,7 +188,9 @@ pre {
border-color: var(--coloured-bg); border-color: var(--coloured-bg);
} }
button, input[type="button"], input[type="submit"] { button,
input[type="button"],
input[type="submit"] {
border-color: var(--dark-border); border-color: var(--dark-border);
border-style: solid; border-style: solid;
border-width: 1px; border-width: 1px;
@ -194,7 +203,9 @@ button, input[type="button"], input[type="submit"] {
border-radius: 4px; border-radius: 4px;
} }
button:hover, input[type="button"]:hover, input[type="submit"]:hover { button:hover,
input[type="button"]:hover,
input[type="submit"]:hover {
background-color: var(--light-coloured-bg); background-color: var(--light-coloured-bg);
color: var(--text-color); color: var(--text-color);
cursor: pointer; cursor: pointer;

View File

@ -0,0 +1,23 @@
{% extends "index.html" %}
{% block title %}Login{% endblock %}
{% block nav %}{% endblock %}
{% block main %}
<article class='login'>
<h3>Login</h3>
<p>The demo login is:</p>
<ul>
<li>Login: demo</li>
<li>Password: demo</li>
</ul>
</article>
{% if message != '' %}
<pre>{{ message }}</pre>
{% endif %}
<article class='left'>
<form method='POST' action='/login'>
Login: <input id='login' name='login' type='text' />
Password: <input id='password' name='password' type='password' />
<input type='submit' value='Log me in' onclick='javascript:return verify_login();'>
</form>
</article>
{% endblock %}

View File

@ -7,7 +7,7 @@
<li>Installation wizard</li> <li>Installation wizard</li>
<li>Back office for basic content management</li> <li>Back office for basic content management</li>
<li><strike>Basic Ajax support</strike></li> <li><strike>Basic Ajax support</strike></li>
<li>Session management</li> <li><strike>Session management</strike></li>
<li>Basic documentation</li> <li>Basic documentation</li>
<li>Horizontal navbar</li> <li>Horizontal navbar</li>
<li>License</li> <li>License</li>

View File

@ -2,7 +2,10 @@
# -*- coding: utf-8 # -*- coding: utf-8
# Required modules # Required modules
import os
import inspect import inspect
import random
import binascii
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash
from functools import wraps from functools import wraps
@ -27,17 +30,15 @@ db = SQLAlchemy(app)
######################################################################## ########################################################################
# Menu management: # Menu management:
# ----------------
#
# The main menu is a list of lists in the followin format
# [string caption, string URL endpoint, int 0]
# - The URL end point MUST match the function called by the corresponding
# route.
# - The int 0 is used to determine which menu entry is actally called.
# The value MUST be 0.
######################################################################## ########################################################################
def get_menu(page): def get_menu(page):
""" The main menu is a list of lists in the followin format:
[string caption, string URL endpoint, int 0]
- The URL end point MUST match the function called by the corresponding
route.
- The int 0 is used to determine which menu entry is actally called.
The value MUST be 0."""
menu = [['Accueil', '/', 0], menu = [['Accueil', '/', 0],
['Articles', '/articles', 0], ['Articles', '/articles', 0],
['Basics', '/basics', 0], ['Basics', '/basics', 0],
@ -60,11 +61,47 @@ def get_menu(page):
# This should never happen # This should never happen
return menu return menu
########################################################################
# Session management
########################################################################
def sync_session(request, session):
""" Synchronize cookies with session """
for key in request.cookies:
session[key] = request.cookies[key].encode('utf8')
def sync_cookies(response, session):
""" Synchronize session with cookies """
for key in session:
response.set_cookie(key, value=str(session[key]))
def check_session(func):
""" Check if the session has required token cookie set.
If not, redirects to the login page. """
@wraps(func)
def check(*args, **kwargs):
try:
if session['token'] == request.cookies['token']:
return func(*args, **kwargs)
except KeyError:
return render_template('login.html', message='')
return check
def check_login(login, password):
""" Puts the login verification code here """
if login == 'demo' and password == 'demo':
return True
return False
def gen_token():
""" Generate a random token to be stored in session and cookie """
token = binascii.hexlify(os.urandom(42))
return token
######################################################################## ########################################################################
# Routes: # Routes:
# ------- # -------
# # Except for the index function, the function name MUST have the same
# Except for the index fonction, the function name MUST have the same
# name than the URL endpoint to make the menu work properly # name than the URL endpoint to make the menu work properly
######################################################################## ########################################################################
@app.errorhandler(404) @app.errorhandler(404)
@ -72,66 +109,101 @@ def page_not_found(e):
""" 404 not found """ """ 404 not found """
return render_template('error.html'), 404 return render_template('error.html'), 404
@app.route("/login", methods=['GET', 'POST'])
def login():
login = request.form.get('login')
password = request.form.get('password')
if check_login(login, password):
# Generate and store a token in session
token = gen_token()
session['token'] = token
# Return user to index page
page = 'index'
menu = get_menu(page)
response = app.make_response(render_template('index.html', menu=menu))
# Push token to cookie
sync_cookies(response, session)
return response
# Credentials are not valid
return render_template('login.html', message='Invalid user or password')
@app.route("/", methods=['GET', 'POST']) @app.route("/", methods=['GET', 'POST'])
@check_session
def index(): def index():
""" Index page """
page = inspect.currentframe().f_code.co_name page = inspect.currentframe().f_code.co_name
menu = get_menu(page) menu = get_menu(page)
return render_template('index.html', menu=menu) return render_template('index.html', menu=menu)
@app.route("/articles", methods=['GET', 'POST']) @app.route("/articles", methods=['GET', 'POST'])
@check_session
def articles(): def articles():
""" Arcticles page """
page = inspect.currentframe().f_code.co_name page = inspect.currentframe().f_code.co_name
menu = get_menu(page) menu = get_menu(page)
return render_template('articles.html', menu=menu) return render_template('articles.html', menu=menu)
@app.route("/basics", methods=['GET', 'POST']) @app.route("/basics", methods=['GET', 'POST'])
@check_session
def basics(): def basics():
""" Basics page """
page = inspect.currentframe().f_code.co_name page = inspect.currentframe().f_code.co_name
menu = get_menu(page) menu = get_menu(page)
return render_template('basics.html', menu=menu) return render_template('basics.html', menu=menu)
@app.route("/inputs", methods=['GET', 'POST']) @app.route("/inputs", methods=['GET', 'POST'])
@check_session
def inputs(): def inputs():
""" Show the input collection """
page = inspect.currentframe().f_code.co_name page = inspect.currentframe().f_code.co_name
menu = get_menu(page) menu = get_menu(page)
return render_template('inputs.html', menu=menu) return render_template('inputs.html', menu=menu)
@app.route("/ajax", methods=['GET', 'POST']) @app.route("/ajax", methods=['GET', 'POST'])
@check_session
def ajax(): def ajax():
""" Propose various AJAX tests """
page = inspect.currentframe().f_code.co_name page = inspect.currentframe().f_code.co_name
menu = get_menu(page) menu = get_menu(page)
return render_template('ajax.html', menu=menu) return render_template('ajax.html', menu=menu)
@app.route("/database", methods=['GET', 'POST']) @app.route("/database", methods=['GET', 'POST'])
@check_session
def database(): def database():
""" A blah on using databases """
page = inspect.currentframe().f_code.co_name page = inspect.currentframe().f_code.co_name
menu = get_menu(page) menu = get_menu(page)
return render_template('database.html', menu=menu) return render_template('database.html', menu=menu)
@app.route("/todo", methods=['GET', 'POST']) @app.route("/todo", methods=['GET', 'POST'])
@check_session
def todo(): def todo():
""" The famous TODO list """
page = inspect.currentframe().f_code.co_name page = inspect.currentframe().f_code.co_name
menu = get_menu(page) menu = get_menu(page)
return render_template('todo.html', menu=menu) return render_template('todo.html', menu=menu)
######################################################################## ########################################################################
# AJAX # AJAX routes
######################################################################## ########################################################################
@app.route("/get_html_from_ajax", methods=['GET', 'POST']) @app.route("/get_html_from_ajax", methods=['GET', 'POST'])
@check_session
def get_html_from_ajax(): def get_html_from_ajax():
import random """ Return HTML code to an AJAX request
It may generate a 404 http error for testing purpose """
if int(random.random()*10) % 2: if int(random.random()*10) % 2:
# Randomlu generate 404 HTTP response # Randomlu generate 404 HTTP response
return render_template('error.html'), 404 return render_template('error.html'), 404
return render_template('ajax_html.html') return render_template('ajax_html.html')
@app.route("/get_value_from_ajax", methods=['GET', 'POST']) @app.route("/get_value_from_ajax", methods=['GET', 'POST'])
@check_session
def get_value_from_ajax(): def get_value_from_ajax():
""" Return a randomly generated value to an AJAX request
It may return an error code for testing purpose """
err_code = 'TETA_ERR' err_code = 'TETA_ERR'
import random
RND = int(random.random()*10) RND = int(random.random()*10)
if RND % 2: if RND % 2:
# Randomlu generate error # Randomlu generate error
@ -139,7 +211,10 @@ def get_value_from_ajax():
return str(RND) return str(RND)
@app.route("/set_value_from_ajax/<value>", methods=['GET', 'POST']) @app.route("/set_value_from_ajax/<value>", methods=['GET', 'POST'])
@check_session
def set_value_from_ajax(value): def set_value_from_ajax(value):
""" Accept a value from an AJAX request
It may return an error code for testing purpose """
err_code = 'TETA_ERR' err_code = 'TETA_ERR'
if value != 'We Make Porn': if value != 'We Make Porn':
return 'True' return 'True'