From cd533a4ea3673fb8c3e1aff1428693f9cd512a49 Mon Sep 17 00:00:00 2001 From: Dark-Storm Date: Tue, 5 Feb 2019 11:30:39 +0100 Subject: [PATCH] =?UTF-8?q?Ajout=20d'un=20d=C3=A9corateur=20@priv=5Frequir?= =?UTF-8?q?ed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/models/privs.py | 4 ++++ app/models/users.py | 2 +- app/routes/account.py | 3 ++- app/routes/search.py | 2 ++ app/routes/users.py | 30 ++++++++++++++++++++++++++++ app/utils/priv_required.py | 41 ++++++++++++++++++++++++++++++++++++++ config.py | 3 +++ 7 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 app/routes/users.py create mode 100644 app/utils/priv_required.py diff --git a/app/models/privs.py b/app/models/privs.py index f3a6324..a8f0d96 100644 --- a/app/models/privs.py +++ b/app/models/privs.py @@ -27,6 +27,10 @@ class SpecialPrivilege(db.Model): def __repr__(self): return f'' + # TODO: clean this. filter does not work ootb + def filter(*args, **kwargs): + return False + # Group: User group, corresponds to a community role and a set of privileges class Group(db.Model): __tablename__ = 'group' diff --git a/app/models/users.py b/app/models/users.py index d896dd0..9f58619 100644 --- a/app/models/users.py +++ b/app/models/users.py @@ -127,7 +127,7 @@ class Member(User, db.Model): def priv(self, priv): """Check whether the member has the specified privilege.""" - if SpecialPrivilege.filter(uif=self.id, priv=priv): + if SpecialPrivilege.filter(uid=self.id, priv=priv): return True return False # return db.session.query(User, Group, GroupPrivilege).filter( diff --git a/app/routes/account.py b/app/routes/account.py index aab1d20..867aab8 100644 --- a/app/routes/account.py +++ b/app/routes/account.py @@ -31,6 +31,7 @@ def account(): return render('account.html', form=form) @app.route('/account/delete', methods=['GET', 'POST']) +@login_required def delete_account(): del_form = DeleteAccountForm() if request.method == "POST": @@ -42,7 +43,7 @@ def delete_account(): return redirect(url_for('index')) else: flash('Erreur lors de la suppression du compte', 'error') - del_form.delete.data = False # Force to check the case to delete the account + del_form.delete.data = False # Force to tick to delete the account return render('delete_account.html', del_form=del_form) diff --git a/app/routes/search.py b/app/routes/search.py index ef3fb41..78c442d 100644 --- a/app/routes/search.py +++ b/app/routes/search.py @@ -2,6 +2,8 @@ from app import app from app.forms.search import AdvancedSearchForm from app.utils.render import render +from app.utils.priv_required import priv_required + @app.route('/search') def search(): form = AdvancedSearchForm() diff --git a/app/routes/users.py b/app/routes/users.py new file mode 100644 index 0000000..86b71df --- /dev/null +++ b/app/routes/users.py @@ -0,0 +1,30 @@ +from flask import redirect, url_for, request, flash +from flask_login import login_required, current_user, logout_user +from app import app, db +from app.forms.account import UpdateAccountForm, RegistrationForm, DeleteAccountForm +from app.models.users import Member +from app.utils.render import render + +@app.route('/users/') +def user(): + form = UpdateAccountForm() + if request.method == "POST": + if form.validate_on_submit(): + if form.avatar.data: + f = form.avatar.data + f.save("./app/static/"+current_user.avatar) + current_user.update( + email = form.email.data or None, + password = form.password.data or None, + birthday = form.birthday.data, + signature = form.signature.data, + biography = form.biography.data, + newsletter = form.newsletter.data + ) + db.session.merge(current_user) + db.session.commit() + flash('Modifications effectuées', 'ok') + else: + flash('Erreur lors de la modification', 'error') + + return render('account.html', form=form) \ No newline at end of file diff --git a/app/utils/priv_required.py b/app/utils/priv_required.py new file mode 100644 index 0000000..5a689cf --- /dev/null +++ b/app/utils/priv_required.py @@ -0,0 +1,41 @@ +from functools import wraps +from flask import redirect, url_for, request, flash +from flask_login import current_user +from flask_login.config import EXEMPT_METHODS +from app import app +from config import V5Config + +def priv_required(*perms): + """ + If you decorate a view with this, it will ensure that the current user is + authenticated and has required permissions before calling the actual view. + (If they are not, it calls the :attr:`LoginManager.unauthorized` callback.) + For example:: + + @app.route('/admin') + @priv_required('access-admin-board') + def admin_board(): + pass + + It can be convenient to globally turn off authentication when unit testing. + To enable this, if the application configuration variable `LOGIN_DISABLED` + is set to `True`, this decorator will be ignored. + """ + def decorated_view(func): + @wraps(func) + def wrapped(*args, **kwargs): + if request.method in EXEMPT_METHODS: + return func(*args, **kwargs) + elif app.config.get('LOGIN_DISABLED'): + #if app.config.get('LOGIN_DISABLED'): + return func(*args, **kwargs) + elif not current_user.is_authenticated: + return app.login_manager.unauthorized() + else: + for p in perms: + if not current_user.priv(p): + flash(V5Config.UNAUTHORIZED_MSG, 'error') + return redirect(url_for('index')) + return func(*args, **kwargs) + return wrapped + return decorated_view \ No newline at end of file diff --git a/config.py b/config.py index 415e58a..cf6b501 100644 --- a/config.py +++ b/config.py @@ -12,3 +12,6 @@ class V5Config(object): PRIVS_MAXLEN = 64 # Forbidden user names FORBIDDEN_USERNAMES = [ "admin", "root", "webmaster", "contact" ] + # Unauthorized message (@priv_required) + UNAUTHORIZED_MSG = "Vous n'avez pas l'autorisation d'effectuer cette action !" +