{% extends "base.html" %} {% from "components/ui.html" import action, ago, anywhere_tag, breadcrumb, card, csrf_field, danger_card, empty, fact, facts, icon, related, rights_tags, row_form, secret_card, tabs, tick, tick_all with context %} {% from "components/audit.html" import activity_rows %} {% from "components/grants.html" import grant_editor, grants_of, role_checks, role_tags, sending_of, service_editor, service_of %} {% block title %}{{ user.name }} · {{ APP_NAME }}{% endblock %} {% block heading %}{{ user.name }}{% endblock %} {% block subheading %}{{ breadcrumb([("Users", "/ui/users"), (user.name, None)]) }}{% endblock %} {% block content %} {% if new_token %} {% call secret_card("New token", new_token) %}Copy it now. Only its hash is kept. It cannot be shown again.{% endcall %} {% endif %} {% if new_password %} {% call secret_card("One-time password", new_password) %}Pass it to {{ user.name }} over a safe channel. It must be changed at the first sign-in.{% endcall %} {% endif %} {% set state %}{% if is_me %}you {% endif %}{% if not user.ui_sign_in %}API only {% endif %}{% if user.disabled %}disabled{% else %}active{% endif %}{% endset %} {{ tabs(tabs_shown, tab, "/ui/users/" ~ user.id) }} {% if tab == "rights" %} {% call facts("User", tags=state) %} {% call fact("Roles") %}{{ role_tags(user.roles) }}{% endcall %} {% call fact("Service rights") %}{{ service_of(user.service) }}{% endcall %} {% call fact("Grants") %}{{ grants_of(user.grants, names) }}{% endcall %} {{ fact("Id", user.id, cls="mono") }} {% endcall %} {% call card("Effective rights", meta="The rights of the user and of its roles together" ~ (". They apply once the user is enabled again" if user.disabled else "") ~ ".", tight=True) %} {% if effective.accounts %}
{% for row in effective.accounts %} {% endfor %}
AccountMaySending
{{ row.email }}{% for warning in row.warnings %}{{ anywhere_tag() }}{% endfor %} {{ rights_tags(row.groups, row.operations) }} {{ sending_of(row.sending) }}
{% else %}

No rights on any account you can see.

{% endif %} {% if effective.groups or effective.operations %}
Of the service
{{ rights_tags(effective.groups, effective.operations) }}
{% endif %} {% endcall %} {% if can_update %}
{{ csrf_field() }} {% call card("Change", meta="You can hand out only rights you hold yourself.") %}
{% if not is_me %}
Access
{% endif %}
{% if role_choices %}{{ role_checks(role_choices, typed.roles if typed else user.roles) }}{% endif %}
Service rights {{ service_editor(service, service_names, service_ops) }}
Grants {{ grant_editor(rows, account_choices, groups, group_ops, open=typed) }}
{% endcall %}
{% endif %} {% elif tab == "access" %} {% call facts("Sign-in", tags=state) %} {% call fact("To the UI") %}{% if not user.ui_sign_in %}off: an API user, tokens only{% elif sign_in.must_change %}with a password set for it, to change at the next sign-in{% elif sign_in.has_password %}with a password{% else %}not possible: no password yet{% endif %}{% if is_me and user.ui_sign_in %} · Change your password{% endif %}{% endcall %} {% if user.ui_sign_in %}{% call fact("Second factor") %}{% if sign_in.second_factor %}on a code of an authenticator app after the password{% else %}off{% endif %}{% if is_me %} · {% if sign_in.second_factor %}Your devices{% else %}Set up a second factor{% endif %}{% endif %}{% endcall %}{% endif %} {{ fact("Last sign-in", sign_in.last_sign_in_at | when) }} {% endcall %} {% if not is_me and can_set_password and user.ui_sign_in %} {% call card("Password", meta="Whoever sets a password can sign in as that user.") %}
{{ csrf_field() }} The service makes one and shows it once.
{{ csrf_field() }}
{{ user.name }} must change it at the next sign-in.
{% endcall %} {% endif %} {% if not is_me and factor and factor.totp %} {% call related("Second factor", count=factor.totp | length, meta="authenticator apps (TOTP), " ~ factor.recovery_codes_left ~ " recovery codes left. Only " ~ user.name ~ " adds one.") %}
{% for device in factor.totp %} {% endfor %}
Authenticator appAddedLast usedActions
{{ device.name }} {{ ago(device.created_at) }} {{ ago(device.last_used_at) }} {% if can_remove_device %}{{ action("/ui/users/" ~ user.id ~ "/second-factor/totp/" ~ device.id ~ "/remove", "Remove", confirm="Remove the device " ~ device.name ~ " of " ~ user.name ~ "? Its codes work no more. The user's sessions end.", cls="small danger", icon="trash", alone=True, name="Remove " ~ device.name) }}{% endif %}
{% if can_remove_factor %}
Removing every device lets {{ user.name }} sign in with the password alone, e.g. after every device is lost. Its sessions end. {{ action("/ui/users/" ~ user.id ~ "/second-factor/remove", "Remove every device", confirm="Remove every device of the second factor of " ~ user.name ~ "? It signs in with the password alone then.", cls="danger", icon="trash") }}
{% endif %} {% endcall %} {% endif %} {% if can_list_tokens %} {% set active = tokens | selectattr(1, "equalto", "active") | list %} {% set batch = can_revoke and active | length > 1 %} {% call related("Tokens", count=tokens | length, meta="for the API and the MCP server", add=("New token" if can_create_token else None), add_form="new-token", add_open=not tokens or typed_token) %} {% if batch %}
{{ csrf_field() }} Ticked tokens
{% endif %}
{% if batch %}{% endif %} {% set columns = 7 if batch else 6 %} {% if can_create_token %} {% call row_form("new-token", columns, open=not tokens or typed_token) %} {{ csrf_field() }}
{% if tokens %}{% endif %}
{% endcall %} {% endif %} {% for token, state in tokens %} {% if batch %}{% endif %} {% endfor %}
{{ tick_all("tokens-batch", "token") }}NameCreatedExpiresLast usedStateActions
{% if state == "active" %}{{ tick("tokens-batch", "token", token.id, "Tick " ~ token.name) }}{% endif %}{{ token.name }}{{ token.id[:16] }}… {{ ago(token.created_at) }} {{ token.expires_at | when }} {{ ago(token.last_used_at) }} {% if state == "active" %}active{% else %}{{ state }}{% endif %} {% if can_revoke and state == "active" %}{{ action("/ui/users/" ~ user.id ~ "/tokens/" ~ token.id ~ "/revoke", "Revoke", confirm="Revoke the token " ~ token.name ~ "? Whoever uses it is locked out at once.", cls="small danger", icon="slash-circle", alone=True, name="Revoke " ~ token.name) }}{% endif %}
{% if not tokens %}{{ empty("No token yet.") }}{% endif %}
{% endcall %} {% endif %} {% elif tab == "activity" %} {% if activity is not none %} {% call related("Recent activity", meta="what this user did, newest first") %}
{% if activity %}{{ activity_rows(activity, outcomes, False, who=False) }}{% else %}{{ empty("Nothing in the audit.") }}{% endif %}
All its activity · What was done to it
{% endcall %} {% endif %} {% endif %} {% if can_delete %} {{ danger_card("Delete", "Deletes the user and all its tokens. What it sent stays in the send audit.", "/ui/users/" ~ user.id ~ "/delete", "Delete user", "Delete " ~ user.name ~ " and its tokens?") }} {% endif %} {% endblock %}