"first proto"
This commit is contained in:
Executable
+363
@@ -0,0 +1,363 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8
|
||||
|
||||
# Required modules
|
||||
import os
|
||||
import inspect
|
||||
import random
|
||||
import binascii
|
||||
import bcrypt
|
||||
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash
|
||||
from functools import wraps
|
||||
|
||||
# Optionnal modules
|
||||
import psycopg2
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
########################################################################
|
||||
# App settings
|
||||
########################################################################
|
||||
app = Flask(__name__)
|
||||
# Path to static files
|
||||
app.static_url_path='/static'
|
||||
# Set debug mode to False for production
|
||||
app.debug = True
|
||||
# Various configuration settings belong here (optionnal)
|
||||
app.config.from_pyfile('config.local.py')
|
||||
# Generate a new key: head -n 40 /dev/urandom | md5sum | cut -d' ' -f1
|
||||
app.secret_key = '9ac80548e3a8d8dfd1aefcd9a3a73473'
|
||||
# Feel free to use SQLAlchemy (or not)
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
|
||||
########################################################################
|
||||
# Sample user database
|
||||
########################################################################
|
||||
class Tetawebapp_users(db.Model):
|
||||
__tablename__ = 'participer_thsf_users'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
mail = db.Column(db.Text, nullable=False)
|
||||
password = db.Column(db.Text, nullable=False)
|
||||
name = db.Column(db.Text, nullable=True)
|
||||
phone = db.Column(db.Text, nullable=True)
|
||||
diet = db.Column(db.Text, nullable=True)
|
||||
|
||||
class Tetawebapp_roles(db.Model):
|
||||
__tablename__ = 'participer_thsf_roles'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
role = db.Column(db.Text, nullable=False)
|
||||
description = db.Column(db.Text, nullable=False)
|
||||
|
||||
########################################################################
|
||||
# Menu and navigation management
|
||||
########################################################################
|
||||
|
||||
def get_menu(page):
|
||||
""" The main menu is a list of lists in the followin format:
|
||||
[unicode caption,
|
||||
{unicode URL endpoint: [unicode route, ...]},
|
||||
int 0]
|
||||
- The URL end point is the URL where to point to (href)
|
||||
- One of the routes MUST match the route called by request
|
||||
- The int 0 is used to determine which menu entry is actally called.
|
||||
The value MUST be 0."""
|
||||
menu = [[u'Accueil', {u'/': [u'/']}, 0],
|
||||
[u'Mon compte', {u'/account': [u'/account', u'/account/<ID>']}, 0],
|
||||
[u'Mes quarts', {u'/turn': [u'/turn']}, 0],
|
||||
[u'Feuille de staff', {u'/staff_sheet': [u'/staff_sheet']}, 0],
|
||||
[u'Déconnexion', {u'/logout': [u'/logout']}, 0],
|
||||
[u'Inputs', {u'/inputs': [u'/inputs']}, 0],
|
||||
[u'Ajax', {u'/ajax': [u'/ajax']}, 0],
|
||||
[u'Database', {u'/database': [u'/database']}, 0],
|
||||
[u'Todo', {u'/todo': [u'/todo']}, 0],
|
||||
]
|
||||
for item in menu:
|
||||
for url in item[1]:
|
||||
for route in item[1][url]:
|
||||
if route == page:
|
||||
item[2] = 1
|
||||
return menu
|
||||
# This should never happen
|
||||
return menu
|
||||
|
||||
def get_navbar(page, selected):
|
||||
""" The horizontal navbar is a list of lists in the followin format:
|
||||
[unicode caption, {unicode URL endpoint: [unicode route, ...]}, int 0]
|
||||
- The URL end point is the URL where to point to (href)
|
||||
- One of the routes MUST match the route called by request
|
||||
- The int 0 is used to de """
|
||||
navbars = [[u'First article', {u'/articles/1': [u'/articles', u'/articles/<ID>']}, 0, 0],
|
||||
[u'Second article', {u'/articles/2': [u'/articles', u'/articles/<ID>']}, 0, 0],
|
||||
[u'Third article', {u'/articles/3': [u'/articles', u'/articles/<ID>']}, 0, 0]
|
||||
]
|
||||
navbar = []
|
||||
for item in navbars:
|
||||
for url in item[1]:
|
||||
if url == selected:
|
||||
item[2] = 1
|
||||
for route in item[1][url]:
|
||||
if route == page:
|
||||
navbar.append(item)
|
||||
navbar[len(navbar) - 1][3] = 1
|
||||
return navbar
|
||||
|
||||
########################################################################
|
||||
# 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'] and len(session['token']) > 0:
|
||||
return func(*args, **kwargs)
|
||||
else:
|
||||
session['token'] = ''
|
||||
response = app.make_response(render_template('login_or_register.html', message=''))
|
||||
sync_cookies(response, session)
|
||||
return response
|
||||
except KeyError:
|
||||
return render_template('login_or_register.html', message='')
|
||||
return check
|
||||
|
||||
def check_login(login, password):
|
||||
""" Puts the login verification code here """
|
||||
password = password.encode('utf-8')
|
||||
hashed_password = bcrypt.hashpw(password, bcrypt.gensalt())
|
||||
stored_hash = Tetawebapp_users.query.filter_by(mail=login).with_entities(Tetawebapp_users.password).first()
|
||||
if stored_hash:
|
||||
if bcrypt.checkpw(password, stored_hash[0].encode('utf-8')):
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_user_info():
|
||||
""" Check user info and send appropriate message if info are not complete"""
|
||||
message = "Vos informations personnelles ne sont pas totalement renseignées. N'oubliez pas de remplir votre fiche située dans la section 'Mon compte'"
|
||||
return message.decode('utf-8')
|
||||
|
||||
def gen_token():
|
||||
""" Generate a random token to be stored in session and cookie """
|
||||
token = binascii.hexlify(os.urandom(42))
|
||||
return token
|
||||
|
||||
########################################################################
|
||||
# Routes:
|
||||
# -------
|
||||
# Except for the index function, the function name MUST have the same
|
||||
# name than the URL endpoint to make the menu work properly
|
||||
########################################################################
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
""" 404 not found """
|
||||
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
|
||||
session['token'] = gen_token()
|
||||
# Return user to index page
|
||||
page = '/'
|
||||
menu = get_menu(page)
|
||||
message = check_user_info()
|
||||
response = app.make_response(render_template('index.html', menu=menu, message=message))
|
||||
# Push token to cookie
|
||||
sync_cookies(response, session)
|
||||
return response
|
||||
# Credentials are not valid
|
||||
response = app.make_response(render_template('login_or_register.html', message='Invalid user or password'))
|
||||
session['token'] = ''
|
||||
sync_cookies(response, session)
|
||||
return response
|
||||
|
||||
@app.route("/register", methods=['GET', 'POST'])
|
||||
def register():
|
||||
login = request.form.get('login')
|
||||
password = request.form.get('password')
|
||||
if check_login(login, password):
|
||||
# Generate and store a token in session
|
||||
session['token'] = gen_token()
|
||||
# Return user to index page
|
||||
page = '/'
|
||||
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
|
||||
response = app.make_response(render_template('login_or_register.html', message='Invalid user or password'))
|
||||
session['token'] = ''
|
||||
sync_cookies(response, session)
|
||||
return response
|
||||
|
||||
@app.route("/", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def index():
|
||||
""" Index page """
|
||||
page = str(request.url_rule)
|
||||
menu = get_menu(page)
|
||||
message = check_user_info()
|
||||
return render_template('index.html', menu=menu, message=message)
|
||||
|
||||
@app.route("/account", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def account():
|
||||
""" Arcticles page """
|
||||
page = str(request.url_rule)
|
||||
menu = get_menu(page)
|
||||
return render_template('account.html', menu=menu)
|
||||
|
||||
@app.route("/account/<ID>", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def account_by_id(ID):
|
||||
""" Arcticles page """
|
||||
page = str(request.url_rule)
|
||||
menu = get_menu(page)
|
||||
selected = page.replace('<ID>', ID)
|
||||
navbar = get_navbar(page, selected)
|
||||
return render_template('account_by_id.html', menu=menu, navbar=navbar, ID=ID)
|
||||
|
||||
@app.route("/logout", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def logout():
|
||||
""" Logout user """
|
||||
# Remove session token
|
||||
session['token'] = None
|
||||
# Return user to index page
|
||||
response = app.make_response(render_template('login_or_register.html', message=''))
|
||||
# Push token to cookie
|
||||
sync_cookies(response, session)
|
||||
return response
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@app.route("/basics", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def basics():
|
||||
""" Basics page """
|
||||
page = str(request.url_rule)
|
||||
menu = get_menu(page)
|
||||
return render_template('basics.html', menu=menu)
|
||||
|
||||
@app.route("/inputs", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def inputs():
|
||||
""" Show the input collection """
|
||||
page = str(request.url_rule)
|
||||
menu = get_menu(page)
|
||||
return render_template('inputs.html', menu=menu)
|
||||
|
||||
@app.route("/ajax", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def ajax():
|
||||
""" Propose various AJAX tests """
|
||||
page = str(request.url_rule)
|
||||
menu = get_menu(page)
|
||||
return render_template('ajax.html', menu=menu)
|
||||
|
||||
@app.route("/database", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def database():
|
||||
""" A blah on using databases """
|
||||
page = str(request.url_rule)
|
||||
menu = get_menu(page)
|
||||
return render_template('database.html', menu=menu)
|
||||
|
||||
@app.route("/todo", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def todo():
|
||||
""" The famous TODO list """
|
||||
page = str(request.url_rule)
|
||||
menu = get_menu(page)
|
||||
return render_template('todo.html', menu=menu)
|
||||
|
||||
########################################################################
|
||||
# AJAX routes
|
||||
########################################################################
|
||||
|
||||
@app.route("/get_html_from_ajax", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
def get_html_from_ajax():
|
||||
""" Return HTML code to an AJAX request
|
||||
It may generate a 404 http error for testing purpose """
|
||||
if int(random.random()*10) % 2:
|
||||
# Randomly generate 404 HTTP response
|
||||
return render_template('error.html'), 404
|
||||
return render_template('ajax_html.html')
|
||||
|
||||
@app.route("/get_value_from_ajax", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
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'
|
||||
RND = int(random.random()*10)
|
||||
if RND % 2:
|
||||
# Randomly generate error
|
||||
return err_code
|
||||
return str(RND)
|
||||
|
||||
@app.route("/set_value_from_ajax/<value>", methods=['GET', 'POST'])
|
||||
@check_session
|
||||
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'
|
||||
if value != 'We Make Porn':
|
||||
return 'True'
|
||||
return err_code
|
||||
|
||||
@app.route("/upload", methods=['POST'])
|
||||
@check_session
|
||||
def upload():
|
||||
""" Save a file from AJAX request
|
||||
Files are saved in UPLOADED_FILES_DEST (see config.local.py) """
|
||||
err_code = 'TETA_ERR'
|
||||
RND = int(random.random()*10)
|
||||
if RND % 2:
|
||||
# Randomly generate error
|
||||
print err_code
|
||||
return err_code
|
||||
uploaded_files = []
|
||||
if len(request.files) > 0 and request.files['files']:
|
||||
uploaded_files = request.files.getlist("files")
|
||||
print "Uploaded files:"
|
||||
for f in uploaded_files:
|
||||
print ' [+] %s [%s]' % (f.filename, f.content_type)
|
||||
# Before saving you should:
|
||||
# - Secure the filename
|
||||
# - Check file size
|
||||
# - Check content type
|
||||
f.save(os.path.join(app.config['UPLOADED_FILES_DEST'], f.filename))
|
||||
f.close()
|
||||
return "OK"
|
||||
|
||||
########################################################################
|
||||
# Main
|
||||
########################################################################
|
||||
if __name__ == '__main__':
|
||||
app.run(host='0.0.0.0')
|
||||
Reference in New Issue
Block a user