summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Sulfrian <alexander@sulfrian.net>2016-01-25 00:15:12 +0100
committerAlexander Sulfrian <alexander@sulfrian.net>2016-02-02 04:22:16 +0100
commit7619809115e6fdc3d7df8705abb20f228863e7c2 (patch)
tree5afaeeb9020d418143b7cea3e1e2540efcc2140e
parent753c03b3477071279299ca47ce76f5fcd346d5bd (diff)
downloadweb-7619809115e6fdc3d7df8705abb20f228863e7c2.tar.gz
web-7619809115e6fdc3d7df8705abb20f228863e7c2.tar.bz2
web-7619809115e6fdc3d7df8705abb20f228863e7c2.zip
Use Flask-Login for login handling
-rw-r--r--accounts/__init__.py29
-rw-r--r--accounts/forms.py5
-rw-r--r--accounts/models.py14
-rw-r--r--accounts/templates/base.html18
-rw-r--r--accounts/utils/__init__.py49
-rw-r--r--accounts/utils/login.py24
-rw-r--r--accounts/views/admin/__init__.py5
-rw-r--r--accounts/views/default/__init__.py61
-rw-r--r--requirements.txt1
9 files changed, 102 insertions, 104 deletions
diff --git a/accounts/__init__.py b/accounts/__init__.py
index b11a143..ba5a670 100644
--- a/accounts/__init__.py
+++ b/accounts/__init__.py
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
+from flask.ext.login import LoginManager
import account
from flask import Flask, g, session
from utils import *
-
from utils.sessions import EncryptedSessionInterface
+from utils.login import parse_userid
from views import default, admin
@@ -24,23 +25,21 @@ if app.config.get('USERNAME_BLACKLIST_FILE'):
with open(app.config['USERNAME_BLACKLIST_FILE']) as f:
app.username_blacklist = f.read().split('\n')
+login_manager = LoginManager()
+login_manager.init_app(app)
+
+@login_manager.user_loader
+def load_user(user_id):
+ try:
+ username, password = parse_userid(user_id)
+ return current_app.user_backend.auth(username, password)
+ except (current_app.user_backend.NoSuchUserError,
+ current_app.user_backend.InvalidPasswordError):
+ return None
+
@app.before_request
def session_permanent():
if app.config.get('PERMANENT_SESSION_LIFETIME'):
session.permanent = True
else:
session.permanent = False
-
-@app.before_request
-def initialize_user():
- g.user = None
-
- if 'username' in session and 'password' in session:
- username = ensure_utf8(session['username'])
- password = ensure_utf8(session['password'])
- try:
- g.user = current_app.user_backend.auth(username, password)
- except (current_app.user_backend.NoSuchUserError,
- current_app.user_backend.InvalidPasswordError):
- # we had crap in the session, delete it
- logout_user()
diff --git a/accounts/forms.py b/accounts/forms.py
index 9e2e8f3..bcbe747 100644
--- a/accounts/forms.py
+++ b/accounts/forms.py
@@ -2,6 +2,7 @@
from account import SERVICES
from flask import g, current_app, session, Markup
from flask.ext.wtf import Form
+from flask.ext.login import current_user
from wtforms import TextField, PasswordField, ValidationError, BooleanField,\
validators
from functools import partial
@@ -98,13 +99,13 @@ class SettingsForm(Form):
if form.password.data:
if not field.data:
raise ValidationError(u'Gib bitte dein altes Passwort ein, um ein neues zu setzen.')
- if field.data != decrypt_password(session['password']):
+ if field.data != current_user.password:
raise ValidationError(u'Altes Passwort ist falsch.')
def validate_mail(form, field):
results = current_app.user_backend.find_by_mail(field.data)
for user in results:
- if user.uid != g.user.uid:
+ if user.uid != current_user.uid:
raise ValidationError(u'Diese E-Mail-Adresse wird schon von einem anderen Account benutzt!')
def get_servicepassword(self, service_id):
diff --git a/accounts/models.py b/accounts/models.py
index 9e0c45e..f967180 100644
--- a/accounts/models.py
+++ b/accounts/models.py
@@ -1,6 +1,11 @@
# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+from flask.ext.login import UserMixin
-class Account(object):
+from accounts.utils.login import create_userid
+
+
+class Account(UserMixin):
"""
An Account represents a complex ldap tree entry for spline users.
For each service a spline user can have a different password.
@@ -62,6 +67,13 @@ class Account(object):
raise AttributeError("'%s' object has no attribute '%s'" %
(self.__class__.__name__, name))
+ def get_id(self):
+ """
+ This is for flask-login. The returned string is saved inside
+ the cookie and used to identify the user.
+ """
+ return create_userid(self.uid, self.password)
+
class Service(object):
def __init__(self, id, name, url):
diff --git a/accounts/templates/base.html b/accounts/templates/base.html
index 1eeda55..f59408d 100644
--- a/accounts/templates/base.html
+++ b/accounts/templates/base.html
@@ -15,9 +15,15 @@
<div id="header-background">&nbsp;</div>
<header>
- <h1><a href="{{ url_for('default.settings') if g.user else url_for('default.index') }}">
- <img src="{{ url_for('static', filename='logo.png') }}" alt="spline accounts" />
- </a></h1>
+ <h1>
+ {%- if current_user.is_authenticated -%}
+ <a href="{{ url_for('default.settings') }}">
+ {%- else -%}
+ <a href="{{ url_for('default.index') }}">
+ {%- endif -%}
+ <img src="{{ url_for('static', filename='logo.png') }}" alt="spline accounts" />
+ </a>
+ </h1>
<span id="roundcornerb">&nbsp;</span>
<span id="roundcornerw">&nbsp;</span>
@@ -31,9 +37,9 @@
{%- if not no_login_message %}
<nav id="usermenu">
<ul>
- {%- if g.user %}
- <li>Angemeldet als <strong>{{ g.user.uid }}</strong></li>
- {%- if g.user.uid in config.get('ADMIN_USERS', []) %}
+ {%- if current_user.is_authenticated %}
+ <li>Angemeldet als <strong>{{ current_user.uid }}</strong></li>
+ {%- if current_user.uid in config.get('ADMIN_USERS', []) %}
<li><a href="{{ url_for('admin.index') }}">Admin</a></li>
{%- endif %}
<li><a href="{{ url_for('default.logout') }}">Abmelden</a></li>
diff --git a/accounts/utils/__init__.py b/accounts/utils/__init__.py
index 4529796..06cf969 100644
--- a/accounts/utils/__init__.py
+++ b/accounts/utils/__init__.py
@@ -4,6 +4,7 @@ import re
from functools import wraps
from flask import current_app, flash, g, redirect, render_template, request, session
from flask import url_for as flask_url_for
+from flask.ext.login import current_user
from werkzeug.exceptions import Forbidden
from wtforms.validators import Regexp, ValidationError
@@ -31,54 +32,6 @@ def templated(template=None):
return templated__
return templated_
-def login_required(f):
- @wraps(f)
- def login_required_(*args, **kwargs):
- if not g.user:
- raise Forbidden(u'Bitte einloggen!')
- return f(*args, **kwargs)
- return login_required_
-
-def admin_required(f):
- @wraps(f)
- def admin_required_(*args, **kwargs):
- if not g.user:
- raise Forbidden(u'Bitte einloggen!')
- if g.user.uid not in current_app.config.get('ADMIN_USERS', []):
- raise Forbidden(u'Du bist kein Admin.')
- return f(*args, **kwargs)
- return admin_required_
-
-def logout_required(f):
- @wraps(f)
- def logout_required_(*args, **kwargs):
- if g.user:
- raise Forbidden(u'Diese Seite ist nur für nicht eingeloggte Benutzer gedacht!')
- return f(*args, **kwargs)
- return logout_required_
-
-
-def login_user(username, password):
- username = ensure_utf8(username)
- password = ensure_utf8(password)
-
- try:
- g.user = current_app.user_backend.auth(username, password)
- except (current_app.user_backend.NoSuchUserError,
- current_app.user_backend.InvalidPasswordError):
- return False
-
- session['username'] = username
- session['password'] = password
-
- return True
-
-
-def logout_user():
- session.pop('username', None)
- session.pop('password', None)
- g.user = None
-
def ensure_utf8(s):
if isinstance(s, unicode):
diff --git a/accounts/utils/login.py b/accounts/utils/login.py
new file mode 100644
index 0000000..9888e89
--- /dev/null
+++ b/accounts/utils/login.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from flask.ext.login import current_user
+from functools import wraps
+from werkzeug.exceptions import Forbidden
+from itsdangerous import base64_decode, base64_encode, compact_json
+
+
+def create_userid(username, password):
+ userid = (username, password)
+ return base64_encode(compact_json.dumps(userid))
+
+
+def parse_userid(value):
+ return compact_json.loads(base64_decode(value))
+
+
+def logout_required(f):
+ @wraps(f)
+ def logout_required_(*args, **kwargs):
+ if current_user.is_authenticated:
+ raise Forbidden(u'Diese Seite ist nur für nicht eingeloggte Benutzer gedacht!')
+ return f(*args, **kwargs)
+ return logout_required_
+
diff --git a/accounts/views/admin/__init__.py b/accounts/views/admin/__init__.py
index 5564f93..be7f3d7 100644
--- a/accounts/views/admin/__init__.py
+++ b/accounts/views/admin/__init__.py
@@ -3,6 +3,7 @@ from __future__ import absolute_import
from flask import Blueprint
from flask import current_app, redirect, request, g, flash, url_for
+from flask.ext.login import current_user
from uuid import uuid4
from werkzeug.exceptions import Forbidden
@@ -15,9 +16,9 @@ bp = Blueprint('admin', __name__)
@bp.before_request
def restrict_bp_to_admins():
- if not g.user:
+ if not current_user.is_authenticated:
raise Forbidden(u'Bitte einloggen!')
- if g.user.uid not in current_app.config.get('ADMIN_USERS', []):
+ if current_user.uid not in current_app.config.get('ADMIN_USERS', []):
raise Forbidden(u'Du bist kein Admin.')
diff --git a/accounts/views/default/__init__.py b/accounts/views/default/__init__.py
index 64c855f..37f71f6 100644
--- a/accounts/views/default/__init__.py
+++ b/accounts/views/default/__init__.py
@@ -4,11 +4,13 @@ from __future__ import absolute_import
from copy import deepcopy
from flask import Blueprint
from flask import current_app, redirect, request, g, flash, url_for
+from flask.ext.login import login_required, login_user, logout_user, current_user
from accounts.forms import LoginForm, RegisterForm, RegisterCompleteForm, \
LostPasswordForm, SettingsForm
from accounts.utils import *
from accounts.utils.confirmation import Confirmation
+from accounts.utils.login import logout_required
from accounts.models import Account
@@ -18,17 +20,21 @@ bp = Blueprint('default', __name__)
@bp.route('/', methods=['GET', 'POST'])
@templated('index.html')
def index():
- if not g.user:
- form = LoginForm(request.form)
- if form.validate_on_submit():
- if login_user(form.username.data, form.password.data):
- flash(u'Erfolgreich eingeloggt', 'success')
- return redirect(url_for('.settings'))
- else:
- flash(u'Ungültiger Benutzername und/oder Passwort', 'error')
- else:
+ if current_user.is_authenticated:
return redirect(url_for('.settings'))
+ form = LoginForm(request.form)
+ if form.validate_on_submit():
+ try:
+ user = current_app.user_backend.auth(form.username.data,
+ form.password.data)
+ login_user(user)
+ flash(u'Erfolgreich eingeloggt', 'success')
+ return redirect(url_for('.settings'))
+ except (current_app.user_backend.NoSuchUserError,
+ current_app.user_backend.InvalidPasswordError):
+ flash(u'Ungültiger Benutzername und/oder Passwort', 'error')
+
return {'form': form}
@@ -71,9 +77,7 @@ def register_complete(token):
user = Account(username, mail, password=form.password.data)
current_app.user_backend.register(user)
-
- # populate request context and session
- assert login_user(user.uid, user.password)
+ login_user(user)
if current_app.config.get('MAIL_REGISTER_NOTIFY'):
current_app.mail_backend.send(
@@ -132,11 +136,9 @@ def lost_password_complete(token):
user = current_app.user_backend.get_by_uid(username)
user.change_password(form.password.data)
current_app.user_backend.update(user, as_admin=True)
+ login_user(user)
- session['username'] = username
- session['password'] = form.password.data
flash(u'Passwort geändert.', 'success')
-
return redirect(url_for('.settings'))
return {
@@ -150,7 +152,7 @@ def lost_password_complete(token):
@templated('settings.html')
@login_required
def settings():
- form = SettingsForm(request.form, mail=g.user.attributes['mail'])
+ form = SettingsForm(request.form, mail=current_user.attributes['mail'])
if form.validate_on_submit():
changed = False
@@ -158,15 +160,15 @@ def settings():
for service in current_app.all_services:
field = form.get_servicedelete(service.id)
if(field.data):
- g.user.reset_password(service.id)
+ current_user.reset_password(service.id)
changed = True
elif request.form.get('submit_main'):
- if form.mail.data and form.mail.data != g.user.attributes['mail']:
- confirm_token = Confirmation('change_mail').dumps((g.user.uid, form.mail.data))
+ if form.mail.data and form.mail.data != current_user.attributes['mail']:
+ confirm_token = Confirmation('change_mail').dumps((current_user.uid, form.mail.data))
confirm_link = url_for('.change_mail', token=confirm_token, _external=True)
- body = render_template('mail/change_mail.txt', username=g.user.uid,
+ body = render_template('mail/change_mail.txt', username=current_user.uid,
mail=form.mail.data, link=confirm_link)
current_app.mail_backend.send(
@@ -179,9 +181,7 @@ def settings():
changed = True
if form.password.data:
- g.user.change_password(form.password.data, form.old_password.data)
- session['password'] = form.password.data
-
+ current_user.change_password(form.password.data, form.old_password.data)
flash(u'Passwort geändert', 'success')
changed = True
@@ -189,10 +189,11 @@ def settings():
field = form.get_servicepassword(service.id)
if field.data:
changed = True
- g.user.change_password(field.data, None, service.id)
+ current_user.change_password(field.data, None, service.id)
if changed:
- current_app.user_backend.update(g.user, as_admin=True) #XXX: as_admin wieder wegmachen sobald ACLs richtig gesetzt sind
+ current_app.user_backend.update(current_user, as_admin=True) #XXX: as_admin wieder wegmachen sobald ACLs richtig gesetzt sind
+ login_user(current_user)
return redirect(url_for('.settings'))
else:
flash(u'Nichts geändert.')
@@ -200,7 +201,7 @@ def settings():
services = deepcopy(current_app.all_services)
for s in services:
- s.changed = s.id in g.user.services
+ s.changed = s.id in current_user.services
return {
'form': form,
@@ -213,16 +214,16 @@ def settings():
def change_mail(token):
username, mail = Confirmation('change_mail').loads_http(token, max_age=3*24*60*60)
- if g.user.uid != username:
+ if current_user.uid != username:
raise Forbidden(u'Bitte logge dich als der Benutzer ein, dessen E-Mail-Adresse du ändern willst.')
results = current_app.user_backend.find_by_mail(mail)
for user in results:
- if user.uid != g.user.uid:
+ if user.uid != current_user.uid:
raise Forbidden(u'Diese E-Mail-Adresse wird schon von einem anderen account benutzt!')
- g.user.change_email(mail)
- current_app.user_backend.update(g.user)
+ current_user.change_email(mail)
+ current_app.user_backend.update(current_user)
flash(u'E-Mail-Adresse geändert.', 'success')
return redirect(url_for('.settings'))
diff --git a/requirements.txt b/requirements.txt
index ea8476c..0d0a1f4 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,7 @@
Flask>=0.10
Flask-WTF
Flask-Script
+Flask-Login
Werkzeug>=0.6
Jinja2>=2.4
WTForms>=1.0