Added users table in database.
authorGuillaume Subiron <maethor@subiron.org>
Mon, 28 May 2012 13:23:53 +0000 (15:23 +0200)
committerJulien Rabier <taziden@flexiden.org>
Mon, 28 May 2012 16:29:15 +0000 (18:29 +0200)
Users are connected from database.
Users can be connected without password, using key field. Usefull in
case of password loss.
Added some new templates.

main.py
schema.sql
templates/layout.html
templates/login.html
templates/password_lost.html [new file with mode: 0644]
templates/user_settings.html [new file with mode: 0644]

diff --git a/main.py b/main.py
index f749cc4..1ce53fb 100755 (executable)
--- a/main.py
+++ b/main.py
@@ -12,8 +12,6 @@ locale.setlocale(locale.LC_ALL, '')
 DATABASE = '/tmp/cavote.db'
 SECRET_KEY = '{J@uRKO,xO-PK7B,jF?>iHbxLasF9s#zjOoy=+:'
 DEBUG = True
-USERNAME = 'admin'
-PASSWORD = 'admin'
 
 app = Flask(__name__)
 app.config.from_object(__name__)
@@ -49,36 +47,71 @@ def init_db():
 # Login / Logout
 
 def valid_login(username, password):
-    return username == app.config['USERNAME'] and password == app.config['PASSWORD']
+    return query_db('select * from users where email = ? and password = ?', [username, password], one=True)
+
+def connect_user(user):
+    session['userid'] = user['id']
+    session['username'] = user['name']
+    session['email'] = user['email']
+    session['organization'] = user['organization']
+    if user['is_admin'] == 1:
+        session['is_admin'] = True
+
+def disconnect_user():
+    session.pop('username', None)
+    session.pop('is_admin', None)
 
 @app.route('/login', methods=['GET', 'POST'])
 def login():
-    error = None
     if request.method == 'POST':
-        if valid_login(request.form['username'], request.form['password']):
-            session['username'] = request.form['username']
-            if session['username'] == 'admin':
-                session['is_admin'] = True
-            flash('You were logged in')
-            return redirect(url_for('home'))
+        user = valid_login(request.form['username'], request.form['password'])
+        if user is None:
+            flash('Invalid username/password', 'error')
         else:
-            error = "Invalid username/password"
-    return render_template('login.html', error=error)
+            connect_user(user)
+            flash('You were logged in', 'success')
+            return redirect(url_for('home'))
+    return render_template('login.html')
 
 @app.route('/logout')
 def logout():
-    session.pop('username', None)
-    session.pop('is_admin', None)
-    flash('You were logged out')
+    disconnect_user()
+    flash('You were logged out', 'info')
     return redirect(url_for('home'))
 
+#-----------------
+# Change password
+
+@app.route('/password/lost', methods=['GET', 'POST'])
+def password_lost():
+    info = None
+    if request.method == 'POST':
+        user = query_db('select * from users where email = ?', [request.form['email']], one=True)
+        if user is None:
+            flash('Cet utilisateur n\'existe pas !', 'error')
+        else:
+            # :TODO:maethor:120528: Générer la clé, la mettre dans la base de données et envoyer le mail
+            flash(u"Un mail a été envoyé à " + user['email'], 'info')
+    return render_template('password_lost.html')
+
+@app.route('/login/<username>/<key>')
+def login_key(username, key):
+    user = query_db('select * from users where email = ? and key = ?', [username, key], one=True)
+    if user is None:
+        abort(404)
+    else:
+        connect_user(user)
+        # :TODO:maethor:120528: Remplacer la clé pour qu'elle ne puisse plus être utilisée
+        return redirect(url_for('home'))
+
 #---------------
 # User settings
+
 @app.route('/user/settings/<username>')
-def show_settings(username):
-    if username != session['username']:
+def show_user(username):
+    if username != session.get('username'):
         abort(401)
-
+    return render_template('user_settings.html')
 
 #------------
 # User admin
@@ -127,7 +160,7 @@ def add_vote():
     g.db.execute('insert into votes (title, description, date_begin, date_end, is_transparent, is_public, is_multiplechoice) values (?, ?, ?, ?, ?, ?, ?)',
             [request.form['title'], request.form['description'], date_begin, date_end, transparent, public, multiplechoice])
     g.db.commit()
-    flash('New entry was successfully posted')
+    flash('New entry was successfully posted', 'info')
     return redirect(url_for('home'))
 
 #------
index 4b23934..2036913 100644 (file)
@@ -1,4 +1,16 @@
 drop table if exists votes;
