Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • Dockerfile
  • feature_invite_validuntil_minmax
  • incremental-sync
  • jwt_encode_inconsistencies
  • master
  • redis-rate-limits
  • roles-recursive-cte
  • typehints
  • v1.0.x
  • v1.1.x
  • v1.2.x
  • v1.x.x
  • v0.1.2
  • v0.1.4
  • v0.1.5
  • v0.2.0
  • v0.3.0
  • v1.0.0
  • v1.0.1
  • v1.0.2
  • v1.1.0
  • v1.1.1
  • v1.1.2
  • v1.2.0
  • v2.0.0
  • v2.0.1
  • v2.1.0
  • v2.2.0
  • v2.3.0
  • v2.3.1
30 results

Target

Select target project
  • uffd/uffd
  • rixx/uffd
  • thies/uffd
  • leona/uffd
  • strifel/uffd
  • thies/uffd-2
6 results
Select Git revision
  • Dockerfile
  • claims-in-idtoke
  • feature_invite_validuntil_minmax
  • incremental-sync
  • jwt_encode_inconsistencies
  • master
  • recovery-code-pwhash
  • redis-rate-limits
  • roles-recursive-cte
  • typehints
  • v1.0.x
  • v1.1.x
  • v1.2.x
  • v1.x.x
  • v0.1.2
  • v0.1.4
  • v0.1.5
  • v0.2.0
  • v0.3.0
  • v1.0.0
  • v1.0.1
  • v1.0.2
  • v1.1.0
  • v1.1.1
  • v1.1.2
  • v1.2.0
  • v2.0.0
  • v2.0.1
  • v2.1.0
  • v2.2.0
  • v2.3.0
  • v2.3.1
