Merge pull request #47 from DanielGrams/issue/46-edit-user-roles

#46 Edit user roles
This commit is contained in:
Daniel Grams 2020-12-20 19:45:57 +01:00 committed by GitHub
commit 0200249fdc
7 changed files with 180 additions and 5 deletions

View File

@ -2,6 +2,7 @@ from flask_wtf import FlaskForm
from flask_babelex import lazy_gettext
from wtforms import TextAreaField, SubmitField
from wtforms.validators import Optional
from project.forms.widgets import MultiCheckboxField
class AdminSettingsForm(FlaskForm):
@ -11,3 +12,8 @@ class AdminSettingsForm(FlaskForm):
privacy = TextAreaField(lazy_gettext("Privacy"), validators=[Optional()])
submit = SubmitField(lazy_gettext("Save"))
class UpdateUserForm(FlaskForm):
roles = MultiCheckboxField(lazy_gettext("Roles"))
submit = SubmitField(lazy_gettext("Update user"))

View File

@ -6,20 +6,37 @@ def create_user(email, password):
return user_datastore.create_user(email=email, password=hash_password(password))
def add_roles_to_user(email, role_names):
def add_roles_to_user(email, roles):
user = find_user_by_email(email)
if user is None:
raise ValueError("User with given email does not exist.")
for role_name in role_names:
user_datastore.add_role_to_user(user, role_name)
for role in roles:
user_datastore.add_role_to_user(user, role)
def add_admin_roles_to_user(email):
add_roles_to_user(email, ["admin", "event_verifier"])
def remove_roles_from_user(email, roles):
user = find_user_by_email(email)
for role in roles:
user_datastore.remove_role_from_user(user, role)
def remove_all_roles_from_user(email):
user = find_user_by_email(email)
remove_roles_from_user(email, user.roles)
def set_roles_for_user(email, roles):
remove_all_roles_from_user(email)
add_roles_to_user(email, roles)
def upsert_user_role(role_name, role_title, permissions):
role = user_datastore.find_or_create_role(role_name)
role.title = role_title

View File

@ -19,6 +19,10 @@
{{ _('Admin Units') }}
<i class="fa fa-caret-right"></i>
</a>
<a href="{{ url_for('admin_users') }}" class="list-group-item">
{{ _('Users') }}
<i class="fa fa-caret-right"></i>
</a>
</div>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_field_with_errors, render_field %}
{% block title %}
{{ _('Update user') }}
{% endblock %}
{% block content %}
<h1>{{ _('Update user') }}</h1>
<form action="" method="POST">
{{ form.hidden_tag() }}
<div class="card mb-4">
<div class="card-header">
{{ user.email }}
</div>
<div class="card-body">
{{ render_field_with_errors(form.roles, ri="multicheckbox") }}
</div>
</div>
{{ render_field(form.submit) }}
</form>
{% endblock %}

View File

@ -0,0 +1,36 @@
{% extends "layout.html" %}
{% from "_macros.html" import render_pagination %}
{% block title %}
{{ _('Users') }}
{% endblock %}
{% block content %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{{ url_for('admin') }}">{{ _('Admin') }}</a></li>
<li class="breadcrumb-item active" aria-current="page">{{ _('Users') }}</li>
</ol>
</nav>
<div class="table-responsive">
<table class="table table-sm table-bordered table-hover table-striped">
<thead>
<tr>
<th>{{ _('Email') }}</th>
<th></th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.email }}</td>
<td><a href="{{ url_for('admin_user_update', id=user.id) }}">{{ _('Edit') }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="my-4">{{ render_pagination(pagination) }}</div>
{% endblock %}

View File

@ -1,15 +1,18 @@
from project import app, db
from project.models import AdminUnit
from project.models import AdminUnit, User, Role
from flask import render_template, flash, url_for, redirect
from flask_babelex import gettext
from flask_security import roles_required
from project.forms.admin import AdminSettingsForm
from project.forms.admin import AdminSettingsForm, UpdateUserForm
from project.services.admin import upsert_settings
from project.services.user import set_roles_for_user
from project.views.utils import (
flash_errors,
handleSqlError,
get_pagination_urls,
)
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.sql import func
@app.route("/admin")
@ -44,3 +47,38 @@ def admin_settings():
flash_errors(form)
return render_template("admin/settings.html", form=form)
@app.route("/admin/users")
@roles_required("admin")
def admin_users():
users = User.query.order_by(func.lower(User.email)).paginate()
return render_template(
"admin/users.html", users=users.items, pagination=get_pagination_urls(users)
)
@app.route("/admin/user/<int:id>/update", methods=("GET", "POST"))
@roles_required("admin")
def admin_user_update(id):
user = User.query.get_or_404(id)
form = UpdateUserForm()
form.roles.choices = [
(c.name, gettext(c.title)) for c in Role.query.order_by(Role.id).all()
]
if form.validate_on_submit():
set_roles_for_user(user.email, form.roles.data)
try:
db.session.commit()
flash(gettext("User successfully updated"), "success")
return redirect(url_for("admin_users"))
except SQLAlchemyError as e:
db.session.rollback()
flash(handleSqlError(e), "danger")
else:
form.roles.data = [c.name for c in user.roles]
return render_template("admin/update_user.html", user=user, form=form)

View File

@ -58,3 +58,52 @@ def test_admin_settings(client, seeder, utils, app, mocker, db_error):
assert settings.legal_notice == "Mein Impressum"
assert settings.contact == "Mein Kontakt"
assert settings.privacy == "Mein Datenschutz"
def test_admin_users(client, seeder, utils, app):
seeder.create_user(admin=True)
user = utils.login()
seeder.create_admin_unit(user, "Meine Crew")
response = client.get("/admin/users")
assert b"test@test.de" in response.data
@pytest.mark.parametrize("db_error", [True, False])
def test_admin_user_update(client, seeder, utils, app, mocker, db, db_error):
user_id, admin_unit_id = seeder.setup_base(True)
other_user_id = seeder.create_user("other@test.de")
with app.app_context():
from project.models import User
from project.services.user import set_roles_for_user
user = User.query.get_or_404(other_user_id)
set_roles_for_user(user.email, ["event_verifier"])
db.session.commit()
url = utils.get_url("admin_user_update", id=other_user_id)
response = utils.get_ok(url)
if db_error:
utils.mock_db_commit(mocker)
response = utils.post_form(
url,
response,
{
"roles": "admin",
},
)
if db_error:
utils.assert_response_db_error(response)
return
utils.assert_response_redirect(response, "admin_users")
with app.app_context():
from project.models import User
user = User.query.get_or_404(other_user_id)
assert len(user.roles) == 1
assert any(r.name == "admin" for r in user.roles)