mirror of
https://github.com/spl0k/supysonic.git
synced 2024-09-19 19:01:03 +00:00
Apply changes
This commit is contained in:
parent
8e2adf8fc8
commit
b2f0c99f79
@ -33,6 +33,9 @@ log_level = WARNING
|
||||
; for testing purposes. Default: on
|
||||
;mount_api = on
|
||||
|
||||
; Require API key for authentication. Default: yes
|
||||
require_api_key = yes
|
||||
|
||||
; Enable the administrative web interface. Default: on
|
||||
;mount_webui = on
|
||||
|
||||
@ -87,3 +90,21 @@ default_transcode_target = mp3
|
||||
;mp3 = audio/mpeg
|
||||
;ogg = audio/vorbis
|
||||
|
||||
[ldap]
|
||||
; Server URL. Default: none
|
||||
;url = ldapi://%2Frun%2Fslapd%2Fldapi
|
||||
;url = ldap://127.0.0.1:389
|
||||
|
||||
; Bind credentials. Default: none
|
||||
;bind_dn = cn=username,dc=example,dc=org
|
||||
;bind_pw = password
|
||||
|
||||
; Base DN. Default: none
|
||||
;base_dn = ou=Users,dc=example,dc=org
|
||||
|
||||
; User filter. The variable '{username}' is used for filtering. Default: none
|
||||
;user_filter = (&(objectClass=inetOrgperson)(uid={username}))
|
||||
|
||||
; Mail attribute. Default: mail
|
||||
;mail_attr = mail
|
||||
|
||||
|
@ -11,6 +11,7 @@ import binascii
|
||||
import uuid
|
||||
from flask import request
|
||||
from flask import Blueprint
|
||||
from flask import current_app
|
||||
from peewee import IntegrityError
|
||||
|
||||
from ..db import ClientPrefs, Folder
|
||||
@ -56,7 +57,13 @@ def decode_password(password):
|
||||
|
||||
@api.before_request
|
||||
def authorize():
|
||||
require_api_key = current_app.config["WEBAPP"]["require_api_key"]
|
||||
|
||||
if request.authorization:
|
||||
user = UserManager.try_auth_api(
|
||||
request.authorization.username, request.authorization.password
|
||||
)
|
||||
if user is None and not require_api_key:
|
||||
user = UserManager.try_auth(
|
||||
request.authorization.username, request.authorization.password
|
||||
)
|
||||
@ -69,7 +76,11 @@ def authorize():
|
||||
password = request.values["p"]
|
||||
password = decode_password(password)
|
||||
|
||||
user = UserManager.try_auth(username, password)
|
||||
user = UserManager.try_auth_api(username, password)
|
||||
if user is None and not require_api_key:
|
||||
user = UserManager.try_auth(
|
||||
request.authorization.username, request.authorization.password
|
||||
)
|
||||
if user is None:
|
||||
raise Unauthorized()
|
||||
|
||||
|
121
supysonic/cli.py
121
supysonic/cli.py
@ -7,6 +7,7 @@
|
||||
|
||||
import click
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from click.exceptions import ClickException
|
||||
|
||||
@ -236,12 +237,12 @@ def user():
|
||||
def user_list():
|
||||
"""Lists users."""
|
||||
|
||||
click.echo("Name\t\tAdmin\tJukebox\tEmail")
|
||||
click.echo("----\t\t-----\t-------\t-----")
|
||||
click.echo("Name\t\tLDAP\tAdmin\tJukebox\tEmail")
|
||||
click.echo("----\t\t-----\t-----\t-------\t-----")
|
||||
for u in User.select():
|
||||
click.echo(
|
||||
"{: <16}{}\t{}\t{}".format(
|
||||
u.name, "*" if u.admin else "", "*" if u.jukebox else "", u.mail
|
||||
"{: <16}{}\t{}\t{}\t{}".format(
|
||||
u.name, "*" if u.ldap else "", "*" if u.admin else "", "*" if u.jukebox else "", u.mail
|
||||
)
|
||||
)
|
||||
|
||||
@ -249,7 +250,7 @@ def user_list():
|
||||
@user.command("add")
|
||||
@click.argument("name")
|
||||
@click.password_option("-p", "--password", help="Specifies the user's password")
|
||||
@click.option("-e", "--email", default="", help="Sets the user's email address")
|
||||
@click.option("-e", "--email", default=None, help="Sets the user's email address")
|
||||
def user_add(name, password, email):
|
||||
"""Adds a new user.
|
||||
|
||||
@ -262,10 +263,42 @@ def user_add(name, password, email):
|
||||
raise ClickException(str(e)) from e
|
||||
|
||||
|
||||
@user.group("edit")
|
||||
def user_edit():
|
||||
"""User edit commands"""
|
||||
pass
|
||||
|
||||
|
||||
@user_edit.command("email")
|
||||
@click.argument("name")
|
||||
@click.option("-e", "--email", prompt=True, default="", help="Sets the user's email address")
|
||||
def user_edit_email(name, email):
|
||||
"""Changes an user's email.
|
||||
|
||||
|
||||
NAME is the name (or login) of the user to edit.
|
||||
"""
|
||||
|
||||
user = User.get(name=name)
|
||||
if user is None:
|
||||
raise ClickException("No such user")
|
||||
|
||||
if user.ldap:
|
||||
raise ClickException("Unavailable for LDAP users")
|
||||
|
||||
if email == "":
|
||||
email = None
|
||||
|
||||
if user.mail != email:
|
||||
user.mail = email
|
||||
user.save()
|
||||
click.echo(f"Updated user '{name}'")
|
||||
|
||||
|
||||
@user.command("delete")
|
||||
@click.argument("name")
|
||||
def user_delete(name):
|
||||
"""Deletes a user.
|
||||
"""Deletes an user.
|
||||
|
||||
NAME is the name of the user to delete.
|
||||
"""
|
||||
@ -295,7 +328,7 @@ def _echo_role_change(username, name, value):
|
||||
help="Grant or revoke jukebox rights",
|
||||
)
|
||||
def user_roles(name, admin, jukebox):
|
||||
"""Enable/disable rights for a user.
|
||||
"""Enable/disable rights for an user.
|
||||
|
||||
NAME is the login of the user to which grant or revoke rights.
|
||||
"""
|
||||
@ -314,27 +347,31 @@ def user_roles(name, admin, jukebox):
|
||||
user.save()
|
||||
|
||||
|
||||
@user.command("changepass")
|
||||
@user_edit.command("password")
|
||||
@click.argument("name")
|
||||
@click.password_option("-p", "--password", help="New password")
|
||||
def user_changepass(name, password):
|
||||
"""Changes a user's password.
|
||||
"""Changes an user's password.
|
||||
|
||||
NAME is the login of the user to which change the password.
|
||||
"""
|
||||
|
||||
try:
|
||||
user = User.get(name=name)
|
||||
if user is None:
|
||||
raise ClickException(f"User '{name}' does not exist.")
|
||||
|
||||
if user.ldap:
|
||||
raise ClickException("Unavailable for LDAP users")
|
||||
|
||||
UserManager.change_password2(name, password)
|
||||
click.echo(f"Successfully changed '{name}' password")
|
||||
except User.DoesNotExist as e:
|
||||
raise ClickException(f"User '{name}' does not exist.") from e
|
||||
|
||||
|
||||
@user.command("rename")
|
||||
@user_edit.command("username")
|
||||
@click.argument("name")
|
||||
@click.argument("newname")
|
||||
def user_rename(name, newname):
|
||||
"""Renames a user.
|
||||
"""Renames an user.
|
||||
|
||||
User NAME will then be known as NEWNAME.
|
||||
"""
|
||||
@ -361,6 +398,62 @@ def user_rename(name, newname):
|
||||
click.echo(f"User '{name}' renamed to '{newname}'")
|
||||
|
||||
|
||||
@user.group("apikey")
|
||||
def user_apikey():
|
||||
"""User API key commands"""
|
||||
pass
|
||||
|
||||
|
||||
@user_apikey.command("show")
|
||||
@click.argument("name")
|
||||
def user_apikey_show(name):
|
||||
"""Shows the API key of an user.
|
||||
|
||||
NAME is the name (or login) of the user to show the API key.
|
||||
"""
|
||||
|
||||
user = User.get(name=name)
|
||||
if user is None:
|
||||
raise ClickException(f"User '{name}' does not exist.")
|
||||
|
||||
click.echo(f"'{name}' API key: {user.api_key}")
|
||||
|
||||
|
||||
@user_apikey.command("new")
|
||||
@click.argument("name")
|
||||
def user_apikey_new(name):
|
||||
"""Generates a new API key for an user.
|
||||
|
||||
NAME is the name (or login) of the user to generate the API key for.
|
||||
"""
|
||||
|
||||
user = User.get(name=name)
|
||||
if user is None:
|
||||
raise ClickException(f"User '{name}' does not exist.")
|
||||
|
||||
user.api_key = str(uuid.uuid4()).replace("-", "")
|
||||
user.save()
|
||||
click.echo(f"Updated '{name}' API key")
|
||||
|
||||
|
||||
@user_apikey.command("delete")
|
||||
@click.argument("name")
|
||||
def user_apikey_delete(name):
|
||||
"""Deletes the API key of an user.
|
||||
|
||||
NAME is the name (or login) of the user to delete the API key.
|
||||
"""
|
||||
|
||||
user = User.get(name=name)
|
||||
if user is None:
|
||||
raise ClickException(f"User '{name}' does not exist.")
|
||||
|
||||
if user.api_key is not None:
|
||||
user.api_key = None
|
||||
user.save()
|
||||
click.echo(f"Deleted '{name}' API key")
|
||||
|
||||
|
||||
def main():
|
||||
config = IniConfig.from_common_locations()
|
||||
init_database(config.BASE["database_uri"])
|
||||
|
@ -36,6 +36,7 @@ class DefaultConfig:
|
||||
"log_level": "WARNING",
|
||||
"mount_webui": True,
|
||||
"mount_api": True,
|
||||
"require_api_key": True,
|
||||
"index_ignored_prefixes": "El La Le Las Les Los The",
|
||||
}
|
||||
DAEMON = {
|
||||
@ -51,6 +52,14 @@ class DefaultConfig:
|
||||
LASTFM = {"api_key": None, "secret": None}
|
||||
TRANSCODING = {}
|
||||
MIMETYPES = {}
|
||||
LDAP = {
|
||||
"url": None,
|
||||
"bind_dn": None,
|
||||
"bind_pw": None,
|
||||
"base_dn": None,
|
||||
"user_filter": None,
|
||||
"mail_attr": "mail"
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
current_config = self
|
||||
|
@ -428,12 +428,15 @@ class User(_Model):
|
||||
id = PrimaryKeyField()
|
||||
name = CharField(64, unique=True)
|
||||
mail = CharField(null=True)
|
||||
password = FixedCharField(40)
|
||||
salt = FixedCharField(6)
|
||||
password = FixedCharField(40, null=True)
|
||||
salt = FixedCharField(6, null=True)
|
||||
|
||||
ldap = BooleanField(default=False)
|
||||
admin = BooleanField(default=False)
|
||||
jukebox = BooleanField(default=False)
|
||||
|
||||
api_key = CharField(32, null=True)
|
||||
|
||||
lastfm_session = FixedCharField(32, null=True)
|
||||
lastfm_status = BooleanField(
|
||||
default=True
|
||||
|
@ -6,6 +6,7 @@
|
||||
# Distributed under terms of the GNU AGPLv3 license.
|
||||
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from flask import flash, redirect, render_template, request, session, url_for
|
||||
from flask import current_app
|
||||
@ -172,6 +173,10 @@ def change_username_post(uid):
|
||||
@frontend.route("/user/<uid>/changemail")
|
||||
@me_or_uuid
|
||||
def change_mail_form(uid, user):
|
||||
if user.ldap:
|
||||
flash("Unavailable for LDAP users")
|
||||
return redirect(url_for("frontend.user_profile", uid=uid))
|
||||
else:
|
||||
return render_template("change_mail.html", user=user)
|
||||
|
||||
|
||||
@ -179,14 +184,25 @@ def change_mail_form(uid, user):
|
||||
@me_or_uuid
|
||||
def change_mail_post(uid, user):
|
||||
mail = request.form.get("mail", "")
|
||||
if mail == "":
|
||||
mail = None
|
||||
if user.mail == mail:
|
||||
flash("No changes made")
|
||||
else:
|
||||
# No validation, lol.
|
||||
user.mail = mail
|
||||
user.save()
|
||||
flash("Email changed")
|
||||
return redirect(url_for("frontend.user_profile", uid=uid))
|
||||
|
||||
|
||||
@frontend.route("/user/<uid>/changepass")
|
||||
@me_or_uuid
|
||||
def change_password_form(uid, user):
|
||||
if user.ldap:
|
||||
flash("Unavailable for LDAP users")
|
||||
return redirect(url_for("frontend.user_profile", uid=uid))
|
||||
else:
|
||||
return render_template("change_pass.html", user=user)
|
||||
|
||||
|
||||
@ -235,8 +251,8 @@ def add_user_form():
|
||||
def add_user_post():
|
||||
error = False
|
||||
args = request.form.copy()
|
||||
(name, passwd, passwd_confirm) = map(
|
||||
args.pop, ("user", "passwd", "passwd_confirm"), (None,) * 3
|
||||
(name, passwd, passwd_confirm, mail) = map(
|
||||
args.pop, ("user", "passwd", "passwd_confirm", "mail"), (None,) * 4
|
||||
)
|
||||
if not name:
|
||||
flash("The name is required.")
|
||||
@ -248,9 +264,12 @@ def add_user_post():
|
||||
flash("The passwords don't match.")
|
||||
error = True
|
||||
|
||||
if mail == "":
|
||||
mail = None
|
||||
|
||||
if not error:
|
||||
try:
|
||||
UserManager.add(name, passwd, **args)
|
||||
UserManager.add(name, passwd, mail=mail, **args)
|
||||
flash(f"User '{name}' successfully added")
|
||||
return redirect(url_for("frontend.user_index"))
|
||||
except ValueError as e:
|
||||
@ -333,3 +352,24 @@ def logout():
|
||||
session.clear()
|
||||
flash("Logged out!")
|
||||
return redirect(url_for("frontend.login"))
|
||||
|
||||
|
||||
@frontend.route("/user/<uid>/new_api_key")
|
||||
@me_or_uuid
|
||||
def new_api_key(uid, user):
|
||||
user.api_key = str(uuid.uuid4()).replace("-", "")
|
||||
user.save()
|
||||
flash("API key updated")
|
||||
return redirect(url_for("frontend.user_profile", uid=uid))
|
||||
|
||||
|
||||
@frontend.route("/user/<uid>/del_api_key")
|
||||
@me_or_uuid
|
||||
def del_api_key(uid, user):
|
||||
if user.api_key is None:
|
||||
flash("No changes made")
|
||||
else:
|
||||
user.api_key = None
|
||||
user.save()
|
||||
flash("API key deleted")
|
||||
return redirect(url_for("frontend.user_profile", uid=uid))
|
||||
|
47
supysonic/ldap.py
Normal file
47
supysonic/ldap.py
Normal file
@ -0,0 +1,47 @@
|
||||
# This file is part of Supysonic.
|
||||
# Supysonic is a Python implementation of the Subsonic server API.
|
||||
#
|
||||
# Copyright (C) 2013-2018 Alban 'spl0k' Féron
|
||||
#
|
||||
# Distributed under terms of the GNU AGPLv3 license.
|
||||
|
||||
import logging
|
||||
|
||||
try:
|
||||
import ldap3
|
||||
except ModuleNotFoundError:
|
||||
ldap3 = None
|
||||
|
||||
from flask import current_app
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Ldap:
|
||||
@staticmethod
|
||||
def try_auth(username, password):
|
||||
config = current_app.config["LDAP"]
|
||||
if None in config.values():
|
||||
return
|
||||
elif not ldap3:
|
||||
logger.warning("Module 'ldap3' is not installed.")
|
||||
return
|
||||
|
||||
server = ldap3.Server(config["url"], get_info=None)
|
||||
|
||||
with ldap3.Connection(server, config["bind_dn"], config["bind_pw"], read_only=True) as conn:
|
||||
conn.search(
|
||||
config["base_dn"],
|
||||
config["user_filter"].format(username=username),
|
||||
search_scope=ldap3.LEVEL,
|
||||
attributes=[config["mail_attr"]],
|
||||
size_limit=1
|
||||
)
|
||||
entries = conn.entries
|
||||
|
||||
if entries:
|
||||
try:
|
||||
with ldap3.Connection(server, entries[0].entry_dn, password, read_only=True):
|
||||
return {"mail": entries[0][config["mail_attr"]].value}
|
||||
except ldap3.core.exceptions.LDAPBindError:
|
||||
return False
|
@ -12,6 +12,7 @@ import string
|
||||
import uuid
|
||||
|
||||
from ..db import User
|
||||
from ..ldap import Ldap
|
||||
|
||||
|
||||
class UserManager:
|
||||
@ -45,14 +46,41 @@ class UserManager:
|
||||
user.delete_instance(recursive=True)
|
||||
|
||||
@staticmethod
|
||||
def try_auth(name, password):
|
||||
def try_auth_api(name, password):
|
||||
user = User.get_or_none(name=name)
|
||||
if user is None:
|
||||
return None
|
||||
if user.api_key is None:
|
||||
return None
|
||||
elif password != user.api_key:
|
||||
return None
|
||||
else:
|
||||
return user
|
||||
|
||||
@staticmethod
|
||||
def try_auth(name, password):
|
||||
ldap_user = Ldap.try_auth(name, password)
|
||||
user = User.get_or_none(name=name)
|
||||
|
||||
if ldap_user is None:
|
||||
if user is None:
|
||||
return None
|
||||
elif UserManager.__encrypt_password(password, user.salt)[0] != user.password:
|
||||
return None
|
||||
else:
|
||||
return user
|
||||
elif ldap_user:
|
||||
if user is None:
|
||||
user = User.create(name=name, mail=ldap_user["mail"], ldap=True)
|
||||
return user
|
||||
elif not user.ldap:
|
||||
return None
|
||||
else:
|
||||
if user.mail != ldap_user["mail"]:
|
||||
user.mail = ldap_user["mail"]
|
||||
return user
|
||||
else:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def change_password(uid, old_pass, new_pass):
|
||||
|
@ -53,10 +53,12 @@ CREATE TABLE IF NOT EXISTS user (
|
||||
id CHAR(32) PRIMARY KEY,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
mail VARCHAR(256),
|
||||
password CHAR(40) NOT NULL,
|
||||
salt CHAR(6) NOT NULL,
|
||||
password CHAR(40),
|
||||
salt CHAR(6),
|
||||
ldap BOOLEAN NOT NULL,
|
||||
admin BOOLEAN NOT NULL,
|
||||
jukebox BOOLEAN NOT NULL,
|
||||
api_key CHAR(32),
|
||||
lastfm_session CHAR(32),
|
||||
lastfm_status BOOLEAN NOT NULL,
|
||||
last_play_id CHAR(32) REFERENCES track(id),
|
||||
|
@ -53,10 +53,12 @@ CREATE TABLE IF NOT EXISTS "user" (
|
||||
id UUID PRIMARY KEY,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
mail VARCHAR(256),
|
||||
password CHAR(40) NOT NULL,
|
||||
salt CHAR(6) NOT NULL,
|
||||
password CHAR(40),
|
||||
salt CHAR(6),
|
||||
ldap BOOLEAN NOT NULL,
|
||||
admin BOOLEAN NOT NULL,
|
||||
jukebox BOOLEAN NOT NULL,
|
||||
api_key CHAR(32),
|
||||
lastfm_session CHAR(32),
|
||||
lastfm_status BOOLEAN NOT NULL,
|
||||
last_play_id UUID REFERENCES track,
|
||||
|
@ -55,10 +55,12 @@ CREATE TABLE IF NOT EXISTS user (
|
||||
id CHAR(36) PRIMARY KEY,
|
||||
name VARCHAR(64) NOT NULL,
|
||||
mail VARCHAR(256),
|
||||
password CHAR(40) NOT NULL,
|
||||
salt CHAR(6) NOT NULL,
|
||||
password CHAR(40),
|
||||
salt CHAR(6),
|
||||
ldap BOOLEAN NOT NULL,
|
||||
admin BOOLEAN NOT NULL,
|
||||
jukebox BOOLEAN NOT NULL,
|
||||
api_key CHAR(32),
|
||||
lastfm_session CHAR(32),
|
||||
lastfm_status BOOLEAN NOT NULL,
|
||||
last_play_id CHAR(36) REFERENCES track,
|
||||
|
@ -32,7 +32,7 @@
|
||||
<label class="sr-only" for="mail">eMail</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon"><span class="glyphicon glyphicon-envelope"></span></div>
|
||||
<input type="text" class="form-control" id="mail" name="mail" value="{{ request.form.mail or user.mail }}" placeholder="eMail" />
|
||||
<input type="text" class="form-control" id="mail" name="mail" value="{{ user.mail or "" }}" placeholder="eMail" />
|
||||
</div>
|
||||
</div>
|
||||
<input type="submit" class="btn btn-default" />
|
||||
|
@ -25,7 +25,9 @@
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="page-header first-header">
|
||||
<h2>{{ user.name }}{% if user.admin %} <small><span class="glyphicon
|
||||
<h2>{{ user.name }}{% if user.ldap %} <small><span class="glyphicon
|
||||
glyphicon-cloud" data-toggle="tooltip" data-placement="right" title="LDAP"></span></small>{% endif %}
|
||||
{% if user.admin %} <small><span class="glyphicon
|
||||
glyphicon-certificate" data-toggle="tooltip" data-placement="right"
|
||||
title="{% if request.user.id == user.id %}You're an admin!{% else %}The user is an admin!{% endif %}"></span></small>{% endif %}</h2>
|
||||
</div>
|
||||
@ -37,6 +39,7 @@
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">User eMail</div>
|
||||
<input type="text" class="form-control" id="email" placeholder="{{ user.mail }}" readonly>
|
||||
{% if not user.ldap %}
|
||||
<div class="input-group-btn">
|
||||
{% if request.user.id == user.id %}
|
||||
<a href="{{ url_for('frontend.change_mail_form', uid = 'me') }}" class="btn btn-default">Change eMail</a>
|
||||
@ -44,6 +47,24 @@
|
||||
<a href="{{ url_for('frontend.change_mail_form', uid = user.id) }}" class="btn btn-default">Change eMail</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label class="sr-only" for="apikey">API key</label>
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">API key</div>
|
||||
<input type="text" class="form-control" id="apikey" {% if user.api_key %}value{% else %}placeholder{% endif %}="{{ user.api_key }}" readonly>
|
||||
<div class="input-group-btn">
|
||||
<a href="{{ url_for('frontend.new_api_key', uid = 'me') if request.user.id == user.id else url_for('frontend.new_api_key', uid = user.id) }}" class="btn btn-default">
|
||||
<span class="glyphicon glyphicon-refresh" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Generate key"></span></a>
|
||||
<a href="{{ url_for('frontend.del_api_key', uid = 'me') if request.user.id == user.id else url_for('frontend.del_api_key', uid = user.id) }}" class="btn btn-default{% if not user.api_key %} disabled{% endif %}">
|
||||
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete key"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@ -83,11 +104,15 @@
|
||||
</div>
|
||||
</div>
|
||||
{% if request.user.id == user.id %}
|
||||
{% if not user.ldap %}
|
||||
<a href="{{ url_for('frontend.change_password_form', uid = 'me') }}" class="btn btn-default">Change password</a></li>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<a href="{{ url_for('frontend.change_username_form', uid = user.id) }}" class="btn btn-default">Change username or admin status</a></li>
|
||||
{% if not user.ldap %}
|
||||
<a href="{{ url_for('frontend.change_password_form', uid = user.id) }}" class="btn btn-default">Change password</a></li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if clients.count() %}
|
||||
<div class="page-header">
|
||||
<h2>Clients</h2>
|
||||
|
@ -18,14 +18,14 @@
|
||||
</div>
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr><th>Name</th><th>EMail</th><th>Admin</th><th>Last play date</th><th></th></tr>
|
||||
<tr><th>Name</th><th>EMail</th><th>LDAP</th><th>Admin</th><th>Last play date</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td>{% if request.user.id == user.id %}{{ user.name }}{% else %}
|
||||
<a href="{{ url_for('frontend.user_profile', uid = user.id) }}">{{ user.name }}</a>{% endif %}</td>
|
||||
<td>{{ user.mail }}</td><td>{{ user.admin }}</td><td>{{ user.last_play_date }}</td><td>
|
||||
<td>{{ user.mail }}</td><td>{{ user.ldap }}</td><td>{{ user.admin }}</td><td>{{ user.last_play_date }}</td><td>
|
||||
{% if request.user.id != user.id %}<button class="btn btn-danger btn-xs" data-href="{{ url_for('frontend.del_user', uid = user.id) }}" data-toggle="modal" data-target="#confirm-delete" aria-label="Delete user">
|
||||
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true" data-toggle="tooltip" data-placement="top" title="Delete user"></span></button>{% endif %}</td></tr>
|
||||
{% endfor %}
|
||||
|
Loading…
Reference in New Issue
Block a user