"Session management"
This commit is contained in:
parent
4c7b3eb090
commit
f087a270e4
@ -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
|
||||||
* **************************************************************************************/
|
* **************************************************************************************/
|
||||||
|
@ -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;
|
||||||
|
23
tetawebapp/templates/login.html
Normal file
23
tetawebapp/templates/login.html
Normal 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 %}
|
@ -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>
|
||||||
|
@ -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'
|
||||||
|
Loading…
Reference in New Issue
Block a user