mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-10 04:06:47 +00:00
The API Tokens tab gets a "Last used" column, so an unused token can be told from one a script still needs. The token's last use is written when it authenticates, at most once a minute (accounts.auth.TokenAuthentication, knox's class with that one addition; the API schema describes it as knox's). A user has at most ten tokens; a new one is refused until one is deleted. Expired tokens do not count, and are deleted when a new token is made.
149 lines
No EOL
6.8 KiB
HTML
149 lines
No EOL
6.8 KiB
HTML
{% extends "base.html" %}
|
|
{% load i18n %}
|
|
{% load django_bootstrap5 %}
|
|
{% load bootstrap_icons %}
|
|
{% load tags_fingerprint %}
|
|
|
|
{% block title %}{% trans "Profile" %}: {{ request.user.first_name }} {{ request.user.last_name}}{% endblock %}
|
|
|
|
{% block page_heading %}{% trans "Profile" %}: {{ request.user.first_name }} {{ request.user.last_name}}{% endblock page_heading %}
|
|
|
|
{% block content %}
|
|
<ul class="nav nav-tabs">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" data-bs-toggle="tab" data-bs-target="#editprofile" type="button" role="tab" aria-controls="editprofile" aria-selected="true">
|
|
{% trans "Edit Profile" %}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#sshkeys" type="button" role="tab" aria-controls="sshkeys" aria-selected="false">
|
|
{% trans "SSH Keys" %}
|
|
</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" data-bs-toggle="tab" data-bs-target="#apitokens" type="button" role="tab" aria-controls="apitokens" aria-selected="false">
|
|
{% trans "API Tokens" %}
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
<div class="tab-content">
|
|
<div class="tab-pane tab-pane-bordered active" id="editprofile">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form method="post" action="" role="form" aria-label="Edit user info form">
|
|
{% csrf_token %}
|
|
{% bootstrap_form profile_form layout='horizontal' %}
|
|
{% if perms.accounts.change_password %}
|
|
<a href="{% url 'accounts:change_password' %}" class="btn btn-primary">
|
|
{% bs_icon 'lock' %} {% trans "Change Password" %}
|
|
</a>
|
|
{% endif %}
|
|
<div class="mb-0 float-end">
|
|
<button type="submit" class="btn btn-primary">
|
|
{% bs_icon 'pencil' %} {% trans "Update" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tab-pane tab-pane-bordered fade" id="sshkeys">
|
|
{% if publickeys %}
|
|
<div class="col-lg-12">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<tbody class="text-center">
|
|
{% for key in publickeys %}
|
|
<tr>
|
|
<td>{{ key.keyname }} ({% ssh_to_fingerprint key.keypublic %})</td>
|
|
<td>
|
|
<a href="{% url 'accounts:ssh_key_delete' key.id %}" title="{% trans "Delete" %}" class="btn btn-sm btn-secondary">
|
|
{% bs_icon 'trash' %}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
{%trans "Add SSH Key" %}
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="post" action="{% url 'accounts:ssh_key_create' %}" role="form" aria-label="Add key to user form">
|
|
{% csrf_token %}
|
|
{% bootstrap_form ssh_key_form layout='horizontal' %}
|
|
<div class="mb-0 float-end">
|
|
<button type="submit" class="btn btn-primary">
|
|
{% bs_icon 'plus-circle-fill' %} {% trans "Add" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tab-pane tab-pane-bordered fade" id="apitokens">
|
|
<p class="text-body-secondary">{% blocktrans %}A token lets a script or a tool use the REST API as you, without your password or OTP: send it as <code>Authorization: Token <token></code>. A password change does not delete tokens: delete a token you no longer use, or one that may have leaked.{% endblocktrans %}</p>
|
|
{% if api_tokens %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">{% trans "Name" %}</th>
|
|
<th scope="col">{% trans "Starts with" %}</th>
|
|
<th scope="col">{% trans "Created" %}</th>
|
|
<th scope="col">{% trans "Expires" %}</th>
|
|
<th scope="col">{% trans "Last used" %}</th>
|
|
<th scope="col"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for token in api_tokens %}
|
|
<tr>
|
|
<td>{{ token.name }}</td>
|
|
<td><code>{{ token.token_key }}</code></td>
|
|
<td>{{ token.created|date:"Y-m-d H:i" }}</td>
|
|
<td>{% if token.expiry %}{{ token.expiry|date:"Y-m-d H:i" }}{% else %}{% trans "never" %}{% endif %}</td>
|
|
<td>{% if token.last_used %}{{ token.last_used|date:"Y-m-d H:i" }}{% else %}{% trans "never" %}{% endif %}</td>
|
|
<td class="text-end">
|
|
<a href="{% url 'accounts:api_token_delete' token.token_key %}" title="{% trans "Delete" %}" class="btn btn-sm btn-secondary">
|
|
{% bs_icon 'trash' %}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endif %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
{% trans "Create an API token" %}
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="post" action="{% url 'accounts:api_token_create' %}" role="form" aria-label="{% trans "Create an API token" %}">
|
|
{% csrf_token %}
|
|
{% bootstrap_form api_token_form layout='horizontal' %}
|
|
<div class="mb-0 float-end">
|
|
<button type="submit" class="btn btn-primary">
|
|
{% bs_icon 'plus-circle-fill' %} {% trans "Create" %}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
{% block script %}
|
|
<script>
|
|
// a link to a tab (#apitokens) opens it
|
|
var linked = location.hash.length > 1 && document.querySelector('[data-bs-target="#' + CSS.escape(location.hash.slice(1)) + '"]');
|
|
if (linked) {
|
|
bootstrap.Tab.getOrCreateInstance(linked).show();
|
|
}
|
|
</script>
|
|
{% endblock %} |