32 results
Show changes
Showing
with 553 additions and 57 deletions
......@@ -3,6 +3,11 @@
{% block body %}
<div class="row">
<div class="col">
<p class="text-right">
<a class="btn btn-primary" href="{{ url_for("group.show") }}">
<i class="fa fa-plus" aria-hidden="true"></i> {{_("New")}}
</a>
</p>
<table class="table table-striped table-sm">
<thead>
<tr>
......@@ -12,13 +17,13 @@
</tr>
</thead>
<tbody>
{% for group in groups|sort(attribute="gid") %}
<tr id="group-{{ group.gid }}">
{% for group in groups|sort(attribute="unix_gid") %}
<tr id="group-{{ group.id }}">
<th scope="row">
{{ group.gid }}
{{ group.unix_gid }}
</th>
<td>
<a href="{{ url_for("group.show", gid=group.gid) }}">
<a href="{{ url_for("group.show", id=group.id) }}">
{{ group.name }}
</a>
</td>
......
{% extends 'base.html' %}
{% block body %}
<form action="{{ url_for("group.update", id=group.id) }}" method="POST" autocomplete="off">
<div class="align-self-center">
<div class="clearfix pb-2 col">
<div class="float-sm-right">
<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> {{_("Save")}}</button>
<a href="{{ url_for("group.index") }}" class="btn btn-secondary">{{_("Cancel")}}</a>
{% if group.id %}
<a href="{{ url_for("group.delete", id=group.id) }}" onClick='return confirm({{_("Are you sure?")|tojson}});' class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i> {{_("Delete")}}</a>
{% else %}
<a href="#" class="btn btn-danger disabled"><i class="fa fa-trash" aria-hidden="true"></i> {{_("Delete")}}</a>
{% endif %}
</div>
</div>
<div class="form-group col">
<label for="group-gid">{{_("Group ID")}}</label>
{% if not group.id %}
<input type="number" class="form-control" id="group-gid" name="unix_gid" value="" placeholder="Automatically chosen if empty">
{% else %}
<input type="number" class="form-control" id="group-gid" name="unix_gid" value="{{ group.unix_gid }}" readonly>
{% endif %}
</div>
<div class="form-group col">
<label for="group-loginname">{{_("Name")}}</label>
<input type="text" class="form-control" id="group-loginname" name="name" minlength=1 maxlength=32 pattern="[a-z0-9_-]*" value="{{ group.name or '' }}" {{ 'readonly' if group.id }}>
<small class="form-text text-muted">
{{_('At least one and at most 32 lower-case characters, digits, dashes ("-") or underscores ("_"). <b>Cannot be changed later!</b>')|safe}}
</small>
</div>
<div class="form-group col">
<label for="group-description">{{_("Description")}}</label>
<textarea class="form-control" id="group-description" name="description" rows="5">{{ group.description or '' }}</textarea>
<small class="form-text text-muted">
</small>
</div>
{% if group.id %}
<div class="col">
<span>{{_("Members")}}:</span>
<ul class="row">
{% for member in group.members|sort(attribute='loginname') %}
<li class="col-12 col-xs-6 col-sm-4 col-md-3 col-lg-2"><a href="{{ url_for("user.show", id=member.id) }}">{{ member.loginname }}</a></li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
</form>
{% endblock %}
......@@ -22,17 +22,15 @@
<tr>
<td>
{% if invite.creator == request.user and invite.active %}
<a href="{{ url_for('invite.use', token=invite.token) }}"><code>{{ invite.short_token }}</code></a>
<button type="button" class="btn btn-link btn-sm p-0 copy-clipboard" data-copy="{{ url_for('invite.use', token=invite.token, _external=True) }}" title="{{_('Copy link to clipboard')}}"><i class="fas fa-clipboard"></i></button>
<a href="{{ url_for('invite.use', invite_id=invite.id, token=invite.token) }}"><code>{{ invite.short_token }}</code></a>
<button type="button" class="btn btn-link btn-sm p-0 copy-clipboard" data-copy="{{ url_for('invite.use', invite_id=invite.id, token=invite.token, _external=True) }}" title="{{_('Copy link to clipboard')}}"><i class="fas fa-clipboard"></i></button>
<button type="button" class="btn btn-link btn-sm p-0" data-toggle="modal" data-target="#modal-{{ invite.id }}-qrcode" title="{{_('Show link as QR code')}}"><i class="fas fa-qrcode"></i></button>
{% else %}
<code>{{ invite.short_token }}</code>
{% endif %}
</td>
<td>
{% if not invite.creator_dn %}
{{ '<admin>' }}
{% elif not invite.creator %}
{% if not invite.creator %}
{{ '<deleted user>' }}
{% else %}
{{ invite.creator.loginname }}
......@@ -43,7 +41,7 @@
{% for role in invite.roles %}{{ ', ' if loop.index != 1 }}<i class="fas fa-key"></i>&thinsp;{{ role.name }}{% endfor %}
</td>
<td>
<span style="white-space: nowrap;">{{ invite.signups|selectattr('completed')|list|length }} <i class="fas fa-users" title="user registrations"></i></span>,
<span style="white-space: nowrap;">{{ invite.signups|selectattr('completed')|list|length }} <i class="fas fa-users" title="{{ _('user signups') }}"></i></span>,
<span style="white-space: nowrap;">{{ invite.grants|length }} <i class="fas fa-key" title="role grants"></i></span>
</td>
<td>
......@@ -58,9 +56,9 @@
{% elif not invite.active %}
{{_('Invalid')}}
{% elif invite.single_use %}
{{ _('Valid once, expires %(expiry_date)s', expiry_date=invite.valid_until.strftime('%Y-%m-%d')) }}
{{ _('Valid once, expires %(expiry_date)s', expiry_date=invite.valid_until|dateformat) }}
{% else %}
{{ _('Valid, expires %(expiry_date)s',expiry_date=invite.valid_until.strftime('%Y-%m-%d')) }}
{{ _('Valid, expires %(expiry_date)s', expiry_date=invite.valid_until|dateformat) }}
{% endif %}
</td>
<td class="text-right">
......@@ -85,8 +83,8 @@
<div class="modal-body">
<ul class="list-unstyled">
<li><b>{{_('Type:')}}</b> {% if invite.single_use %}{{_('Single-use')}}{% else %}{{_('Multi-use')}}{% endif %}</li>
<li><b>{{_('Created:')}}</b> {{ invite.created.strftime('%Y-%m-%d %H:%M:%S') }}</li>
<li><b>{{_('Expires:')}}</b> {{ invite.valid_until.strftime('%Y-%m-%d %H:%M:%S') }}</li>
<li><b>{{_('Created:')}}</b> {{ invite.created|datetimeformat }}</li>
<li><b>{{_('Expires:')}}</b> {{ invite.valid_until|datetimeformat }}</li>
<li><b>{{_('Permissions:')}}</b>
<ul>
{% if invite.allow_signup %}
......@@ -105,10 +103,10 @@
{% else %}
<ul>
{% for signup in invite.signups if signup.completed %}
<li>{{_('Registration of user <a href="%(user_url)s">%(user_name)s</a>', user_url=url_for('user.show', uid=signup.user.uid)|e, user_name=signup.user.loginname|e)|safe}}</li>
<li>{{_('Registration of user <a href="%(user_url)s">%(user_name)s</a>', user_url=url_for('user.show', id=signup.user.id)|e, user_name=signup.user.loginname|e)|safe}}</li>
{% endfor %}
{% for grant in invite.grants if grant.user %}
<li>{{_('Roles granted to <a href="%(user_url)s">%(user_name)s</a>', user_url=url_for('user.show', uid=grant.user.uid)|e, user_name=grant.user.loginname|e)|safe}}</li>
<li>{{_('Roles granted to <a href="%(user_url)s">%(user_name)s</a>', user_url=url_for('user.show', id=grant.user.id)|e, user_name=grant.user.loginname|e)|safe}}</li>
{% endfor %}
</ul>
{% endif %}
......@@ -143,7 +141,7 @@
</button>
</div>
<div class="modal-body">
{{ url_for('invite.use', token=invite.token, _external=True)|qrcode_svg(width='100%', height='100%') }}
{{ url_for('invite.use', invite_id=invite.id, token=invite.token, _external=True)|qrcode_svg(width='100%', height='100%') }}
</div>
</div>
</div>
......
{% extends 'base.html' %}
{% block body %}
<form action="{{ url_for("invite.new_submit") }}" method="POST" class="form">
<form action="{{ url_for("invite.new_submit") }}" method="POST" autocomplete="off" class="form">
<div class="form-group">
<label for="single-use">{{_('Link Type')}}</label>
<select class="form-control" id="single-use" name="single-use">
<option value="1">{{_('Valid for a single successful use')}}</option>
<option value="0">{{_('Multi-use')}}</option>
<option value="1" {{ 'selected' if request.values.get('single-use', '1') == '1' }}>{{_('Valid for a single successful use')}}</option>
<option value="0" {{ 'selected' if request.values.get('single-use', '1') == '0' }}>{{_('Multi-use')}}</option>
</select>
</div>
<div class="form-group">
<label for="valid-until">{{_('Valid Until')}}</label>
<input class="form-control" type="datetime-local" id="valid-until" name="valid-until" value="{{ (datetime.now() + timedelta(hours=36)).replace(hour=23, minute=59, second=59, microsecond=0).isoformat(timespec='minutes') }}">
<input class="form-control" type="datetime-local" id="valid-until" name="valid-until" value="{{ request.values.get('valid-until') or (datetime.now() + timedelta(hours=36)).replace(hour=23, minute=59).isoformat(timespec='minutes') }}" min="{{ datetime.now().isoformat(timespec='minutes') }}" max="{{ (datetime.now() + timedelta(days=config['INVITE_MAX_VALID_DAYS'])).isoformat(timespec='minutes') }}">
<small class="text-muted">{{_('Must be within the next %(max_valid_days)d days', max_valid_days=config['INVITE_MAX_VALID_DAYS'])}}</small>
</div>
{% if allow_signup %}
<div class="form-group">
<label for="allow-signup">{{_('Account Registration')}}</label>
<select class="form-control" id="allow-signup" name="allow-signup">
<option value="1">{{_('Link allows account registration')}}</option>
<option value="0">{{_('No account registration allowed')}}</option>
<option value="1" {{ 'selected' if request.values.get('allow-signup', '1') == '1' }}>{{_('Link allows account registration')}}</option>
<option value="0" {{ 'selected' if request.values.get('allow-signup', '1') == '0' }}>{{_('No account registration allowed')}}</option>
</select>
</div>
{% else %}
......
{% extends 'base_narrow.html' %}
{% block body %}
<div class="col-12 mb-3">
<h2 class="text-center">{{_('Invite Link')}}</h2>
</div>
{% if not request.user %}
<p>{{_('Welcome to the %(org_name)s Single-Sign-On!', org_name=config.ORGANISATION_NAME)}}</p>
{% endif %}
{% if invite.roles and invite.allow_signup %}
<p>{{_('With this link you can register a new user account with the following roles or add the roles to an existing account:')}}</p>
{% elif invite.roles %}
<p>{{_('With this link you can add the following roles to an existing account:')}}</p>
{% elif invite.allow_signup %}
<p>{{_('With this link you can register a new user account.')}}</p>
{% endif %}
{% if invite.roles %}
<ul>
{% for role in invite.roles %}
<li>{{ role.name }}{% if role.description %}: {{ role.description }}{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
{% if request.user %}
{% if invite.roles %}
<form method="POST" action="{{ url_for("invite.grant", invite_id=invite.id, token=invite.token) }}" class="mb-2">
<button type="submit" class="btn btn-primary btn-block">{{_('Add the roles to your account now')}}</button>
</form>
<a href="{{ url_for("session.logout", ref=url_for("session.login", ref=request.full_path)) }}" class="btn btn-secondary btn-block">{{_('Logout and switch to a different account')}}</a>
{% endif %}
{% if invite.allow_signup %}
<a href="{{ url_for("session.logout", ref=url_for("invite.signup_start", invite_id=invite.id, token=invite.token)) }}" class="btn btn-secondary btn-block">{{_('Logout to register a new account')}}</a>
{% endif %}
{% else %}
{% if invite.allow_signup %}
<a href="{{ url_for("invite.signup_start", invite_id=invite.id, token=invite.token) }}" class="btn btn-primary btn-block">{{_('Register a new account')}}</a>
{% endif %}
{% if invite.roles %}
<a href="{{ url_for("session.login", ref=request.full_path) }}" class="btn btn-primary btn-block">{{_('Login and add the roles to your account')}}</a>
{% endif %}
{% endif %}
{% endblock %}
......@@ -18,9 +18,9 @@
</thead>
<tbody>
{% for mail in mails|sort(attribute="uid") %}
<tr id="mail-{{ mail.uid }}">
<tr id="mail-{{ mail.id }}">
<th scope="row">
<a href="{{ url_for("mail.show", uid=mail.uid) }}">
<a href="{{ url_for("mail.show", mail_id=mail.id) }}">
{{ mail.uid }}
</a>
</th>
......
{% extends 'base.html' %}
{% block body %}
<form action="{{ url_for("mail.update", uid=mail.uid) }}" method="POST">
<form action="{{ url_for("mail.update", mail_id=mail.id) }}" method="POST" autocomplete="off">
<div class="align-self-center">
<div class="form-group col">
<label for="mail-name">{{_('Name')}}</label>
<input type="text" class="form-control" id="mail-name" name="mail-uid" {% if mail.uid %} value="{{ mail.uid }}" readonly {% else %} value=""{% endif %}>
<input type="text" class="form-control" id="mail-name" name="mail-uid" {% if mail.id %} value="{{ mail.uid }}" readonly {% else %} value=""{% endif %}>
<small class="form-text text-muted">
</small>
</div>
......@@ -13,7 +13,7 @@
<label for="mail-receivers">{{_('Receiving addresses')}}</label>
<textarea rows="10" class="form-control" id="mail-receivers" name="mail-receivers">{{ mail.receivers|join('\n') }}</textarea>
<small class="form-text text-muted">
{{_('One address per line')}}
{{_('One address pattern (local+ext@domain, local@domain, local, @domain) per line. Only lower-case ASCII letters, digits and symbols.')}}
</small>
</div>
<div class="form-group col">
......@@ -26,8 +26,8 @@
<div class="form-group col">
<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> {{_('Save')}}</button>
<a href="{{ url_for("mail.index") }}" class="btn btn-secondary">{{_('Cancel')}}</a>
{% if mail.uid %}
<a href="{{ url_for("mail.delete", uid=mail.uid) }}" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i> {{_('Delete')}}</a>
{% if mail.id %}
<a href="{{ url_for("mail.delete", mail_id=mail.id) }}" class="btn btn-danger"><i class="fa fa-trash" aria-hidden="true"></i> {{_('Delete')}}</a>
{% else %}
<a href="#" class="btn btn-danger disabled"><i class="fa fa-trash" aria-hidden="true"></i> {{_('Delete')}}</a>
{% endif %}
......
......@@ -3,14 +3,6 @@
{% block body %}
<h1>OAuth2.0 Authorization Error</h1>
<p><b>Error: {{ error }}</b> {{ '(' + error_description + ')' if error_description else '' }}</p>
{% if args %}
<p>Parameters:</p>
<ul>
{% for key, value in args.items() %}
<li>{{ key }}={{ value }}</li>
{% endfor %}
</ul>
{% endif %}
<hr>
......
{% extends 'base.html' %}
{% extends 'base_narrow.html' %}
{% block body %}
<div class="row mt-2 justify-content-center">
<div class="col-lg-6 col-md-10" style="background: #f7f7f7; box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.3); padding: 30px;">
<div class="text-center">
<img alt="branding logo" src="{{ config.get("BRANDING_LOGO_URL") }}" class="col-lg-8 col-md-12" >
</div>
<div class="col-12">
<h2 class="text-center">{{_('Logout')}}</h2>
</div>
......@@ -16,9 +11,9 @@
</noscript>
<p>{{_('While you successfully logged out of the Single-Sign-On service, you may still be logged in on these services:')}}</p>
<ul>
{% for client in clients if client.logout_urls %}
<li class="client" data-urls='{{ client.logout_urls|tojson }}'>
{{ client.client_id }}
{% for client in clients if client.logout_uris %}
<li class="client" data-urls='{{ client.logout_uris_json }}'>
{{ client.service.name }}
<span class="status-active spinner-border spinner-border-sm d-none" role="status" aria-hidden="true"></span>
<i class="status-success fas fa-check d-none"></i>
<i class="status-failed fas fa-exclamation d-none"></i>
......@@ -39,8 +34,6 @@
</a>
</div>
</div>
</div>
<script>
function logout_services() {
......@@ -60,7 +53,6 @@ function logout_services() {
});
});
p = p.then(function () {
console.log('done', elem);
elem.find('.status-active').addClass('d-none');
elem.find('.status-success').removeClass('d-none');
elem.removeClass('client');
......@@ -68,13 +60,11 @@ function logout_services() {
.catch(function (err) {
elem.find('.status-active').addClass('d-none');
elem.find('.status-failed').removeClass('d-none');
console.log(err);
throw err;
});
all_promises.push(p);
});
Promise.allSettled(all_promises).then(function (results) {
console.log(results);
for (result of results) {
if (result.status == 'rejected')
throw result.reason;
......
......@@ -7,9 +7,9 @@
</div>
{% endif %}
<form action="{{ url_for("role.update", roleid=role.id) }}" method="POST">
<form action="{{ url_for("role.update", roleid=role.id) }}" method="POST" autocomplete="off">
<div class="align-self-center">
<div class="float-sm-right pb-2">
<div class="clearfix pb-2"><div class="float-sm-right">
<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> {{_("Save")}}</button>
<a href="{{ url_for("role.index") }}" class="btn btn-secondary">{{_("Cancel")}}</a>
{% if role.id %}
......@@ -23,7 +23,7 @@
<a href="#" class="btn btn-secondary disabled">{{_("Set as default")}}</a>
<a href="#" class="btn btn-danger disabled"><i class="fa fa-trash" aria-hidden="true"></i> {{_("Delete")}}</a>
{% endif %}
</div>
</div></div>
<ul class="nav nav-tabs pt-2 border-0" id="tablist" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="settings-tab" data-toggle="tab" href="#settings" role="tab" aria-controls="settings" aria-selected="true">{{_("Settings")}}</a>
......@@ -55,7 +55,7 @@
<select class="form-control" id="moderator-group" name="moderator-group" {{ 'disabled' if role.locked }}>
<option value="" class="text-muted">{{_("No Moderator Group")}}</option>
{% for group in groups %}
<option value="{{ group.dn }}" {{ 'selected' if group == role.moderator_group }}>{{ group.name }}</option>
<option value="{{ group.id }}" {{ 'selected' if group == role.moderator_group }}>{{ group.name }}</option>
{% endfor %}
</select>
</div>
......@@ -63,7 +63,7 @@
<span>{{_("Moderators")}}:</span>
<ul class="row">
{% for moderator in role.moderator_group.members %}
<li class="col-12 col-xs-6 col-sm-4 col-md-3 col-lg-2"><a href="{{ url_for("user.show", uid=moderator.uid) }}">{{ moderator.loginname }}</a></li>
<li class="col-12 col-xs-6 col-sm-4 col-md-3 col-lg-2"><a href="{{ url_for("user.show", id=moderator.id) }}">{{ moderator.loginname }}</a></li>
{% endfor %}
</ul>
</div>
......@@ -71,7 +71,7 @@
<span>{{_("Members")}}:</span>
<ul class="row">
{% for member in role.members|sort(attribute='loginname') %}
<li class="col-12 col-xs-6 col-sm-4 col-md-3 col-lg-2"><a href="{{ url_for("user.show", uid=member.uid) }}">{{ member.loginname }}</a></li>
<li class="col-12 col-xs-6 col-sm-4 col-md-3 col-lg-2"><a href="{{ url_for("user.show", id=member.id) }}">{{ member.loginname }}</a></li>
{% endfor %}
</ul>
</div>
......@@ -90,7 +90,7 @@
</thead>
<tbody>
{% for r in roles|sort(attribute="name")|sort(attribute='name') %}
<tr id="include-role-{{ role.id }}">
<tr id="include-role-{{ r.id }}">
<td>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="include-role-{{ r.id }}-checkbox" name="include-role-{{ r.id }}" value="1" aria-label="enabled"
......@@ -108,7 +108,7 @@
</td>
<td>
{% for group in r.groups_effective|sort(attribute='name') %}
<a href="{{ url_for("group.show", gid=group.gid) }}">{{ group.name }}</a>{{ ', ' if not loop.last }}
<a href="{{ url_for("group.show", id=group.id) }}">{{ group.name }}</a>{{ ', ' if not loop.last }}
{% endfor %}
</td>
</tr>
......@@ -131,14 +131,14 @@
</thead>
<tbody>
{% for group in groups|sort(attribute="name") %}
<tr id="group-{{ group.gid }}">
<tr id="group-{{ group.id }}">
<td>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="group-{{ group.gid }}-checkbox" name="group-{{ group.gid }}" value="1" aria-label="enabled" {% if group in role.groups %}checked{% endif %} {{ 'disabled' if role.locked }}>
<input class="form-check-input" type="checkbox" id="group-{{ group.id }}-checkbox" name="group-{{ group.id }}" value="1" aria-label="enabled" {% if group in role.groups %}checked{% endif %} {{ 'disabled' if role.locked }}>
</div>
</td>
<td>
<a href="{{ url_for("group.show", gid=group.gid) }}">
<a href="{{ url_for("group.show", id=group.id) }}">
{{ group.name }}
</a>
</td>
......@@ -147,7 +147,7 @@
</td>
<td>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="group-mfa-{{ group.gid }}-checkbox" name="group-mfa-{{ group.gid }}" value="1" aria-label="enabled" {% if group in role.groups and role.groups[group].requires_mfa %}checked{% endif %} {{ 'disabled' if role.locked }}>
<input class="form-check-input" type="checkbox" id="group-mfa-{{ group.id }}-checkbox" name="group-mfa-{{ group.id }}" value="1" aria-label="enabled" {% if group in role.groups and role.groups[group].requires_mfa %}checked{% endif %} {{ 'disabled' if role.locked }}>
</div>
</td>
</tr>
......
......@@ -4,9 +4,7 @@
<form method="POST" action="{{ url_for("rolemod.update", role_id=role.id) }}">
<div class="float-sm-right pb-2">
{% if config['ENABLE_INVITE'] %}
<a href="{{ url_for("invite.new", **{"role-%d"%role.id: 1}) }}" class="btn btn-primary mr-2"><i class="fa fa-link" aria-hidden="true"></i> {{_('Invite Members')}}</a>
{% endif %}
<button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> {{_('Save')}}</button>
<a href="{{ url_for("rolemod.index") }}" class="btn btn-secondary">{{_('Cancel')}}</a>
</div>
......@@ -52,7 +50,7 @@
<tr>
<td>{{ member.displayname }} ({{ member.loginname }})</td>
<td class="text-right">
<a class="btn btn-danger py-0" href="{{ url_for('rolemod.delete_member', role_id=role.id, member_dn=member.dn) }}">{{_('Remove')}}</a>
<a class="btn btn-danger py-0" href="{{ url_for('rolemod.delete_member', role_id=role.id, member_id=member.id) }}">{{_('Remove')}}</a>
</td>
</tr>
{% endfor %}
......
......@@ -7,7 +7,7 @@
You can later generate new recovery codes and setup your applications and devices again.")}}
</p>
<form class="form" action="{{ url_for('mfa.disable_confirm') }}" method="POST">
<form class="form" action="{{ url_for('selfservice.disable_mfa_confirm') }}" method="POST">
<button type="submit" class="btn btn-danger btn-block">{{_("Disable two-factor authentication")}}</button>
</form>
......
{% extends 'base_narrow.html' %}
{% block body %}
<form action="{{ url_for("selfservice.forgot_password") }}" method="POST">
<div class="col-12">
<h2 class="text-center">{{_("Forgot password")}}</h2>
</div>
<div class="form-group col-12">
<label for="user-loginname">{{_("Login Name")}}</label>
<input type="text" autocomplete="username" class="form-control" id="user-loginname" name="loginname" required="required" tabindex="1">
</div>
<div class="form-group col-12">
<label for="user-mail">{{_("Mail Address")}}</label>
<input type="email" autocomplete="email" class="form-control" id="user-mail" name="mail" required="required" tabindex="2">
</div>
<div class="form-group col-12">
<button type="submit" class="btn btn-primary btn-block" tabindex="3">{{_("Send password reset mail")}}</button>
</div>
</form>
{% endblock %}
Hi {{ user.displayname }},
you have requested to change your mail address. To confirm the change, please visit the following url:
{{ url_for('selfservice.token_mail', token=token, _external=True) }}
{{ url_for('selfservice.verify_email', email_id=email.id, secret=secret, _external=True) }}
**The link is valid for 48h**
......@@ -4,7 +4,7 @@ welcome to the {{ config.ORGANISATION_NAME }} infrastructure! An account was cre
Please visit the following url to set your password:
{{ url_for('selfservice.token_password', token=token, _external=True) }}
{{ url_for('selfservice.token_password', token_id=token.id, token=token.token, _external=True) }}
**The link is valid for 48h**
......
Hi {{ user.displayname }},
you have requested a password reset. To reset your password, visit the following url:
{{ url_for('selfservice.token_password', token=token, _external=True) }}
{{ url_for('selfservice.token_password', token_id=token.id, token=token.token, _external=True) }}
**The link is valid for 48h**
......
......@@ -9,32 +9,122 @@
</div>
{% endif %}
<div class="row mt-3">
<div class="row">
<div class="col-12 col-md-5">
<h5>{{_("Profile")}}</h5>
<p>{{_("Your profile information is used by all services that are integrated into the Single-Sign-On. Your e-mail address is also used for password recovery.")}}</p>
<p>{{_("Changes may take serveral minutes to be visible in all services.")}}</p>
<p>{{_("Your profile information is used by all services that are integrated into the Single-Sign-On.")}}</p>
<p>{{_("Changes may take several minutes to be visible in all services.")}}</p>
</div>
<div class="col-12 col-md-7">
<form class="form" action="{{ url_for("selfservice.update_profile") }}" method="POST">
<div class="form-row">
<div class="form-group col-12 col-md-9">
<div class="form-group">
<label>{{_("Login Name")}}</label>
<input type="text" class="form-control" value="{{ user.loginname }}" readonly>
</div>
</div>
<div class="form-group">
<label>{{_("Display Name")}}</label>
<input type="text" class="form-control" id="user-displayname" name="displayname" value="{{ user.displayname }}">
</div>
<button type="submit" class="btn btn-primary btn-block">{{_("Update Profile")}}</button>
</form>
</div>
</div>
<hr>
<div class="row">
<div class="col-12 col-md-5">
<h5>{{_("E-Mail Addresses")}}</h5>
<p>{{_("Add and delete addresses associated with your account. You will need to verify new addresses by opening a link set to them.")}}</p>
</div>
<div class="col-12 col-md-7">
<form method="POST" action="{{ url_for('selfservice.add_email') }}" class="form mb-2">
<div class="row m-0">
<label class="sr-only" for="new-email-address">{{_("Email")}}</label>
<input type="email" autocomplete="email" class="form-control mb-2 col-12 col-lg-auto mr-2" style="width: 20em;" id="new-email-address" name="address" placeholder="{{_("New E-Mail Address")}}" required>
<button type="submit" class="btn btn-primary mb-2 col">{{_("Add address")}}</button>
</div>
</form>
<table class="table mb-0">
<tbody>
{% for email in user.all_emails|sort(attribute='id') %}
<tr>
<td class="pl-0">
{{ email.address }}
{% if email == user.primary_email %}
<span class="badge badge-primary">{{ _('primary') }}</span>
{% elif not email.verified %}
<span class="badge badge-danger">{{ _('unverified') }}</span>
{% endif %}
</td>
<td class="pt-2 pb-1 pr-0">
<form method="POST" action="{{ url_for('selfservice.delete_email', email_id=email.id) }}" onsubmit='return confirm({{_("Are you sure?")|tojson|e}});'>
<button type="submit" class="btn btn-sm btn-danger float-right ml-1 mb-1"{% if email == user.primary_email %} disabled title="{{ _('Cannot delete primary e-mail address') }}"{% endif %}>{{_("Delete")}}</button>
</form>
{% if not email.verified %}
<a href="{{ url_for('selfservice.retry_email_verification', email_id=email.id) }}" class="btn btn-sm btn-primary float-right mb-1">{{_("Retry verification")}}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<hr>
{% set service_users_with_email_prefs = user.service_users|selectattr('has_email_preferences')|list %}
{% set collapse_email_prefs = service_users_with_email_prefs|length > 2 %}
<div class="row">
<div class="col-12 col-md-5">
<h5>{{_("E-Mail Preferences")}}</h5>
<p>
{{ _("Choose your primary e-mail address and the address password recovery e-mails will be sent to.") }}
{% if service_users_with_email_prefs %}
{{ _("You can also select different addresses for different services.") }}
{% endif %}
</p>
<p>{{ _("Adresses must be verified before you can select them here.") }}</p>
</div>
<div class="col-12 col-md-7">
<form class="form" action="{{ url_for("selfservice.update_email_preferences") }}" method="POST">
<div class="form-group">
<label>{{_("E-Mail Address")}}</label>
<input type="email" class="form-control" id="user-mail" name="mail" value="{{ user.mail }}" required>
<small class="form-text text-muted">
{{_("We will send you a confirmation mail to this address if you change it")}}
</small>
<label>{{_("Primary Address")}}</label>
<select name="primary_email" class="form-control">
{% for email in user.all_emails if email.verified %}
<option value="{{ email.id }}" {{ 'selected' if email == user.primary_email }}>{{ email.address }}</option>
{% endfor %}
</select>
</div>
<button type="submit" class="btn btn-primary btn-block">{{_("Update Profile")}}</button>
<div class="form-group">
<label>{{_("Address for Password Reset E-Mails")}}</label>
<select name="recovery_email" class="form-control">
<option value="primary" {{ 'selected' if not user.recovery_email }}>{{ _('Use primary address') }}</option>
{% for email in user.all_emails if email.verified %}
<option value="{{ email.id }}" {{ 'selected' if email == user.recovery_email }}>{{ email.address }}</option>
{% endfor %}
</select>
</div>
{% for service_user in service_users_with_email_prefs %}
{% if collapse_email_prefs and loop.index == 2 %}
<div id="collapsed-email-prefs">
{% endif %}
<div class="form-group">
<label>{{ _('Address for Service "%(name)s"', name=service_user.service.name) }}</label>
<select name="service_{{ service_user.service.id }}_email" class="form-control">
<option value="primary" {{ 'selected' if not service_user.service_email }}>{{ _('Use primary address') }}</option>
{% for email in user.all_emails if email.verified %}
<option value="{{ email.id }}" {{ 'selected' if email == service_user.service_email }}>{{ email.address }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
{% if collapse_email_prefs %}
</div>
<button type="button" class="btn btn-sm btn-link pl-0 mb-1 showmore" data-target="#collapsed-email-prefs" style="display: none;" aria-expanded="false" aria-controls="collapsed-email-prefs">{{ _("Show more settings ...") }}</button>
{% endif %}
<button type="submit" class="btn btn-primary btn-block">{{_("Update E-Mail Preferences")}}</button>
</form>
</div>
</div>
......@@ -49,13 +139,13 @@
<div class="col-12 col-md-7">
<form class="form" action="{{ url_for("selfservice.change_password") }}" method="POST">
<div class="form-group">
<input type="password" class="form-control" id="user-password1" name="password1" placeholder="{{_("New Password")}}" required>
<input type="password" autocomplete="new-password" class="form-control" id="user-password1" name="password1" placeholder="{{_("New Password")}}" minlength={{ User.PASSWORD_MINLEN }} maxlength={{ User.PASSWORD_MAXLEN }} pattern="{{ User.PASSWORD_REGEX }}" required>
<small class="form-text text-muted">
{{_('At least 8 and at most 256 characters, no other special requirements.')}}
{{ User.PASSWORD_DESCRIPTION|safe }}
</small>
</div>
<div class="form-group">
<input type="password" class="form-control" id="user-password2" name="password2" placeholder="{{_("Repeat Password")}}" required>
<input type="password" autocomplete="new-password" class="form-control" id="user-password2" name="password2" placeholder="{{_("Repeat Password")}}" required>
</div>
<button type="submit" class="btn btn-primary btn-block">{{_("Change Password")}}</button>
</form>
......@@ -77,7 +167,56 @@
{{ _("Two-factor authentication is currently <strong>disabled</strong>.")|safe }}
{% endif %}
</p>
<a class="btn btn-primary btn-block" href="{{ url_for('mfa.setup') }}">{{_("Manage two-factor authentication")}}</a>
<a class="btn btn-primary btn-block" href="{{ url_for('selfservice.setup_mfa') }}">{{_("Manage two-factor authentication")}}</a>
</div>
</div>
<hr>
<div class="row mt-3">
<div class="col-12 col-md-5">
<h5>{{_("Active Sessions")}}</h5>
<p>{{_("Your active login sessions on this device and other devices.")}}</p>
<p>{{_("Revoke a session to log yourself out on another device. Note that this is limited to the Single-Sign-On session and <b>does not affect login sessions on services.</b>")}}</p>
</div>
<div class="col-12 col-md-7">
<table class="table">
<thead>
<tr>
<th scope="col">{{_("Last used")}}</th>
<th scope="col">{{_("Device")}}</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr>
<td>{{_("Just now")}}</td>
<td>{{ request.session.user_agent_browser }} on {{ request.session.user_agent_platform }} ({{ request.session.ip_address }})</td>
<td></td>
</tr>
{% for session in user.sessions|sort(attribute='last_used', reverse=True) if not session.expired and session != request.session %}
<tr>
<td>
{% set last_used_rel = session.last_used - datetime.utcnow() %}
{% if -last_used_rel.total_seconds() <= 60 %}
{{_("Just now")}}
{% else %}
{{ last_used_rel|timedeltaformat(add_direction=True, granularity='minute') }}
{% endif %}
</td>
<td>{{ session.user_agent_browser }} on {{ session.user_agent_platform }} ({{ session.ip_address }})</td>
<td>
{% if session != request.session %}
<form action="{{ url_for("selfservice.revoke_session", session_id=session.id) }}" method="POST" onsubmit='return confirm({{_("Are you sure?")|tojson|e}});'>
<button type="submit" class="btn btn-sm btn-danger float-right">{{_("Revoke")}}</button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
......@@ -88,15 +227,12 @@
<h5>{{_("Roles")}}</h5>
<p>{{_("Aside from a set of base permissions, your roles determine the permissions of your account.")}}</p>
{% if config['SERVICES'] %}
<p>{{_("See <a href=\"%(services_url)s\">Services</a> for an overview of your current permissions.", services_url=url_for('services.index'))}}</p>
<p>{{_("See <a href=\"%(services_url)s\">Services</a> for an overview of your current permissions.", services_url=url_for('service.overview'))}}</p>
{% endif %}
</div>
<div class="col-12 col-md-7">
{% if config['ENABLE_INVITE'] %}
<p>{{_("Administrators and role moderators can invite you to new roles.")}}</p>
{% else %}
<p>{{_("Administrators can add new roles to your account.")}}</p>
{% endif %}
<table class="table">
<thead>
<tr>
......@@ -115,11 +251,9 @@
</td>
<td>{{ role.description }}</td>
<td>
{% if config['ENABLE_ROLESELFSERVICE'] %}
<form action="{{ url_for("selfservice.leave_role", roleid=role.id) }}" method="POST" onsubmit='return confirm({{_("Are you sure?")|tojson|e}});'>
<button type="submit" class="btn btn-sm btn-danger float-right">{{_("Leave")}}</button>
</form>
{% endif %}
</td>
</tr>
{% endfor %}
......@@ -130,7 +264,20 @@
{% endif %}
</tbody>
</table>
</div>
</div>
<script>
$(".showmore").each(function () {
$(this).show()
$($(this).data("target")).hide()
})
$(".showmore").on("click", function () {
$(this).slideUp(200)
$(this).prop("ariaExpanded", true)
$($(this).data("target")).slideDown()
})
</script>
{% endblock %}
{% extends 'base_narrow.html' %}
{% block body %}
<form action="{{ url_for("selfservice.token_password", token_id=token.id, token=token.token) }}" method="POST" onInput="password2.setCustomValidity(password1.value != password2.value ? 'Passwords do not match.' : '') ">
<div class="col-12">
<h2 class="text-center">{{_("Reset password")}}</h2>
</div>
<div class="form-group col-12">
<label for="user-password1">{{_("New Password")}}</label>
<input type="password" autocomplete="new-password" class="form-control" id="user-password1" name="password1" tabindex="2" minlength={{ User.PASSWORD_MINLEN }} maxlength={{ User.PASSWORD_MAXLEN }} pattern="{{ User.PASSWORD_REGEX }}" required>
<small class="form-text text-muted">
{{ User.PASSWORD_DESCRIPTION|safe }}
</small>
</div>
<div class="form-group col-12">
<label for="user-password2">{{_("Repeat Password")}}</label>
<input type="password" autocomplete="new-password" class="form-control" id="user-password2" name="password2" tabindex="3" required>
</div>
<div class="form-group col-12">
<button type="submit" class="btn btn-primary btn-block" tabindex="3">{{_("Set password")}}</button>
</div>
</form>
{% endblock %}