+drop table if exists users;
+
+create table users (
+    id INTEGER primary key autoincrement,
+    email TEXT unique not null,
+    password TEXT not null,
+    name TEXT,
+    organization TEXT,
+    is_admin INTEGER default 0 not null,
+    key TEXT
+);
+
 create table votes (
     id INTEGER primary key autoincrement,
     title TEXT not null,
@@ -10,11 +22,14 @@ create table votes (
     is_public INTEGER default 1 not null,
     is_multiplechoice INTEGER default 1 not null,
     is_weighted INTEGER default 0 not null,
-    is_closed INTEGER default 0 not null
-    --id_author INTEGER not null,
+    is_closed INTEGER default 0 not null,
+    id_author INTEGER, -- :COMMENT:maethor:120528: not null ?
     --id_role INTEGER,
-    --FOREIGN KEY(id_author) REFERENCES user(id),
+    FOREIGN KEY(id_author) REFERENCES users(id)
     --FOREIGN KEY(id_role) REFERENCES role(id)
 );
 
+-- Test data
+
+insert into users (email, password, name, organization, is_admin, key) values ("admin@admin.fr", "admin", "Toto (admin) Tata", "World corp", 1, "test");
 
index acc8c74..7dddad3 100644 (file)
     </div>
     <div class="btn-group pull-right">
       {% if 'username' in session %}
-      <a href="#" class="btn"><i class="icon-user"></i> {{ session.username }}</a>
+      <a href="{{ url_for('show_user', username=session.username) }}" class="btn"><i class="icon-user"></i> {{ session.username }}</a>
       <a href="#" class="btn dropdown-toggle" data-toggle="dropdown"><b class="caret"></b></a>
       <ul class="dropdown-menu pull-right">
         <li><a href=""><i class="icon-comment"></i> Votes en attente</a></li>
-        <li><a href=""><i class="icon-cog"></i> Paramètres</a></li>
+        <li><a href="{{ url_for('show_user', username=session.username) }}"><i class="icon-cog"></i> Paramètres</a></li>
         <li class="divider"></li>
         <li><a href="{{ url_for('logout') }}"><i class="icon-off"></i> Déconnexion</a></li>
       </ul>
 </header>
 
 <h1 class="page-header">Outil de vote du CA FFDN</h1>
-{% with messages = get_flashed_messages() %}
+{% with messages = get_flashed_messages(with_categories="true") %}
   {% if messages %}
-    {% for message in messages %}
-    <div class="alert alert-info fade in">
+    {% for category, message in messages %}
+    <div class="alert alert-{{ category }} fade in">
       <button class="close" data-dismiss="alert">×</button>
       {{ message }}
     </div>
     {% endfor %}
   {% endif %}
 {% endwith %}
+
 {% block body %}{% endblock %}
 
 </div> <!-- container -->   
index 334caa1..85d5b07 100644 (file)
@@ -2,18 +2,18 @@
 {% block body %}
 <div class="row">
   <div class="span3 well">
-    {% if error %}<div class="alert alert-error"><strong>Error:</strong> {{ error }}</div>{% endif %}
     <form action="{{ url_for('login') }}" method="post">
     <fieldset><legend>Connexion</legend>
       <label for="username">E-mail</label>
-      <input type="text" name="username" id="username" />
+      <input type="text" name="username" id="username" value="{{ request.form.username }}"/>
       <label for="password">Mot de passe</label>
       <input type="password" name="password" id="password" />
-      <br />
-      <input type="submit" class="btn btn-primary" value="Connexion" />
+      <div class="form-actions">
+        <input type="submit" class="btn btn-primary" value="Connexion" />
+      </div>
     </fieldset>
     </form>
-    <p><a href="">Mot de passe perdu ?</a></p>
+    <p><a href="{{ url_for("password_lost") }}">Mot de passe perdu ?</a></p>
   </div>
 </div>
 {% endblock %}
diff --git a/templates/password_lost.html b/templates/password_lost.html
new file mode 100644 (file)
index 0000000..7fcbbee
--- /dev/null
@@ -0,0 +1,17 @@
+{% extends "layout.html" %}
+{% block body %}
+<div class="row">
+  <div class="span3 well">
+    <form action="{{ url_for('password_lost') }}" method="post">
+    <fieldset><legend>Oubli de mot de passe</legend>
+      <label for="email">E-mail</label>
+      <input type="text" name="email" id="email" />
+      <br />
+      <div class="form-actions">
+        <input type="submit" class="btn btn-primary" value="Je reconnais être tête en l'air" />
+      </div>
+    </fieldset>
+    </form>
+  </div>
+</div>
+{% endblock %}
diff --git a/templates/user_settings.html b/templates/user_settings.html
new file mode 100644 (file)
index 0000000..d11fca9
--- /dev/null
@@ -0,0 +1,5 @@
+{% extends "layout.html" %}
+{% block body %}
+<h2>{{ session.username }}</h2>
+{% endblock %}
+