forked from Github-Mirrors/canaille
Merge branch 'issue-12-groups' into 'master'
Create, edit and delete groups from interface (moderators only) See merge request yaal/canaille!6
This commit is contained in:
commit
f4d6e723ba
41 changed files with 768 additions and 284 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -19,3 +19,4 @@ canaille/conf/openid-configuration.json
|
|||
canaille/conf/*.pem
|
||||
canaille/conf/*.pub
|
||||
canaille/conf/*.key
|
||||
.vscode
|
|
@ -12,6 +12,7 @@ import canaille.consents
|
|||
import canaille.commands.clean
|
||||
import canaille.oauth
|
||||
import canaille.account
|
||||
import canaille.groups
|
||||
import canaille.well_known
|
||||
|
||||
from cryptography.hazmat.primitives import serialization as crypto_serialization
|
||||
|
@ -136,6 +137,7 @@ def setup_app(app):
|
|||
config_oauth(app)
|
||||
setup_ldap_tree(app)
|
||||
app.register_blueprint(canaille.account.bp)
|
||||
app.register_blueprint(canaille.groups.bp, url_prefix="/groups")
|
||||
app.register_blueprint(canaille.oauth.bp, url_prefix="/oauth")
|
||||
app.register_blueprint(canaille.commands.clean.bp)
|
||||
app.register_blueprint(canaille.consents.bp, url_prefix="/consent")
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"https://mydomain.tld/oauth/register",
|
||||
"scopes_supported":
|
||||
["openid", "profile", "email", "address",
|
||||
"phone"],
|
||||
"phone", "groups"],
|
||||
"response_types_supported":
|
||||
["code", "token", "id_token", "code token",
|
||||
"code id_token", "token id_token"],
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"https://mydomain.tld/oauth/register",
|
||||
"scopes_supported":
|
||||
["openid", "profile", "email", "address",
|
||||
"phone"],
|
||||
"phone", "groups"],
|
||||
"response_types_supported":
|
||||
["code", "token", "id_token", "code token",
|
||||
"code id_token", "token id_token"],
|
||||
|
|
|
@ -145,3 +145,19 @@ def profile_form(field_names):
|
|||
render_kw={},
|
||||
)
|
||||
return wtforms.form.BaseForm(fields)
|
||||
|
||||
|
||||
class GroupForm(FlaskForm):
|
||||
name = wtforms.StringField(
|
||||
_("Name"),
|
||||
validators=[wtforms.validators.DataRequired()],
|
||||
render_kw={
|
||||
"placeholder": _("group"),
|
||||
},
|
||||
)
|
||||
|
||||
def validate_name(self, field):
|
||||
if Group.get(field.data):
|
||||
raise wtforms.ValidationError(
|
||||
_("The group '{group}' already exists").format(group=field.data)
|
||||
)
|
||||
|
|
89
canaille/groups.py
Normal file
89
canaille/groups.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
from flask import (
|
||||
Blueprint,
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
request,
|
||||
flash,
|
||||
current_app,
|
||||
abort,
|
||||
)
|
||||
from flask_babel import gettext as _
|
||||
|
||||
from .flaskutils import moderator_needed
|
||||
from .forms import GroupForm
|
||||
from .models import Group
|
||||
|
||||
bp = Blueprint("groups", __name__)
|
||||
|
||||
|
||||
@bp.route("/")
|
||||
@moderator_needed()
|
||||
def groups(user):
|
||||
groups = Group.filter(objectClass=current_app.config["LDAP"]["GROUP_CLASS"])
|
||||
return render_template("groups.html", groups=groups, menuitem="groups")
|
||||
|
||||
|
||||
@bp.route("/add", methods=("GET", "POST"))
|
||||
@moderator_needed()
|
||||
def create_group(user):
|
||||
form = GroupForm(request.form or None)
|
||||
try:
|
||||
if "name" in form:
|
||||
del form["name"].render_kw["disabled"]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if request.form:
|
||||
if not form.validate():
|
||||
flash(_("Group creation failed."), "error")
|
||||
else:
|
||||
group = Group(objectClass=current_app.config["LDAP"]["GROUP_CLASS"])
|
||||
group.member = [user.dn]
|
||||
group.cn = [form.name.data]
|
||||
group.save()
|
||||
flash(
|
||||
_("The group %(group)s has been sucessfully created", group=group.name),
|
||||
"success",
|
||||
)
|
||||
return redirect(url_for("groups.group", groupname=group.name))
|
||||
|
||||
return render_template("group.html", form=form, edited_group=None, members=None)
|
||||
|
||||
|
||||
@bp.route("/<groupname>", methods=("GET", "POST"))
|
||||
@moderator_needed()
|
||||
def group(user, groupname):
|
||||
group = Group.get(groupname) or abort(404)
|
||||
|
||||
if request.method == "GET" or request.form.get("action") == "edit":
|
||||
return edit_group(group)
|
||||
|
||||
if request.form.get("action") == "delete":
|
||||
return delete_group(group)
|
||||
|
||||
abort(400)
|
||||
|
||||
|
||||
def edit_group(group):
|
||||
form = GroupForm(request.form or None, data={"name": group.name})
|
||||
form["name"].render_kw["disabled"] = "true"
|
||||
|
||||
if request.form:
|
||||
if form.validate():
|
||||
group.save()
|
||||
else:
|
||||
flash(_("Group edition failed."), "error")
|
||||
|
||||
return render_template(
|
||||
"group.html", form=form, edited_group=group, members=group.get_members()
|
||||
)
|
||||
|
||||
|
||||
def delete_group(group):
|
||||
flash(
|
||||
_("The group %(group)s has been sucessfully deleted", group=group.name),
|
||||
"success",
|
||||
)
|
||||
group.delete()
|
||||
return redirect(url_for("groups.groups"))
|
|
@ -160,6 +160,11 @@ class Group(LDAPObject):
|
|||
Group.attr_type_by_name(conn=conn)
|
||||
return [(group[attribute][0], group.dn) for group in groups]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
attribute = current_app.config["LDAP"].get("GROUP_NAME_ATTRIBUTE")
|
||||
return self[attribute][0]
|
||||
|
||||
def get_members(self, conn=None):
|
||||
return [User.get(dn=user_dn, conn=conn) for user_dn in self.member]
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ CLAIMS = {
|
|||
"email": ("at", _("Your email address.")),
|
||||
"address": ("envelope open outline", _("Your postal address.")),
|
||||
"phone": ("phone", _("Your phone number.")),
|
||||
"groups": ("users", _("Groups you are belonging to")),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -61,6 +61,8 @@ def generate_user_info(user, scope):
|
|||
fields += ["address"]
|
||||
if "phone" in scope:
|
||||
fields += ["phone_number", "phone_number_verified"]
|
||||
if "groups" in scope:
|
||||
fields += ["groups"]
|
||||
|
||||
data = {}
|
||||
for field in fields:
|
||||
|
@ -69,6 +71,9 @@ def generate_user_info(user, scope):
|
|||
data[field] = user.__getattr__(ldap_field_match)
|
||||
if isinstance(data[field], list):
|
||||
data[field] = data[field][0]
|
||||
if field == "groups":
|
||||
group_name_attr = current_app.config["LDAP"]["GROUP_NAME_ATTRIBUTE"]
|
||||
data[field] = [getattr(g, group_name_attr)[0] for g in user.groups]
|
||||
|
||||
return UserInfo(**data)
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block content %}
|
||||
<div class="ui clearing segment">
|
||||
|
@ -8,7 +7,6 @@
|
|||
<img class="ui tiny centered image" src="/static/img/canaille-head.png" alt="{{ website_name }}">
|
||||
</a>
|
||||
|
||||
{{ flask.messages() }}
|
||||
<h2 class="ui center aligned header">
|
||||
<div class="content">
|
||||
{{ _("About canaille") }}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block content %}
|
||||
<div class="loginform">
|
||||
|
@ -8,8 +7,6 @@
|
|||
{% trans %}View a authorization{% endtrans %}
|
||||
</h3>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<div class="ui attached clearing segment">
|
||||
<ul>
|
||||
{% for attr in authorization.may %}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block content %}
|
||||
<div class="loginform">
|
||||
|
@ -8,8 +7,6 @@
|
|||
{% trans %}Add a client{% endtrans %}
|
||||
</h3>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<div class="ui attached clearing segment">
|
||||
{{ sui.render_form(form, _("Confirm")) }}
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block script %}
|
||||
<script src="/static/js/admin/client_edit.js"></script>
|
||||
|
@ -26,8 +25,6 @@
|
|||
{% trans %}Edit a client{% endtrans %}
|
||||
</h3>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<div class="ui attached clearing segment">
|
||||
<div class="ui form">
|
||||
<form id="readonly">
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block style %}
|
||||
<link href="/static/datatables/jquery.dataTables.min.css" rel="stylesheet">
|
||||
|
@ -14,8 +13,6 @@
|
|||
|
||||
{% block content %}
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<div class="ui segment">
|
||||
<a class="ui primary button" href="{{ url_for('admin_clients.add') }}">{% trans %}Add client{% endtrans %}</a>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block content %}
|
||||
<div class="loginform">
|
||||
|
@ -8,8 +7,6 @@
|
|||
{% trans %}View a token{% endtrans %}
|
||||
</h3>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<div class="ui attached clearing segment">
|
||||
<ul>
|
||||
{% for attr in token.may %}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
{% import 'flask.j2' as flask %}
|
||||
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
|
@ -47,6 +49,11 @@
|
|||
<i class="users icon"></i>
|
||||
{% trans %}Users{% endtrans %}
|
||||
</a>
|
||||
<a class="item {% if menuitem == "groups" %}active{% endif %}"
|
||||
href="{{ url_for('groups.groups') }}">
|
||||
<i class="users cog icon"></i>
|
||||
{% trans %}Groups{% endtrans %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if user.admin %}
|
||||
<div class="ui dropdown item {% if menuitem == "admin" %}active{% endif %}">
|
||||
|
@ -82,6 +89,7 @@
|
|||
|
||||
<div class="ui container">
|
||||
<div class="content">
|
||||
{{ flask.messages() }}
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block style %}
|
||||
<link href="/static/datatables/jquery.dataTables.min.css" rel="stylesheet">
|
||||
|
@ -23,8 +22,6 @@
|
|||
</div>
|
||||
</h2>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
{% if consents %}
|
||||
<div class="ui centered cards">
|
||||
{% for consent in consents %}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block content %}
|
||||
<div class="loginform">
|
||||
|
@ -13,8 +12,6 @@
|
|||
</div>
|
||||
</h3>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<div class="ui attached message">
|
||||
{% trans %}
|
||||
It seems this is the first time you are logging here. In order to finalize your
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block content %}
|
||||
<div class="loginform">
|
||||
|
@ -13,8 +12,6 @@
|
|||
</div>
|
||||
</h3>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<div class="ui attached message">
|
||||
{% trans %}
|
||||
After this form is sent, if the email address or the login you provided
|
||||
|
|
87
canaille/templates/group.html
Normal file
87
canaille/templates/group.html
Normal file
|
@ -0,0 +1,87 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
|
||||
{% block script %}
|
||||
<script src="/static/js/confirm.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if edited_group %}
|
||||
<div class="ui basic modal">
|
||||
<div class="ui icon header">
|
||||
<i class="trash alternate icon"></i>
|
||||
{% trans %}Group deletion{% endtrans %}
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>
|
||||
{% trans %}Are you sure you want to delete this group? This action is unrevokable and all the data about this group will be removed.{% endtrans %}
|
||||
</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui inverted cancel button">{% trans %}Cancel{% endtrans %}</div>
|
||||
<div class="ui inverted red approve button">{% trans %}Delete{% endtrans %}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if edited_group %}
|
||||
<div class="ui segment">
|
||||
<h2 class="ui header">{% trans %}Members{% endtrans %}</h2>
|
||||
<ul>
|
||||
{% for member in members %}
|
||||
<div class="ui left icon">
|
||||
<i class="user icon"></i>
|
||||
<a href="{{ url_for('account.profile_edition', username=member.uid[0]) }}">{{ member.name }}</a></li>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="ui clearing segment">
|
||||
<h2 class="ui center aligned header">
|
||||
<div class="content">
|
||||
{% if not edited_group %}
|
||||
{% trans %}Group creation{% endtrans %}
|
||||
{% else %}
|
||||
{% trans %}Group edition{% endtrans %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="sub header">
|
||||
{% if not edited_group %}
|
||||
{% trans %}Create a new group{% endtrans %}
|
||||
{% else %}
|
||||
{% trans %}Edit informations about a group{% endtrans %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</h2>
|
||||
|
||||
<form method="POST"
|
||||
id="{{ form.__class__.__name__|lower }}"
|
||||
action="{{ request.url }}"
|
||||
role="form"
|
||||
class="ui form"
|
||||
>
|
||||
{{ form.hidden_tag() if form.hidden_tag }}
|
||||
{{ sui.render_field(form.name) }}
|
||||
{% if not edited_group %}
|
||||
<p>
|
||||
{% trans %}Because group cannot be empty, you will be added to the group. You can remove you later by editing your profile when you will have added other members to the group.{% endtrans %}
|
||||
</p>
|
||||
{% endif %}
|
||||
<button type="submit" class="ui right floated primary button" name="action" value="edit">
|
||||
{% if not edited_group %}
|
||||
{% trans %}Create group{% endtrans %}
|
||||
{% else %}
|
||||
{% trans %}Submit{% endtrans %}
|
||||
{% endif %}
|
||||
</button>
|
||||
{% if edited_group %}
|
||||
<button type="submit" class="ui right floated basic negative button confirm" name="action" value="delete" id="delete">
|
||||
{% trans %}Delete group{% endtrans %}
|
||||
</button>
|
||||
{% endif %}
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
19
canaille/templates/groups.html
Normal file
19
canaille/templates/groups.html
Normal file
|
@ -0,0 +1,19 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div class="ui segment">
|
||||
<a class="ui primary button" href="{{ url_for('groups.create_group') }}">{% trans %}Add a group{% endtrans %}</a>
|
||||
</div>
|
||||
|
||||
<div class="ui segment">
|
||||
<h2 class="ui header">{% trans %}Groups{% endtrans %}</h2>
|
||||
<ul>
|
||||
{% for group in groups %}
|
||||
<div class="ui left icon">
|
||||
<i class="users icon"></i>
|
||||
<a href="{{ url_for('groups.group', groupname=group.name) }}">{{ group.name }}</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block content %}
|
||||
<div class="ui clearing segment">
|
||||
|
@ -19,8 +18,6 @@
|
|||
<div class="sub header">{% trans %}Log-in and manage your authorizations.{% endtrans %}</div>
|
||||
</h2>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<form method="POST"
|
||||
id="{{ form.id or form.__class__.__name__|lower }}"
|
||||
action="{{ request.url }}"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block content %}
|
||||
<div class="ui clearing segment">
|
||||
|
@ -19,8 +18,6 @@
|
|||
<div class="sub header">{% trans %}Please enter your password for this account.{% endtrans %}</div>
|
||||
</h2>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<form method="POST"
|
||||
id="{{ form.id or form.__class__.__name__|lower }}"
|
||||
action="{{ request.url }}"
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block script %}
|
||||
<script src="/static/js/profile.js"></script>
|
||||
<script src="/static/js/confirm.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -52,8 +51,6 @@
|
|||
</div>
|
||||
</h2>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<form method="POST"
|
||||
id="{{ form.__class__.__name__|lower }}"
|
||||
action="{{ request.url }}"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% extends 'base.html' %}
|
||||
{% import 'fomanticui.j2' as sui %}
|
||||
{% import 'flask.j2' as flask %}
|
||||
|
||||
{% block content %}
|
||||
<div class="loginform">
|
||||
|
@ -13,8 +12,6 @@
|
|||
</div>
|
||||
</h3>
|
||||
|
||||
{{ flask.messages() }}
|
||||
|
||||
<div class="ui attached clearing segment">
|
||||
{{ sui.render_form(form, _("Password reset"), action=url_for("account.reset", uid=uid, hash=hash)) }}
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="ui segment">
|
||||
<a class="ui primary button" href="{{ url_for('account.profile_creation') }}">{% trans %}Add a user{% endtrans %}</a>
|
||||
</div>
|
||||
|
|
Binary file not shown.
|
@ -3,24 +3,25 @@
|
|||
# This file is distributed under the same license as the PROJECT project.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2020.
|
||||
# Éloi Rivard <eloi.rivard@aquilenet.fr>, 2020-2021.
|
||||
# Camille <camille@yaal.coop>, 2021.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: contact@yaal.fr\n"
|
||||
"POT-Creation-Date: 2021-06-03 15:21+0200\n"
|
||||
"PO-Revision-Date: 2021-06-03 15:22+0200\n"
|
||||
"Last-Translator: Éloi Rivard <eloi.rivard@aquilenet.fr>\n"
|
||||
"POT-Creation-Date: 2021-07-29 16:09+0200\n"
|
||||
"PO-Revision-Date: 2021-07-29 16:31+0200\n"
|
||||
"Last-Translator: Camille <camille@yaal.coop>\n"
|
||||
"Language: fr\n"
|
||||
"Language-Team: French <traduc@traduc.org>\n"
|
||||
"Language-Team: French <contact@yaal.coop>\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
"X-Generator: Gtranslator 40.0\n"
|
||||
"X-Generator: Gtranslator 3.38.0\n"
|
||||
|
||||
#: canaille/account.py:62 canaille/account.py:87 canaille/oauth.py:58
|
||||
#: canaille/account.py:62 canaille/account.py:87 canaille/oauth.py:59
|
||||
msgid "Login failed, please check your information"
|
||||
msgstr "La connexion a échoué, veuillez vérifier vos informations."
|
||||
|
||||
|
@ -155,9 +156,9 @@ msgstr "Identifiant"
|
|||
msgid "jdoe"
|
||||
msgstr "mdupont"
|
||||
|
||||
#: canaille/admin/clients.py:23 canaille/forms.py:82
|
||||
#: canaille/templates/admin/client_list.html:25
|
||||
#: canaille/templates/users.html:22
|
||||
#: canaille/admin/clients.py:23 canaille/forms.py:82 canaille/forms.py:152
|
||||
#: canaille/templates/admin/client_list.html:22
|
||||
#: canaille/templates/users.html:21
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
|
@ -181,7 +182,7 @@ msgstr "Dupont"
|
|||
msgid "Email address"
|
||||
msgstr "Courriel"
|
||||
|
||||
#: canaille/forms.py:109 canaille/templates/users.html:24
|
||||
#: canaille/forms.py:109 canaille/templates/users.html:23
|
||||
msgid "Phone number"
|
||||
msgstr "Numéro de téléphone"
|
||||
|
||||
|
@ -201,10 +202,37 @@ msgstr "Numéro"
|
|||
msgid "1234"
|
||||
msgstr "1234"
|
||||
|
||||
#: canaille/forms.py:143
|
||||
#: canaille/forms.py:143 canaille/templates/base.html:55
|
||||
#: canaille/templates/groups.html:9
|
||||
msgid "Groups"
|
||||
msgstr "Groupes"
|
||||
|
||||
#: canaille/forms.py:155
|
||||
msgid "group"
|
||||
msgstr "groupe"
|
||||
|
||||
#: canaille/forms.py:162
|
||||
msgid "The group '{group}' already exists"
|
||||
msgstr "Le group '{group}' existe déjà"
|
||||
|
||||
#: canaille/groups.py:39
|
||||
msgid "Group creation failed."
|
||||
msgstr "La création du groupe a échoué."
|
||||
|
||||
#: canaille/groups.py:46
|
||||
#, python-format
|
||||
msgid "The group %(group)s has been sucessfully created"
|
||||
msgstr "Le groupe %(group)s a bien été créé"
|
||||
|
||||
#: canaille/groups.py:76
|
||||
msgid "Group edition failed."
|
||||
msgstr "L'édition du groupe a échoué."
|
||||
|
||||
#: canaille/groups.py:85
|
||||
#, python-format
|
||||
msgid "The group %(group)s has been sucessfully deleted"
|
||||
msgstr "Le groupe %(group)s a bien été supprimé"
|
||||
|
||||
#: canaille/admin/mail.py:27 canaille/mails.py:27
|
||||
msgid "Password reset on {website_name}"
|
||||
msgstr "Réinitialisation du mot de passe sur {website_name}"
|
||||
|
@ -229,7 +257,11 @@ msgstr "Votre adresse postale."
|
|||
msgid "Your phone number."
|
||||
msgstr "Votre numéro de téléphone."
|
||||
|
||||
#: canaille/oauth.py:97
|
||||
#: canaille/oauth.py:30
|
||||
msgid "Groups you are belonging to"
|
||||
msgstr "Les groupes dans lesquels vous êtes"
|
||||
|
||||
#: canaille/oauth.py:98
|
||||
msgid "You have been successfully logged out."
|
||||
msgstr "Vous avez été déconnectés."
|
||||
|
||||
|
@ -309,24 +341,24 @@ msgstr "Le client a été édité."
|
|||
msgid "The client has been deleted."
|
||||
msgstr "Le client a été supprimé."
|
||||
|
||||
#: canaille/templates/about.html:14 canaille/templates/base.html:90
|
||||
#: canaille/templates/about.html:12 canaille/templates/base.html:98
|
||||
msgid "About canaille"
|
||||
msgstr "À propos de canaille"
|
||||
|
||||
#: canaille/templates/about.html:16
|
||||
#: canaille/templates/about.html:14
|
||||
msgid "Free and open-source identity provider."
|
||||
msgstr "Fournisseur d'identité numérique libre"
|
||||
|
||||
#: canaille/templates/about.html:19
|
||||
#: canaille/templates/about.html:17
|
||||
#, python-format
|
||||
msgid "Version %(version)s"
|
||||
msgstr "Version %(version)s"
|
||||
|
||||
#: canaille/templates/about.html:20
|
||||
#: canaille/templates/about.html:18
|
||||
msgid "Source code"
|
||||
msgstr "Code source"
|
||||
|
||||
#: canaille/templates/about.html:21
|
||||
#: canaille/templates/about.html:19
|
||||
msgid "Documentation"
|
||||
msgstr "Documentation"
|
||||
|
||||
|
@ -352,67 +384,67 @@ msgstr "Changer d'utilisateur"
|
|||
msgid "Accept"
|
||||
msgstr "Accepter"
|
||||
|
||||
#: canaille/templates/base.html:8
|
||||
#: canaille/templates/base.html:10
|
||||
msgid "authorization interface"
|
||||
msgstr " - Interface de gestion des autorisations"
|
||||
|
||||
#: canaille/templates/base.html:37 canaille/templates/profile.html:38
|
||||
#: canaille/templates/base.html:39 canaille/templates/profile.html:37
|
||||
msgid "My profile"
|
||||
msgstr "Mon profil"
|
||||
|
||||
#: canaille/templates/base.html:42 canaille/templates/consent_list.html:19
|
||||
#: canaille/templates/base.html:44 canaille/templates/consent_list.html:18
|
||||
msgid "My consents"
|
||||
msgstr "Mes autorisations"
|
||||
|
||||
#: canaille/templates/base.html:48
|
||||
#: canaille/templates/base.html:50
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#: canaille/templates/base.html:58
|
||||
#: canaille/templates/base.html:65
|
||||
msgid "Clients"
|
||||
msgstr "Clients"
|
||||
|
||||
#: canaille/templates/base.html:62
|
||||
#: canaille/templates/base.html:69
|
||||
msgid "Tokens"
|
||||
msgstr "Jetons"
|
||||
|
||||
#: canaille/templates/base.html:66
|
||||
#: canaille/templates/base.html:73
|
||||
msgid "Codes"
|
||||
msgstr "Codes"
|
||||
|
||||
#: canaille/templates/base.html:70
|
||||
#: canaille/templates/base.html:77
|
||||
msgid "Consents"
|
||||
msgstr "Autorisations"
|
||||
|
||||
#: canaille/templates/base.html:77
|
||||
#: canaille/templates/base.html:84
|
||||
msgid "Log out"
|
||||
msgstr "Déconnexion"
|
||||
|
||||
#: canaille/templates/consent_list.html:22
|
||||
#: canaille/templates/consent_list.html:21
|
||||
msgid "Consult and revoke the authorization you gave to websites."
|
||||
msgstr "Consultez et révoquez les autorisation que vous avez données."
|
||||
|
||||
#: canaille/templates/consent_list.html:42
|
||||
#: canaille/templates/consent_list.html:39
|
||||
msgid "From:"
|
||||
msgstr "À partir de :"
|
||||
|
||||
#: canaille/templates/consent_list.html:44
|
||||
#: canaille/templates/consent_list.html:41
|
||||
msgid "Revoked:"
|
||||
msgstr "Révoqué le :"
|
||||
|
||||
#: canaille/templates/consent_list.html:47
|
||||
#: canaille/templates/consent_list.html:44
|
||||
msgid "Has access to:"
|
||||
msgstr "A accès à :"
|
||||
|
||||
#: canaille/templates/consent_list.html:57
|
||||
#: canaille/templates/consent_list.html:54
|
||||
msgid "Remove access"
|
||||
msgstr "Supprimer l'accès"
|
||||
|
||||
#: canaille/templates/consent_list.html:67
|
||||
#: canaille/templates/consent_list.html:64
|
||||
msgid "Nothing here"
|
||||
msgstr "Rien ici"
|
||||
|
||||
#: canaille/templates/consent_list.html:68
|
||||
#: canaille/templates/consent_list.html:65
|
||||
msgid "You did not authorize applications yet."
|
||||
msgstr ""
|
||||
"Vous n'avez pas encore autorisé d'application à accéder à votre profil."
|
||||
|
@ -433,11 +465,11 @@ msgstr "Page non trouvée"
|
|||
msgid "Technical problem"
|
||||
msgstr "Problème technique"
|
||||
|
||||
#: canaille/templates/firstlogin.html:12
|
||||
#: canaille/templates/firstlogin.html:11
|
||||
msgid "First login"
|
||||
msgstr "Première connexion"
|
||||
|
||||
#: canaille/templates/firstlogin.html:19
|
||||
#: canaille/templates/firstlogin.html:16
|
||||
msgid ""
|
||||
"\n"
|
||||
" It seems this is the first time you are logging here. In order to "
|
||||
|
@ -459,21 +491,21 @@ msgstr ""
|
|||
" Veuillez cliquer sur le bouton bleu ci-dessous pour envoyer le "
|
||||
"courriel."
|
||||
|
||||
#: canaille/templates/firstlogin.html:35
|
||||
#: canaille/templates/firstlogin.html:32
|
||||
msgid "Send the initialization email"
|
||||
msgstr "Envoyer le courriel d'initialisation"
|
||||
|
||||
#: canaille/templates/firstlogin.html:36
|
||||
#: canaille/templates/forgotten-password.html:43
|
||||
#: canaille/templates/firstlogin.html:33
|
||||
#: canaille/templates/forgotten-password.html:40
|
||||
msgid "Login page"
|
||||
msgstr "Page de connexion"
|
||||
|
||||
#: canaille/templates/forgotten-password.html:12
|
||||
#: canaille/templates/login.html:38 canaille/templates/password.html:35
|
||||
#: canaille/templates/forgotten-password.html:11
|
||||
#: canaille/templates/login.html:35 canaille/templates/password.html:32
|
||||
msgid "Forgotten password"
|
||||
msgstr "Mot de passe oublié"
|
||||
|
||||
#: canaille/templates/forgotten-password.html:19
|
||||
#: canaille/templates/forgotten-password.html:16
|
||||
msgid ""
|
||||
"\n"
|
||||
" After this form is sent, if the email address or the login you "
|
||||
|
@ -490,51 +522,119 @@ msgstr ""
|
|||
" vous permettra de ré-initialiser votre mot de passe.\n"
|
||||
" "
|
||||
|
||||
#: canaille/templates/forgotten-password.html:38
|
||||
#: canaille/templates/profile.html:120 canaille/templates/profile.html:143
|
||||
#: canaille/templates/forgotten-password.html:35
|
||||
#: canaille/templates/profile.html:117 canaille/templates/profile.html:140
|
||||
msgid "Send again"
|
||||
msgstr "Envoyer à nouveau"
|
||||
|
||||
#: canaille/templates/forgotten-password.html:40
|
||||
#: canaille/templates/forgotten-password.html:37
|
||||
msgid "Send"
|
||||
msgstr "Envoyer"
|
||||
|
||||
#: canaille/templates/login.html:17
|
||||
#: canaille/templates/group.html:13
|
||||
msgid "Group deletion"
|
||||
msgstr "Suppression d'un groupe"
|
||||
|
||||
#: canaille/templates/group.html:17
|
||||
msgid ""
|
||||
"Are you sure you want to delete this group? This action is unrevokable and "
|
||||
"all the data about this group will be removed."
|
||||
msgstr ""
|
||||
"Êtes-vous sûrs de vouloir supprimer ce groupe ? Cette action est "
|
||||
"irrévocable, et toutes les données de cet utilisateur seront supprimées."
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:18
|
||||
#: canaille/templates/group.html:21 canaille/templates/profile.html:25
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:19
|
||||
#: canaille/templates/group.html:22 canaille/templates/profile.html:26
|
||||
msgid "Delete"
|
||||
msgstr "Supprimer"
|
||||
|
||||
#: canaille/templates/group.html:29
|
||||
msgid "Members"
|
||||
msgstr "Membres"
|
||||
|
||||
#: canaille/templates/group.html:45
|
||||
msgid "Group creation"
|
||||
msgstr "Nouveau groupe"
|
||||
|
||||
#: canaille/templates/group.html:47
|
||||
msgid "Group edition"
|
||||
msgstr "Édition d'un groupe"
|
||||
|
||||
#: canaille/templates/group.html:53
|
||||
msgid "Create a new group"
|
||||
msgstr "Création d'un nouveau groupe"
|
||||
|
||||
#: canaille/templates/group.html:55
|
||||
msgid "Edit informations about a group"
|
||||
msgstr "Éditez les informations d'un groupe"
|
||||
|
||||
#: canaille/templates/group.html:70
|
||||
msgid ""
|
||||
"Because group cannot be empty, you will be added to the group. You can "
|
||||
"remove you later by editing your profile when you will have added other "
|
||||
"members to the group."
|
||||
msgstr ""
|
||||
"Vous serez ajouté au groupe car le groupe ne peut pas être vide. Vous "
|
||||
"pourrez vous retirer du groupe depuis l'édition de votre profil dès que vous "
|
||||
"aurez ajouter d'autres membres dans le groupe."
|
||||
|
||||
#: canaille/templates/group.html:75
|
||||
msgid "Create group"
|
||||
msgstr "Créer le groupe"
|
||||
|
||||
#: canaille/templates/group.html:77 canaille/templates/profile.html:154
|
||||
msgid "Submit"
|
||||
msgstr "Valider"
|
||||
|
||||
#: canaille/templates/group.html:82
|
||||
msgid "Delete group"
|
||||
msgstr "Supprimer le groupe"
|
||||
|
||||
#: canaille/templates/groups.html:5
|
||||
msgid "Add a group"
|
||||
msgstr "Ajouter un groupe"
|
||||
|
||||
#: canaille/templates/login.html:16
|
||||
#, python-format
|
||||
msgid "Sign in at %(website)s"
|
||||
msgstr "Connexion à %(website)s"
|
||||
|
||||
#: canaille/templates/login.html:19
|
||||
#: canaille/templates/login.html:18
|
||||
msgid "Log-in and manage your authorizations."
|
||||
msgstr "Connectez-vous et gérez vos autorisations."
|
||||
|
||||
#: canaille/templates/login.html:37
|
||||
#: canaille/templates/login.html:34
|
||||
msgid "Continue"
|
||||
msgstr "Continuer"
|
||||
|
||||
#: canaille/templates/password.html:17
|
||||
#: canaille/templates/password.html:16
|
||||
#, python-format
|
||||
msgid "Sign in as %(username)s"
|
||||
msgstr "Connexion en tant que %(username)s"
|
||||
|
||||
#: canaille/templates/password.html:19
|
||||
#: canaille/templates/password.html:18
|
||||
msgid "Please enter your password for this account."
|
||||
msgstr "Veuillez entre votre mot de passe pour ce compte."
|
||||
|
||||
#: canaille/templates/password.html:34
|
||||
#: canaille/templates/password.html:31
|
||||
msgid "Sign in"
|
||||
msgstr "Se connecter"
|
||||
|
||||
#: canaille/templates/password.html:36
|
||||
#: canaille/templates/password.html:33
|
||||
#, python-format
|
||||
msgid "I am not %(username)s"
|
||||
msgstr "Je ne suis pas %(username)s"
|
||||
|
||||
#: canaille/templates/profile.html:14
|
||||
#: canaille/templates/profile.html:13
|
||||
msgid "Account deletion"
|
||||
msgstr "Suppression d'un compte"
|
||||
|
||||
#: canaille/templates/profile.html:19
|
||||
#: canaille/templates/profile.html:18
|
||||
msgid ""
|
||||
"Are you sure you want to delete this user? This action is unrevokable and "
|
||||
"all the data about this user will be removed."
|
||||
|
@ -542,7 +642,7 @@ msgstr ""
|
|||
"Êtes-vous sûrs de vouloir supprimer cet utilisateur ? Cette action est "
|
||||
"irrévocable, et toutes les données de cet utilisateur seront supprimées."
|
||||
|
||||
#: canaille/templates/profile.html:21
|
||||
#: canaille/templates/profile.html:20
|
||||
msgid ""
|
||||
"Are you sure you want to delete your account? This action is unrevokable and "
|
||||
"all your data will be removed forever."
|
||||
|
@ -550,57 +650,47 @@ msgstr ""
|
|||
"Êtes-vous sûrs de vouloir supprimer votre compte ? Cette action est "
|
||||
"irrévocable et toutes vos données seront supprimées pour toujours."
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:19
|
||||
#: canaille/templates/profile.html:26
|
||||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:20
|
||||
#: canaille/templates/profile.html:27
|
||||
msgid "Delete"
|
||||
msgstr "Supprimer"
|
||||
|
||||
#: canaille/templates/profile.html:36
|
||||
#: canaille/templates/profile.html:35
|
||||
msgid "User creation"
|
||||
msgstr "Nouvel utilisateur"
|
||||
|
||||
#: canaille/templates/profile.html:40
|
||||
#: canaille/templates/profile.html:39
|
||||
msgid "User profile edition"
|
||||
msgstr "Édition d'un profil utilisateur"
|
||||
|
||||
#: canaille/templates/profile.html:46
|
||||
#: canaille/templates/profile.html:45
|
||||
msgid "Create a new user account"
|
||||
msgstr "Création d'un nouveau compte utilisateur"
|
||||
|
||||
#: canaille/templates/profile.html:48
|
||||
#: canaille/templates/profile.html:47
|
||||
msgid "Edit your personal informations"
|
||||
msgstr "Éditez vos informations personnelles"
|
||||
|
||||
#: canaille/templates/profile.html:50
|
||||
#: canaille/templates/profile.html:49
|
||||
msgid "Edit informations about an user"
|
||||
msgstr "Éditez les informations d'un utilisateur"
|
||||
|
||||
#: canaille/templates/profile.html:67
|
||||
#: canaille/templates/profile.html:64
|
||||
msgid "Personal information"
|
||||
msgstr "Informations personnelles"
|
||||
|
||||
#: canaille/templates/profile.html:89
|
||||
#: canaille/templates/profile.html:86
|
||||
msgid "Account information"
|
||||
msgstr "Informations sur le compte"
|
||||
|
||||
#: canaille/templates/profile.html:105
|
||||
#: canaille/templates/profile.html:102
|
||||
msgid "User password is not mandatory"
|
||||
msgstr "Le mot de passe utilisateur n'est pas requis à la création"
|
||||
|
||||
#: canaille/templates/profile.html:107
|
||||
#: canaille/templates/profile.html:104
|
||||
msgid "The user password can be set:"
|
||||
msgstr "Il pourra être renseigné :"
|
||||
|
||||
#: canaille/templates/profile.html:109
|
||||
#: canaille/templates/profile.html:106
|
||||
msgid "by filling this form;"
|
||||
msgstr "en remplissant ce formulaire ;"
|
||||
|
||||
#: canaille/templates/profile.html:110
|
||||
#: canaille/templates/profile.html:107
|
||||
msgid ""
|
||||
"by sending the user a password initialization mail, after the account "
|
||||
"creation;"
|
||||
|
@ -608,7 +698,7 @@ msgstr ""
|
|||
"en envoyant un lien d'initialisation de mot de passe, par mail à "
|
||||
"l'utilisateur, après la création de son compte;"
|
||||
|
||||
#: canaille/templates/profile.html:111 canaille/templates/profile.html:134
|
||||
#: canaille/templates/profile.html:108 canaille/templates/profile.html:131
|
||||
msgid ""
|
||||
"or simply waiting for the user to sign-in a first time, and then receive a "
|
||||
"password initialization mail."
|
||||
|
@ -616,40 +706,40 @@ msgstr ""
|
|||
"ou simplement en attendant la première connexion de l'utilisateur, afin "
|
||||
"qu'il reçoive un lien d'initialisation de mot de passe par email."
|
||||
|
||||
#: canaille/templates/profile.html:124
|
||||
#: canaille/templates/profile.html:121
|
||||
msgid "Send email"
|
||||
msgstr "Envoyer l'email"
|
||||
|
||||
#: canaille/templates/profile.html:128
|
||||
#: canaille/templates/profile.html:125
|
||||
msgid "This user does not have a password yet"
|
||||
msgstr "L'utilisateur n'a pas encore de mot de passe"
|
||||
|
||||
#: canaille/templates/profile.html:130
|
||||
#: canaille/templates/profile.html:127
|
||||
msgid "You can solve this by:"
|
||||
msgstr "Vous pouvez régler ceci en :"
|
||||
|
||||
#: canaille/templates/profile.html:132
|
||||
#: canaille/templates/profile.html:129
|
||||
msgid "setting a password using this form;"
|
||||
msgstr "renseignant un mot de passe via ce formulaire ;"
|
||||
|
||||
#: canaille/templates/profile.html:133
|
||||
#: canaille/templates/profile.html:130
|
||||
msgid ""
|
||||
"sending the user a password initialization mail, by clicking this button;"
|
||||
msgstr ""
|
||||
"envoyant un lien d'initialisation de mot de passe, par mail à l'utilisateur, "
|
||||
"en cliquant sur ce bouton;"
|
||||
|
||||
#: canaille/templates/profile.html:145
|
||||
#: canaille/templates/profile.html:142
|
||||
msgid "Send mail"
|
||||
msgstr "Envoyer l'email"
|
||||
|
||||
#: canaille/templates/profile.html:149
|
||||
#: canaille/templates/reset-password.html:12
|
||||
#: canaille/templates/reset-password.html:19
|
||||
#: canaille/templates/profile.html:146
|
||||
#: canaille/templates/reset-password.html:11
|
||||
#: canaille/templates/reset-password.html:16
|
||||
msgid "Password reset"
|
||||
msgstr "Réinitialisation du mot de passe"
|
||||
|
||||
#: canaille/templates/profile.html:151
|
||||
#: canaille/templates/profile.html:148
|
||||
msgid ""
|
||||
"If the user has forgotten his password, you can send him a password reset "
|
||||
"email by clicking this button."
|
||||
|
@ -657,27 +747,23 @@ msgstr ""
|
|||
"Si l'utilisateur a oublié son mot de passe, vous pouvez lui envoyer un email "
|
||||
"contenant un lien de réinitilisation en cliquant sur ce bouton."
|
||||
|
||||
#: canaille/templates/profile.html:157
|
||||
msgid "Submit"
|
||||
msgstr "Valider"
|
||||
|
||||
#: canaille/templates/profile.html:161
|
||||
#: canaille/templates/profile.html:158
|
||||
msgid "Impersonate"
|
||||
msgstr "Prendre l'identité"
|
||||
|
||||
#: canaille/templates/profile.html:167
|
||||
#: canaille/templates/profile.html:164
|
||||
msgid "Delete the user"
|
||||
msgstr "Supprimer l'utilisateur"
|
||||
|
||||
#: canaille/templates/profile.html:169
|
||||
#: canaille/templates/profile.html:166
|
||||
msgid "Delete my account"
|
||||
msgstr "Supprimer mon compte"
|
||||
|
||||
#: canaille/templates/users.html:17
|
||||
#: canaille/templates/users.html:16
|
||||
msgid "Add a user"
|
||||
msgstr "Nouvel utilisateur"
|
||||
|
||||
#: canaille/templates/users.html:23
|
||||
#: canaille/templates/users.html:22
|
||||
msgid "Email"
|
||||
msgstr "Courriel"
|
||||
|
||||
|
@ -697,28 +783,28 @@ msgid "Subject"
|
|||
msgstr "Utilisateur"
|
||||
|
||||
#: canaille/templates/admin/authorization_list.html:21
|
||||
#: canaille/templates/admin/client_list.html:27
|
||||
#: canaille/templates/admin/client_list.html:24
|
||||
#: canaille/templates/admin/token_list.html:21
|
||||
msgid "Created"
|
||||
msgstr "Créé"
|
||||
|
||||
#: canaille/templates/admin/authorization_view.html:8
|
||||
#: canaille/templates/admin/authorization_view.html:7
|
||||
msgid "View a authorization"
|
||||
msgstr "Voir une autorisation"
|
||||
|
||||
#: canaille/templates/admin/client_add.html:8
|
||||
#: canaille/templates/admin/client_add.html:7
|
||||
msgid "Add a client"
|
||||
msgstr "Ajouter un client"
|
||||
|
||||
#: canaille/templates/admin/client_add.html:14
|
||||
#: canaille/templates/admin/client_add.html:11
|
||||
msgid "Confirm"
|
||||
msgstr "Confirmer"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:13
|
||||
#: canaille/templates/admin/client_edit.html:12
|
||||
msgid "Client deletion"
|
||||
msgstr "Suppression d'un client"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:16
|
||||
#: canaille/templates/admin/client_edit.html:15
|
||||
msgid ""
|
||||
"Are you sure you want to delete this client? This action is unrevokable and "
|
||||
"all the data about this client will be removed."
|
||||
|
@ -726,39 +812,39 @@ msgstr ""
|
|||
"Êtes-vous sûrs de vouloir supprimer ce client ? Cette action est irrévocable "
|
||||
"et toutes les données à propos de ce client seront supprimées."
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:26
|
||||
#: canaille/templates/admin/client_edit.html:25
|
||||
msgid "Edit a client"
|
||||
msgstr "Éditer un client"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:35
|
||||
#: canaille/templates/admin/client_edit.html:32
|
||||
msgid "ID"
|
||||
msgstr "ID"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:39
|
||||
#: canaille/templates/admin/client_edit.html:36
|
||||
msgid "Secret"
|
||||
msgstr "Secret"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:43
|
||||
#: canaille/templates/admin/client_edit.html:40
|
||||
msgid "Issued at"
|
||||
msgstr "Créé le"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:60
|
||||
#: canaille/templates/admin/client_edit.html:57
|
||||
msgid "Edit"
|
||||
msgstr "Éditer"
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:63
|
||||
#: canaille/templates/admin/client_edit.html:60
|
||||
msgid "Delete the client"
|
||||
msgstr "Supprimer le client"
|
||||
|
||||
#: canaille/templates/admin/client_list.html:20
|
||||
#: canaille/templates/admin/client_list.html:17
|
||||
msgid "Add client"
|
||||
msgstr "Ajouter un client"
|
||||
|
||||
#: canaille/templates/admin/client_list.html:26
|
||||
#: canaille/templates/admin/client_list.html:23
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
|
||||
#: canaille/templates/admin/token_view.html:8
|
||||
#: canaille/templates/admin/token_view.html:7
|
||||
msgid "View a token"
|
||||
msgstr "Voir un jeton"
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PROJECT VERSION\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
"POT-Creation-Date: 2021-06-03 15:21+0200\n"
|
||||
"POT-Creation-Date: 2021-07-29 16:09+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -17,7 +17,7 @@ msgstr ""
|
|||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.9.1\n"
|
||||
|
||||
#: canaille/account.py:62 canaille/account.py:87 canaille/oauth.py:58
|
||||
#: canaille/account.py:62 canaille/account.py:87 canaille/oauth.py:59
|
||||
msgid "Login failed, please check your information"
|
||||
msgstr ""
|
||||
|
||||
|
@ -144,9 +144,9 @@ msgstr ""
|
|||
msgid "jdoe"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/admin/clients.py:23 canaille/forms.py:82
|
||||
#: canaille/templates/admin/client_list.html:25
|
||||
#: canaille/templates/users.html:22
|
||||
#: canaille/admin/clients.py:23 canaille/forms.py:82 canaille/forms.py:152
|
||||
#: canaille/templates/admin/client_list.html:22
|
||||
#: canaille/templates/users.html:21
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
|
@ -170,7 +170,7 @@ msgstr ""
|
|||
msgid "Email address"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:109 canaille/templates/users.html:24
|
||||
#: canaille/forms.py:109 canaille/templates/users.html:23
|
||||
msgid "Phone number"
|
||||
msgstr ""
|
||||
|
||||
|
@ -190,10 +190,37 @@ msgstr ""
|
|||
msgid "1234"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:143
|
||||
#: canaille/forms.py:143 canaille/templates/base.html:55
|
||||
#: canaille/templates/groups.html:9
|
||||
msgid "Groups"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:155
|
||||
msgid "group"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/forms.py:162
|
||||
msgid "The group '{group}' already exists"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/groups.py:39
|
||||
msgid "Group creation failed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/groups.py:46
|
||||
#, python-format
|
||||
msgid "The group %(group)s has been sucessfully created"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/groups.py:76
|
||||
msgid "Group edition failed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/groups.py:85
|
||||
#, python-format
|
||||
msgid "The group %(group)s has been sucessfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/admin/mail.py:27 canaille/mails.py:27
|
||||
msgid "Password reset on {website_name}"
|
||||
msgstr ""
|
||||
|
@ -218,7 +245,11 @@ msgstr ""
|
|||
msgid "Your phone number."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/oauth.py:97
|
||||
#: canaille/oauth.py:30
|
||||
msgid "Groups you are belonging to"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/oauth.py:98
|
||||
msgid "You have been successfully logged out."
|
||||
msgstr ""
|
||||
|
||||
|
@ -298,24 +329,24 @@ msgstr ""
|
|||
msgid "The client has been deleted."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/about.html:14 canaille/templates/base.html:90
|
||||
#: canaille/templates/about.html:12 canaille/templates/base.html:98
|
||||
msgid "About canaille"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/about.html:16
|
||||
#: canaille/templates/about.html:14
|
||||
msgid "Free and open-source identity provider."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/about.html:19
|
||||
#: canaille/templates/about.html:17
|
||||
#, python-format
|
||||
msgid "Version %(version)s"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/about.html:20
|
||||
#: canaille/templates/about.html:18
|
||||
msgid "Source code"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/about.html:21
|
||||
#: canaille/templates/about.html:19
|
||||
msgid "Documentation"
|
||||
msgstr ""
|
||||
|
||||
|
@ -341,67 +372,67 @@ msgstr ""
|
|||
msgid "Accept"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:8
|
||||
#: canaille/templates/base.html:10
|
||||
msgid "authorization interface"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:37 canaille/templates/profile.html:38
|
||||
#: canaille/templates/base.html:39 canaille/templates/profile.html:37
|
||||
msgid "My profile"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:42 canaille/templates/consent_list.html:19
|
||||
#: canaille/templates/base.html:44 canaille/templates/consent_list.html:18
|
||||
msgid "My consents"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:48
|
||||
#: canaille/templates/base.html:50
|
||||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:58
|
||||
#: canaille/templates/base.html:65
|
||||
msgid "Clients"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:62
|
||||
#: canaille/templates/base.html:69
|
||||
msgid "Tokens"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:66
|
||||
#: canaille/templates/base.html:73
|
||||
msgid "Codes"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:70
|
||||
#: canaille/templates/base.html:77
|
||||
msgid "Consents"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/base.html:77
|
||||
#: canaille/templates/base.html:84
|
||||
msgid "Log out"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:22
|
||||
#: canaille/templates/consent_list.html:21
|
||||
msgid "Consult and revoke the authorization you gave to websites."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:42
|
||||
#: canaille/templates/consent_list.html:39
|
||||
msgid "From:"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:44
|
||||
#: canaille/templates/consent_list.html:41
|
||||
msgid "Revoked:"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:47
|
||||
#: canaille/templates/consent_list.html:44
|
||||
msgid "Has access to:"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:57
|
||||
#: canaille/templates/consent_list.html:54
|
||||
msgid "Remove access"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:67
|
||||
#: canaille/templates/consent_list.html:64
|
||||
msgid "Nothing here"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/consent_list.html:68
|
||||
#: canaille/templates/consent_list.html:65
|
||||
msgid "You did not authorize applications yet."
|
||||
msgstr ""
|
||||
|
||||
|
@ -421,11 +452,11 @@ msgstr ""
|
|||
msgid "Technical problem"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/firstlogin.html:12
|
||||
#: canaille/templates/firstlogin.html:11
|
||||
msgid "First login"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/firstlogin.html:19
|
||||
#: canaille/templates/firstlogin.html:16
|
||||
msgid ""
|
||||
"\n"
|
||||
" It seems this is the first time you are logging here. In order to"
|
||||
|
@ -438,21 +469,21 @@ msgid ""
|
|||
" "
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/firstlogin.html:35
|
||||
#: canaille/templates/firstlogin.html:32
|
||||
msgid "Send the initialization email"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/firstlogin.html:36
|
||||
#: canaille/templates/forgotten-password.html:43
|
||||
#: canaille/templates/firstlogin.html:33
|
||||
#: canaille/templates/forgotten-password.html:40
|
||||
msgid "Login page"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/forgotten-password.html:12
|
||||
#: canaille/templates/login.html:38 canaille/templates/password.html:35
|
||||
#: canaille/templates/forgotten-password.html:11
|
||||
#: canaille/templates/login.html:35 canaille/templates/password.html:32
|
||||
msgid "Forgotten password"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/forgotten-password.html:19
|
||||
#: canaille/templates/forgotten-password.html:16
|
||||
msgid ""
|
||||
"\n"
|
||||
" After this form is sent, if the email address or the login you "
|
||||
|
@ -463,181 +494,230 @@ msgid ""
|
|||
" "
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/forgotten-password.html:38
|
||||
#: canaille/templates/profile.html:120 canaille/templates/profile.html:143
|
||||
#: canaille/templates/forgotten-password.html:35
|
||||
#: canaille/templates/profile.html:117 canaille/templates/profile.html:140
|
||||
msgid "Send again"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/forgotten-password.html:40
|
||||
#: canaille/templates/forgotten-password.html:37
|
||||
msgid "Send"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/login.html:17
|
||||
#: canaille/templates/group.html:13
|
||||
msgid "Group deletion"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:17
|
||||
msgid ""
|
||||
"Are you sure you want to delete this group? This action is unrevokable "
|
||||
"and all the data about this group will be removed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:18
|
||||
#: canaille/templates/group.html:21 canaille/templates/profile.html:25
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:19
|
||||
#: canaille/templates/group.html:22 canaille/templates/profile.html:26
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:29
|
||||
msgid "Members"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:45
|
||||
msgid "Group creation"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:47
|
||||
msgid "Group edition"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:53
|
||||
msgid "Create a new group"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:55
|
||||
msgid "Edit informations about a group"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:70
|
||||
msgid ""
|
||||
"Because group cannot be empty, you will be added to the group. You can "
|
||||
"remove you later by editing your profile when you will have added other "
|
||||
"members to the group."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:75
|
||||
msgid "Create group"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:77 canaille/templates/profile.html:154
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/group.html:82
|
||||
msgid "Delete group"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/groups.html:5
|
||||
msgid "Add a group"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/login.html:16
|
||||
#, python-format
|
||||
msgid "Sign in at %(website)s"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/login.html:19
|
||||
#: canaille/templates/login.html:18
|
||||
msgid "Log-in and manage your authorizations."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/login.html:37
|
||||
#: canaille/templates/login.html:34
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/password.html:17
|
||||
#: canaille/templates/password.html:16
|
||||
#, python-format
|
||||
msgid "Sign in as %(username)s"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/password.html:19
|
||||
#: canaille/templates/password.html:18
|
||||
msgid "Please enter your password for this account."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/password.html:34
|
||||
#: canaille/templates/password.html:31
|
||||
msgid "Sign in"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/password.html:36
|
||||
#: canaille/templates/password.html:33
|
||||
#, python-format
|
||||
msgid "I am not %(username)s"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:14
|
||||
#: canaille/templates/profile.html:13
|
||||
msgid "Account deletion"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:19
|
||||
#: canaille/templates/profile.html:18
|
||||
msgid ""
|
||||
"Are you sure you want to delete this user? This action is unrevokable and"
|
||||
" all the data about this user will be removed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:21
|
||||
#: canaille/templates/profile.html:20
|
||||
msgid ""
|
||||
"Are you sure you want to delete your account? This action is unrevokable "
|
||||
"and all your data will be removed forever."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:19
|
||||
#: canaille/templates/profile.html:26
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:20
|
||||
#: canaille/templates/profile.html:27
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:36
|
||||
#: canaille/templates/profile.html:35
|
||||
msgid "User creation"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:40
|
||||
#: canaille/templates/profile.html:39
|
||||
msgid "User profile edition"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:46
|
||||
#: canaille/templates/profile.html:45
|
||||
msgid "Create a new user account"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:48
|
||||
#: canaille/templates/profile.html:47
|
||||
msgid "Edit your personal informations"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:50
|
||||
#: canaille/templates/profile.html:49
|
||||
msgid "Edit informations about an user"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:67
|
||||
#: canaille/templates/profile.html:64
|
||||
msgid "Personal information"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:89
|
||||
#: canaille/templates/profile.html:86
|
||||
msgid "Account information"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:105
|
||||
#: canaille/templates/profile.html:102
|
||||
msgid "User password is not mandatory"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:107
|
||||
#: canaille/templates/profile.html:104
|
||||
msgid "The user password can be set:"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:109
|
||||
#: canaille/templates/profile.html:106
|
||||
msgid "by filling this form;"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:110
|
||||
#: canaille/templates/profile.html:107
|
||||
msgid ""
|
||||
"by sending the user a password initialization mail, after the account "
|
||||
"creation;"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:111 canaille/templates/profile.html:134
|
||||
#: canaille/templates/profile.html:108 canaille/templates/profile.html:131
|
||||
msgid ""
|
||||
"or simply waiting for the user to sign-in a first time, and then receive "
|
||||
"a password initialization mail."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:124
|
||||
#: canaille/templates/profile.html:121
|
||||
msgid "Send email"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:128
|
||||
#: canaille/templates/profile.html:125
|
||||
msgid "This user does not have a password yet"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:130
|
||||
#: canaille/templates/profile.html:127
|
||||
msgid "You can solve this by:"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:132
|
||||
#: canaille/templates/profile.html:129
|
||||
msgid "setting a password using this form;"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:133
|
||||
#: canaille/templates/profile.html:130
|
||||
msgid "sending the user a password initialization mail, by clicking this button;"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:145
|
||||
#: canaille/templates/profile.html:142
|
||||
msgid "Send mail"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:149
|
||||
#: canaille/templates/reset-password.html:12
|
||||
#: canaille/templates/reset-password.html:19
|
||||
#: canaille/templates/profile.html:146
|
||||
#: canaille/templates/reset-password.html:11
|
||||
#: canaille/templates/reset-password.html:16
|
||||
msgid "Password reset"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:151
|
||||
#: canaille/templates/profile.html:148
|
||||
msgid ""
|
||||
"If the user has forgotten his password, you can send him a password reset"
|
||||
" email by clicking this button."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:157
|
||||
msgid "Submit"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:161
|
||||
#: canaille/templates/profile.html:158
|
||||
msgid "Impersonate"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:167
|
||||
#: canaille/templates/profile.html:164
|
||||
msgid "Delete the user"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/profile.html:169
|
||||
#: canaille/templates/profile.html:166
|
||||
msgid "Delete my account"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/users.html:17
|
||||
#: canaille/templates/users.html:16
|
||||
msgid "Add a user"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/users.html:23
|
||||
#: canaille/templates/users.html:22
|
||||
msgid "Email"
|
||||
msgstr ""
|
||||
|
||||
|
@ -657,66 +737,66 @@ msgid "Subject"
|
|||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/authorization_list.html:21
|
||||
#: canaille/templates/admin/client_list.html:27
|
||||
#: canaille/templates/admin/client_list.html:24
|
||||
#: canaille/templates/admin/token_list.html:21
|
||||
msgid "Created"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/authorization_view.html:8
|
||||
#: canaille/templates/admin/authorization_view.html:7
|
||||
msgid "View a authorization"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_add.html:8
|
||||
#: canaille/templates/admin/client_add.html:7
|
||||
msgid "Add a client"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_add.html:14
|
||||
#: canaille/templates/admin/client_add.html:11
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:13
|
||||
#: canaille/templates/admin/client_edit.html:12
|
||||
msgid "Client deletion"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:16
|
||||
#: canaille/templates/admin/client_edit.html:15
|
||||
msgid ""
|
||||
"Are you sure you want to delete this client? This action is unrevokable "
|
||||
"and all the data about this client will be removed."
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:26
|
||||
#: canaille/templates/admin/client_edit.html:25
|
||||
msgid "Edit a client"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:35
|
||||
#: canaille/templates/admin/client_edit.html:32
|
||||
msgid "ID"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:39
|
||||
#: canaille/templates/admin/client_edit.html:36
|
||||
msgid "Secret"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:43
|
||||
#: canaille/templates/admin/client_edit.html:40
|
||||
msgid "Issued at"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:60
|
||||
#: canaille/templates/admin/client_edit.html:57
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_edit.html:63
|
||||
#: canaille/templates/admin/client_edit.html:60
|
||||
msgid "Delete the client"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_list.html:20
|
||||
#: canaille/templates/admin/client_list.html:17
|
||||
msgid "Add client"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/client_list.html:26
|
||||
#: canaille/templates/admin/client_list.html:23
|
||||
msgid "URL"
|
||||
msgstr ""
|
||||
|
||||
#: canaille/templates/admin/token_view.html:8
|
||||
#: canaille/templates/admin/token_view.html:7
|
||||
msgid "View a token"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ def create_app():
|
|||
server_metadata_url=get_well_known_url(
|
||||
app.config["OAUTH_AUTH_SERVER"], external=True
|
||||
),
|
||||
client_kwargs={"scope": "openid profile email"},
|
||||
client_kwargs={"scope": "openid profile email groups"},
|
||||
)
|
||||
|
||||
@app.route("/")
|
||||
|
|
|
@ -53,7 +53,10 @@
|
|||
<h2 class="ui header">{{ name }}</h2>
|
||||
<div>
|
||||
{% if user %}
|
||||
Welcome {{ user.name }}
|
||||
<p>Welcome {{ user.name }}</p>
|
||||
{% if user.groups %}
|
||||
<p>You're a member of the following groups: {{ user.groups }}</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
Welcome, please <a href="{{ url_for('login') }}">log-in</a>.
|
||||
{% endif %}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"http://localhost:5000/oauth/register",
|
||||
"scopes_supported":
|
||||
["openid", "profile", "email", "address",
|
||||
"phone"],
|
||||
"phone", "groups"],
|
||||
"response_types_supported":
|
||||
["code", "token", "id_token", "code token",
|
||||
"code id_token", "token id_token"],
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"http://localhost:5000/oauth/register",
|
||||
"scopes_supported":
|
||||
["openid", "profile", "email", "address",
|
||||
"phone"],
|
||||
"phone", "groups"],
|
||||
"response_types_supported":
|
||||
["code", "token", "id_token", "code token",
|
||||
"code id_token", "token id_token"],
|
||||
|
|
|
@ -94,6 +94,7 @@ oauthGrantType: refresh_token
|
|||
oauthScope: openid
|
||||
oauthScope: profile
|
||||
oauthScope: email
|
||||
oauthScope: groups
|
||||
oauthResponseType: code
|
||||
oauthResponseType: id_token
|
||||
oauthTokenEndpointAuthMethod: client_secret_basic
|
||||
|
@ -111,6 +112,7 @@ oauthGrantType: refresh_token
|
|||
oauthScope: openid
|
||||
oauthScope: profile
|
||||
oauthScope: email
|
||||
oauthScope: groups
|
||||
oauthResponseType: code
|
||||
oauthResponseType: id_token
|
||||
oauthTokenEndpointAuthMethod: client_secret_basic
|
||||
|
|
|
@ -215,7 +215,7 @@ def client(app, slapd_connection):
|
|||
"refresh_token",
|
||||
],
|
||||
oauthResponseType=["code", "token", "id_token"],
|
||||
oauthScope=["openid", "profile"],
|
||||
oauthScope=["openid", "profile", "groups"],
|
||||
oauthTermsOfServiceURI="https://mydomain.tld/tos",
|
||||
oauthPolicyURI="https://mydomain.tld/policy",
|
||||
oauthJWKURI="https://mydomain.tld/jwk",
|
||||
|
@ -361,7 +361,9 @@ def foo_group(app, user, slapd_connection):
|
|||
g.save(slapd_connection)
|
||||
with app.app_context():
|
||||
user.load_groups(conn=slapd_connection)
|
||||
return g
|
||||
yield g
|
||||
user._groups = []
|
||||
g.delete(conn=slapd_connection)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
@ -375,9 +377,6 @@ def bar_group(app, admin, slapd_connection):
|
|||
g.save(slapd_connection)
|
||||
with app.app_context():
|
||||
admin.load_groups(conn=slapd_connection)
|
||||
return g
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def groups(foo_group, bar_group, slapd_connection):
|
||||
return (foo_group, bar_group)
|
||||
yield g
|
||||
admin._groups = []
|
||||
g.delete(conn=slapd_connection)
|
||||
|
|
|
@ -47,7 +47,7 @@ def test_authorization_code_flow(testclient, slapd_connection, logged_user, clie
|
|||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
status=200,
|
||||
)
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user"} == res.json
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user", "groups": []} == res.json
|
||||
|
||||
|
||||
def test_logout_login(testclient, slapd_connection, logged_user, client):
|
||||
|
@ -105,7 +105,7 @@ def test_logout_login(testclient, slapd_connection, logged_user, client):
|
|||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
status=200,
|
||||
)
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user"} == res.json
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user", "groups": []} == res.json
|
||||
|
||||
|
||||
def test_refresh_token(testclient, slapd_connection, logged_user, client):
|
||||
|
@ -164,7 +164,7 @@ def test_refresh_token(testclient, slapd_connection, logged_user, client):
|
|||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
status=200,
|
||||
)
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user"} == res.json
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user", "groups": []} == res.json
|
||||
|
||||
|
||||
def test_code_challenge(testclient, slapd_connection, logged_user, client):
|
||||
|
@ -218,7 +218,7 @@ def test_code_challenge(testclient, slapd_connection, logged_user, client):
|
|||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
status=200,
|
||||
)
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user"} == res.json
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user", "groups": []} == res.json
|
||||
|
||||
client.oauthTokenEndpointAuthMethod = "client_secret_basic"
|
||||
client.save(slapd_connection)
|
||||
|
|
|
@ -9,7 +9,7 @@ def test_no_group(app, slapd_connection):
|
|||
def test_set_groups(app, slapd_connection, user, foo_group, bar_group):
|
||||
with app.app_context():
|
||||
Group.attr_type_by_name(conn=slapd_connection)
|
||||
a = User.attr_type_by_name(conn=slapd_connection)
|
||||
User.attr_type_by_name(conn=slapd_connection)
|
||||
|
||||
user = User.get(dn=user.dn, conn=slapd_connection)
|
||||
assert set(Group.available_groups(conn=slapd_connection)) == {
|
||||
|
@ -21,14 +21,80 @@ def test_set_groups(app, slapd_connection, user, foo_group, bar_group):
|
|||
assert user.groups[0].dn == foo_group.dn
|
||||
|
||||
user.set_groups([foo_group, bar_group], conn=slapd_connection)
|
||||
|
||||
bar_dns = {g.dn for g in bar_group.get_members(conn=slapd_connection)}
|
||||
assert user.dn in bar_dns
|
||||
|
||||
assert user.groups[1].dn == bar_group.dn
|
||||
|
||||
user.set_groups([foo_group], conn=slapd_connection)
|
||||
|
||||
foo_dns = {g.dn for g in foo_group.get_members(conn=slapd_connection)}
|
||||
bar_dns = {g.dn for g in bar_group.get_members(conn=slapd_connection)}
|
||||
|
||||
assert user.dn in foo_dns
|
||||
assert user.dn not in bar_dns
|
||||
|
||||
|
||||
def test_moderator_can_create_edit_and_delete_group(
|
||||
testclient, slapd_connection, logged_moderator, foo_group
|
||||
):
|
||||
# The group does not exist
|
||||
res = testclient.get("/groups", status=200)
|
||||
with testclient.app.app_context():
|
||||
assert Group.get("bar", conn=slapd_connection) is None
|
||||
assert Group.get("foo", conn=slapd_connection) == foo_group
|
||||
assert "bar" not in res.text
|
||||
assert "foo" in res.text
|
||||
|
||||
# Fill the form for a new group
|
||||
res = testclient.get("/groups/add", status=200)
|
||||
res.form["name"] = "bar"
|
||||
|
||||
# Group has been created
|
||||
res = res.form.submit(status=302).follow(status=200)
|
||||
|
||||
with testclient.app.app_context():
|
||||
bar_group = Group.get("bar", conn=slapd_connection)
|
||||
assert bar_group.name == "bar"
|
||||
assert [
|
||||
member.dn for member in bar_group.get_members(conn=slapd_connection)
|
||||
] == [
|
||||
logged_moderator.dn
|
||||
] # Group cannot be empty so creator is added in it
|
||||
assert "bar" in res.text
|
||||
|
||||
# Group name can not be edited
|
||||
res = testclient.get("/groups/bar", status=200)
|
||||
res.form["name"] = "bar2"
|
||||
|
||||
res = res.form.submit(name="action", value="edit", status=200)
|
||||
|
||||
with testclient.app.app_context():
|
||||
bar_group = Group.get("bar", conn=slapd_connection)
|
||||
assert bar_group.name == "bar"
|
||||
assert Group.get("bar2", conn=slapd_connection) is None
|
||||
members = bar_group.get_members(conn=slapd_connection)
|
||||
for member in members:
|
||||
assert member.name in res.text
|
||||
|
||||
# Group is deleted
|
||||
res = res.form.submit(name="action", value="delete", status=302).follow(status=200)
|
||||
with testclient.app.app_context():
|
||||
assert Group.get("bar", conn=slapd_connection) is None
|
||||
assert "The group bar has been sucessfully deleted" in res.text
|
||||
|
||||
|
||||
def test_cannot_create_already_existing_group(
|
||||
testclient, slapd_connection, logged_moderator, foo_group
|
||||
):
|
||||
res = testclient.post("/groups/add", {"name": "foo"}, status=200)
|
||||
|
||||
assert "Group creation failed." in res
|
||||
assert "The group 'foo' already exists" in res
|
||||
|
||||
|
||||
def test_simple_user_cannot_view_or_edit_groups(
|
||||
testclient, slapd_connection, logged_user, foo_group
|
||||
):
|
||||
testclient.get("/groups", status=403)
|
||||
testclient.get("/groups/add", status=403)
|
||||
testclient.get("/groups/foo", status=403)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
from authlib.jose import jwt
|
||||
from urllib.parse import urlsplit, parse_qs
|
||||
from canaille.models import AuthorizationCode, Token
|
||||
from canaille.models import AuthorizationCode, Token, User
|
||||
|
||||
|
||||
def test_oauth_hybrid(testclient, slapd_connection, user, client):
|
||||
User.attr_type_by_name(slapd_connection)
|
||||
res = testclient.get(
|
||||
"/oauth/authorize",
|
||||
params=dict(
|
||||
|
@ -41,7 +42,7 @@ def test_oauth_hybrid(testclient, slapd_connection, user, client):
|
|||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
status=200,
|
||||
)
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user"} == res.json
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user", "groups": []} == res.json
|
||||
|
||||
|
||||
def test_oidc_hybrid(testclient, slapd_connection, logged_user, client, keypair):
|
||||
|
@ -80,4 +81,4 @@ def test_oidc_hybrid(testclient, slapd_connection, logged_user, client, keypair)
|
|||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
status=200,
|
||||
)
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user"} == res.json
|
||||
assert {"name": "John Doe", "family_name": "Doe", "sub": "user", "groups": []} == res.json
|
||||
|
|
|
@ -40,7 +40,7 @@ def test_oauth_implicit(testclient, slapd_connection, user, client):
|
|||
"/oauth/userinfo", headers={"Authorization": f"Bearer {access_token}"}
|
||||
)
|
||||
assert "application/json" == res.content_type
|
||||
assert {"name": "John Doe", "sub": "user", "family_name": "Doe"} == res.json
|
||||
assert {"name": "John Doe", "sub": "user", "family_name": "Doe", "groups": []} == res.json
|
||||
|
||||
client.oauthGrantType = ["code"]
|
||||
client.oauthTokenEndpointAuthMethod = "client_secret_basic"
|
||||
|
@ -92,7 +92,60 @@ def test_oidc_implicit(testclient, keypair, slapd_connection, user, client):
|
|||
status=200,
|
||||
)
|
||||
assert "application/json" == res.content_type
|
||||
assert {"name": "John Doe", "sub": "user", "family_name": "Doe"} == res.json
|
||||
assert {"name": "John Doe", "sub": "user", "family_name": "Doe", "groups": []} == res.json
|
||||
|
||||
client.oauthGrantType = ["code"]
|
||||
client.oauthTokenEndpointAuthMethod = "client_secret_basic"
|
||||
client.save(slapd_connection)
|
||||
|
||||
|
||||
def test_oidc_implicit_with_group(testclient, keypair, slapd_connection, user, client, foo_group):
|
||||
client.oauthGrantType = ["token id_token"]
|
||||
client.oauthTokenEndpointAuthMethod = "none"
|
||||
|
||||
client.save(slapd_connection)
|
||||
|
||||
res = testclient.get(
|
||||
"/oauth/authorize",
|
||||
params=dict(
|
||||
response_type="id_token token",
|
||||
client_id=client.oauthClientID,
|
||||
scope="openid profile groups",
|
||||
nonce="somenonce",
|
||||
),
|
||||
)
|
||||
assert "text/html" == res.content_type
|
||||
|
||||
res.form["login"] = "user"
|
||||
res.form["password"] = "correct horse battery staple"
|
||||
res = res.form.submit(status=302)
|
||||
|
||||
res = res.follow(status=200)
|
||||
assert "text/html" == res.content_type, res.json
|
||||
|
||||
res = res.form.submit(name="answer", value="accept", status=302)
|
||||
|
||||
assert res.location.startswith(client.oauthRedirectURIs[0])
|
||||
params = parse_qs(urlsplit(res.location).fragment)
|
||||
|
||||
access_token = params["access_token"][0]
|
||||
token = Token.get(access_token, conn=slapd_connection)
|
||||
assert token is not None
|
||||
|
||||
id_token = params["id_token"][0]
|
||||
claims = jwt.decode(id_token, keypair[1])
|
||||
assert user.uid[0] == claims["sub"]
|
||||
assert user.cn[0] == claims["name"]
|
||||
assert [client.oauthClientID] == claims["aud"]
|
||||
assert ["foo"] == claims["groups"]
|
||||
|
||||
res = testclient.get(
|
||||
"/oauth/userinfo",
|
||||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
status=200,
|
||||
)
|
||||
assert "application/json" == res.content_type
|
||||
assert {"name": "John Doe", "sub": "user", "family_name": "Doe", "groups": ["foo"]} == res.json
|
||||
|
||||
client.oauthGrantType = ["code"]
|
||||
client.oauthTokenEndpointAuthMethod = "client_secret_basic"
|
||||
|
|
|
@ -15,7 +15,7 @@ def test_password_flow(testclient, slapd_connection, user, client):
|
|||
status=200,
|
||||
)
|
||||
|
||||
assert res.json["scope"] == "openid profile"
|
||||
assert res.json["scope"] == "openid profile groups"
|
||||
assert res.json["token_type"] == "Bearer"
|
||||
access_token = res.json["access_token"]
|
||||
|
||||
|
@ -27,4 +27,4 @@ def test_password_flow(testclient, slapd_connection, user, client):
|
|||
headers={"Authorization": f"Bearer {access_token}"},
|
||||
status=200,
|
||||
)
|
||||
assert {"name": "John Doe", "sub": "user", "family_name": "Doe"} == res.json
|
||||
assert {"name": "John Doe", "sub": "user", "family_name": "Doe", "groups": []} == res.json
|
||||
|
|
Loading…
Reference in a new issue