Browse Source

privs: create privileges from groups and users

The groups-privileges page takes care of removing privileges
before deleting groups and users; this is to be moved soon to a
proper group/user deletion API.
master
Lephe 8 months ago
parent
commit
7921bb5765

+ 4
- 0
app/models/privs.py View File

@@ -50,6 +50,10 @@ class Group(db.Model):
self.description = descr
self.members = []

def privs(self):
gps = GroupPrivilege.query.filter_by(gid=self.id).all()
return [ gp.priv for gp in gps ]

def __repr__(self):
return f'<Group "{self.name}">'


+ 4
- 0
app/models/users.py View File

@@ -134,6 +134,10 @@ class Member(User, db.Model):
# Group.id.in_(User.groups), GroupPrivilege.gid==Group.id,
# GroupPrivilege.priv==priv).first() is not None

def special_privileges(self):
sp = SpecialPrivilege.query.filter_by(mid=self.id).all()
return sorted(row.priv for row in sp)

def update(self, **data):
"""
Update all or part of the user's metadata. The [data] dictionary

+ 63
- 33
app/routes/admin.py View File

@@ -11,44 +11,67 @@ from app import app, db

@app.route('/admin', methods=['GET', 'POST'])
@priv_required('admin-panel')
def admin():
class AdminForm(FlaskForm):
def adm():
return render('admin/index.html')

# Default groups and privileges.
default_groups = [
('Administrateur', 'color: #ee0000',
"Vous voyez Chuck Norris ? Pareil."),
('Modérateur', 'color: green',
"Maîtres du kick, ils sont là pour faire respecter un semblant " +
"d'ordre."),
('Développeur', 'color: #4169e1',
"Les développeurs maintiennent et améliorent le code du site."),
('Rédacteur', 'color: blue',
"Rédigent les meilleurs articles de la page d'accueil, rien " +
"que pour vous <3"),
('Responsable communauté', 'color: DarkOrange',
"Anime les pages Twitter et Facebook de Planète Casio et " +
"surveille l'évolution du monde autour de nous !"),
('Partenaire', 'color: purple',
"Membres de l'équipe d'administration des sites partenaires."),
('Compte communautaire', 'background: #d8d8d8; border-radius: ' +
'4px; color:#303030; padding: 1px 2px',
"Compte à usage général de l'équipe de Planète Casio."),

('Robot', 'color: #cf25d0',
"♫ Je suis Nono, le petit robot, l'ami d'Ulysse ♫",
"shoutbox-kick shoutbox-ban".split()),

('Membre de CreativeCalc', 'color: #222222',
"CreativeCalc est l'association qui gère Planète Casio.",
"access-assoc-board".split()),
]

@app.route('/admin/groups', methods=['GET', 'POST'])
@priv_required('admin-manel')
def adm_groups():
class GroupRegenerationForm(FlaskForm):
submit = SubmitField('Régénérer les groupes, privilèges, et comptes communs')

form = AdminForm()
form = GroupRegenerationForm()
if form.validate_on_submit():
# Clean up groups
for g in Group.query.all():
for gp in GroupPrivilege.query.filter_by(gid=g.id).all():
db.session.delete(gp)
db.session.commit()
db.session.delete(g)
db.session.commit( )

# Create base groups
groups = [
('Administrateur', 'color: #ee0000',
"Vous voyez Chuck Norris ? Pareil."),
('Modérateur', 'color: green',
"Maîtres du kick, ils sont là pour faire respecter un semblant " +
"d'ordre."),
('Développeur', 'color: #4169e1',
"Les développeurs maintiennent et améliorent le code du site."),
('Rédacteur', 'color: blue',
"Rédigent les meilleurs articles de la page d'accueil, rien " +
"que pour vous <3"),
('Responsable communauté', 'color: DarkOrange',
"Anime les pages Twitter et Facebook de Planète Casio et " +
"surveille l'évolution du monde autour de nous !"),
('Partenaire', 'color: purple',
"Membres de l'équipe d'administration des sites partenaires."),
('Compte communautaire', 'background: #d8d8d8; border-radius: ' +
'4px; color:#303030; padding: 1px 2px',
"Compte à usage général de l'équipe de Planète Casio."),
('Robot', 'color: #cf25d0',
"♫ Je suis Nono, le petit robot, l'ami d'Ulysse ♫"),
('Membre de CreativeCalc', 'color: #222222',
"CreativeCalc est l'association qui gère Planète Casio."),
]
groups = [ Group(g[0], g[1], g[2]) for g in default_groups ]
for g in groups:
db.session.add(Group(*g))
db.session.add(g)
db.session.commit()

for g, dg in zip(groups, default_groups):
if len(dg) < 4:
continue
for priv in dg[3]:
db.session.add(GroupPrivilege(g, priv))
db.session.commit()

# Clean up test members
for name in "PlanèteCasio GLaDOS".split():
@@ -75,6 +98,8 @@ def admin():
m = Member('GLaDOS', 'glados@aperture.science', 'v5-forever')
addgroup(m, "Robot")
db.session.add(m)
db.session.commit()

db.session.add(SpecialPrivilege(m, "edit-posts"))
db.session.add(SpecialPrivilege(m, "shoutbox-ban"))

@@ -82,7 +107,7 @@ def admin():

users = Member.query.all()
groups = Group.query.all()
return render('admin/index.html', users=users, groups=groups, form=form)
return render('admin/groups_privileges.html', users=users, groups=groups, form=form)

@app.route('/admin/edit-account/<user_id>', methods=['GET', 'POST'])
@priv_required('edit-account')
@@ -117,9 +142,7 @@ def adm_edit_account(user_id):
@app.route('/admin/edit-account/<user_id>/delete', methods=['GET', 'POST'])
@priv_required('delete-account')
def adm_delete_account(user_id):
user = Member.query.filter_by(id=user_id).first()
if not user:
abort(404)
user = Member.query.filter_by(id=user_id).first_or_404()

# Note: A user deleting their own account will be disconnected.

@@ -131,10 +154,17 @@ def adm_delete_account(user_id):
del_form = AdminDeleteAccountForm()
if request.method == "POST":
if del_form.validate_on_submit():
# First delete all special privileges for this user
for sp in SpecialPrivilege.query.filter_by(mid=user.id).all():
db.session.delete(sp)
db.session.commit()

# Then delete the user
db.session.delete(user)
db.session.commit()

flash('Compte supprimé', 'ok')
return redirect(url_for('admin'))
return redirect(url_for('adm'))
else:
flash('Erreur lors de la suppression du compte', 'error')
del_form.delete.data = False # Force to tick to delete the account

+ 4
- 1
app/static/css/header.css View File

@@ -18,7 +18,10 @@ header {
}
}

header h1 {
header .title a {
color: inherit;
}
header .title h1 {
font-family: Cantarell; font-weight: bold; font-size: 18px;
color: #181818;
display: inline;

+ 8
- 2
app/templates/admin/delete_account.html View File

@@ -1,12 +1,18 @@
{% extends "base/base.html" %}

{% block title %}
Panneau d'administration » <h1>Suppression du compte '{{ user.name }}'</h1>
<a href="{{ url_for('adm') }}">Panneau d'administration</a> » <h1>Suppression du compte de '{{ user.name }}'</h1>
{% endblock %}

{% block content %}
<section class="form">
<h2>Suppression du compte de '{{ user.name }}'</h2>
<h2>Confirmer la suppression du compte</h2>
<p>Le compte '{{ user.name }}' que vous allez supprimer est lié à :</p>
<ul>
<li>{{ user.groups | length }} groupes</li>
<li>{{ user.special_privileges() | length }} privilèges spéciaux</li>
</ul>

<form action="{{ url_for('adm_delete_account', user_id=user.id) }}" method=post>
{{ del_form.hidden_tag() }}
<div>

+ 1
- 5
app/templates/admin/edit_account.html View File

@@ -1,14 +1,10 @@
{% extends "base/base.html" %}

{% block title %}
Panneau d'administration » <h1>Édition du compte de '{{ user.name }}'</h1>
<a href="{{ url_for('adm') }}">Panneau d'administration</a> » <h1>Édition du compte de '{{ user.name }}'</h1>
{% endblock %}

{% block content %}
<section class="form">
<a href="{{ url_for('admin') }}" class="button bg-white">Retour à l'administration</a>
</section>

<section class="form">
<form action="{{ url_for('adm_edit_account', user_id=user.id) }}" method="post" enctype="multipart/form-data">
{{ form.hidden_tag() }}

+ 74
- 0
app/templates/admin/groups_privileges.html View File

@@ -0,0 +1,74 @@
{% extends "base/base.html" %}

{% block title %}
<a href="{{ url_for('adm') }}">Panneau d'administration</a> » <h1>Groupes et privilèges</h1>
{% endblock %}

{% block content %}
<section>
<p>Cette page présente une vue d'ensemble des groupes et privilèges
associés. Elle supervise également les détenteurs de privilèges.</p>

<h2>Membres détenteurs de privilèges</h2>

<table style="width:90%; margin: auto;">
<tr><th>Pseudo</th><th>Email</th><th>Groupes</th>
<th>Privilèges spéciaux</th><th>Modifier</th></tr>

{% for user in users %}
<tr><td><a href="{{ url_for('user_by_id', user_id=user.id) }}" title="Page de profil publique de {{ user.name }}">{{ user.name }}</a></td>
<td>{{ user.email }}</td>
<td>{% for g in user.groups %}
<span style="{{ g.css }}">{{ g.name }}</span>
{{ ', ' if not loop.last }}
{% endfor %}</td>
<td>{% for priv in user.special_privileges() %}
<code>{{ priv }}</code>
{{- ', ' if not loop.last }}
{% endfor %}</td>
<td><a href="{{ url_for('adm_edit_account', user_id=user.id) }}">Modifier</a></td>
</tr>
{% endfor %}
</table>

<h2>Liste des groupes</h2>

<table style="width:90%; margin: auto;">
<tr><th>Groupe</th><th>Membres</th><th>Privilèges</th></tr>

{% for group in groups %}
<tr><td><span style="{{ group.css }}">{{ group.name }}</span></td><td>
{% for user in group.members %}
{{ user.name }}
{% endfor %}
</td><td>
{% for priv in group.privs() %}
<code>{{ priv }}</code>
{{- ', ' if not loop.last }}
{% endfor %}
</td></tr>
{% endfor %}
</table>

<h2>Restauration des groupes et privilèges</h2>

<p>Cette fonction régénère un ensemble minimal de groupes et membres
permettant de lancer le forum. Elle opère les modifications
suivantes :</p>

<ul>
<li>Suppression de tous les groupes.</li>
<li>Création des groupes Administrateur, Modérateur, Développeur,
Rédacteur, Responsable communauté, Partenaire, Compte communautaire,
Robot, Membre de CreativeCalc.</li>
<li>Attribution des privilèges associés à ces groupes.</li>
<li>Recréation des comptes communs : PlanèteCasio (compte communautaire),
GLaDOS (robot).</li>
</ul>

<form action='' method='POST'>
{{ form.hidden_tag() }}
{{ form.submit(class="bg-orange") }}
</form>
</section>
{% endblock %}

+ 5
- 60
app/templates/admin/index.html View File

@@ -1,69 +1,14 @@
{% extends "base/base.html" %}

{% block title %}
Panneau d'administration » <h1>Utilisateurs et groupes</h1>
<h1>Panneau d'administration</h1>
{% endblock %}

{% block content %}
<section>
<p>Cette page présente une vue d'ensemble des utilisateurs et groupes, ainsi
que les privilèges associés. Seuls les utilisateurs appartenant à un
groupe ou possédant au moins un privilège spécial sont affichés.</p>

<h2>Membres associés à des groupes ou privilèges spéciaux</h2>

<table style="width:90%; margin: auto;">
<tr><th>Pseudo</th><th>Email</th><th>Date d'inscription</th><th>XP</th>
<th>Innovation</th><th>Newsletter</th><th>Modifier</th></tr>

{% for user in users %}
<tr><td><a href="{{ url_for('user_by_id', user_id=user.id) }}" title="Page de profil publique de {{ user.name }}">{{ user.name }}</a></td>
<td>{{ user.email }}</td>
<td>{{ user.register_date }}</td><td>{{ user.xp }}</td>
<td>{{ user.innovation }}</td>
<td>{{ "Oui" if user.newsletter else "Non" }}</td>
<td><a href="{{ url_for('adm_edit_account', user_id=user.id) }}">Modifier</a></td>
</tr>
{% endfor %}
</table>

<h2>Liste des groupes</h2>

<table style="width:90%; margin: auto;">
<tr><th>Groupe</th><th>Membres</th><th>Privilèges</th></tr>

{% for group in groups %}
<tr><td><span style="{{ group.css }}">{{ group.name }}</span></td><td>
{% for user in group.members %}
{{ user.name }}
{% endfor %}
</td><td>
{% for priv in group.privs %}
<code>{{ priv }}</code>
{% endfor %}
</td></tr>
{% endfor %}
</table>

<h2>Restauration des groupes et privilèges</h2>

<p>Cette fonction régénère un ensemble minimal de groupes et membres
permettant de lancer le forum. Elle opère les modifications
suivantes :</p>

<ul>
<li>Suppression de tous les groupes.</li>
<li>Création des groupes Administrateur, Modérateur, Développeur,
Rédacteur, Responsable communauté, Partenaire, Compte communautaire,
Robot, Membre de CreativeCalc.</li>
<li>Attribution des privilèges associés à ces groupes.</li>
<li>Recréation des comptes communs : PlanèteCasio (compte communautaire),
GLaDOS (robot).</li>
</ul>

<form action='' method='POST'>
{{ form.hidden_tag() }}
{{ form.submit(class="bg-orange") }}
</form>
<p>Pages générales du panneau d'administration :</p>
<ul>
<li><a href="{{ url_for('adm_groups') }}">Groupes et privilèges</a></li>
</ul>
</section>
{% endblock %}

+ 1
- 1
app/templates/base/navbar/account.html View File

@@ -24,7 +24,7 @@
</svg>
Topics favoris
</a>
<a href="{{ url_for('admin') }}">
<a href="{{ url_for('adm') }}">
<svg viewBox="0 0 24 24">
<path fill="#ffffff" d="M3,1H19A1,1 0 0,1 20,2V6A1,1 0 0,1 19,7H3A1,1 0 0,1 2,6V2A1,1 0 0,1 3,1M3,9H19A1,1 0 0,1 20,10V10.67L17.5,9.56L11,12.44V15H3A1,1 0 0,1 2,14V10A1,1 0 0,1 3,9M3,17H11C11.06,19.25 12,21.4 13.46,23H3A1,1 0 0,1 2,22V18A1,1 0 0,1 3,17M8,5H9V3H8V5M8,13H9V11H8V13M8,21H9V19H8V21M4,3V5H6V3H4M4,11V13H6V11H4M4,19V21H6V19H4M17.5,12L22,14V17C22,19.78 20.08,22.37 17.5,23C14.92,22.37 13,19.78 13,17V14L17.5,12M17.5,13.94L15,15.06V17.72C15,19.26 16.07,20.7 17.5,21.06V13.94Z"></path>
</svg>

Loading…
Cancel
Save