atr/templates/keys-review.html (185 lines of code) (raw):
{% extends "layouts/base.html" %}
{% block title %}
Manage keys ~ ATR
{% endblock title %}
{% block description %}
Review your keys.
{% endblock description %}
{% block content %}
<h1>Manage keys</h1>
<p class="mb-4">
<a href="#your-public-keys" class="btn btn-sm btn-secondary me-3">Your public keys</a>
<a href="#your-committee-keys" class="btn btn-sm btn-secondary">Your committee's keys</a>
</p>
<h2 id="your-public-keys">Your public keys</h2>
<p>Review your public keys used for signing release artifacts.</p>
<div>
<p>
Welcome, <strong>{{ asf_id }}</strong>! You are authenticated as an ASF committer.
</p>
</div>
<div class="d-flex gap-3 mb-4">
<a href="{{ as_url(routes.keys.add) }}" class="btn btn-outline-primary">Add your GPG key</a>
<a href="{{ as_url(routes.keys.ssh_add) }}"
class="btn btn-outline-primary">Add your SSH key</a>
</div>
<h3>GPG keys</h3>
{% if user_keys %}
<div class="mb-5 p-4 bg-light rounded">
<div class="d-grid gap-4">
{% for key in user_keys %}
<div class="card p-3 border">
<table class="mb-0">
<tbody>
<tr>
<th class="p-2 text-dark">Fingerprint</th>
<td class="text-break">{{ key.fingerprint }}</td>
</tr>
<tr>
<th class="p-2 text-dark">Type</th>
<td class="text-break">{{ algorithms[key.algorithm] }} ({{ key.length }} bits)</td>
</tr>
<tr>
<th class="p-2 text-dark">Created</th>
<td class="text-break">{{ key.created.strftime("%Y-%m-%d %H:%M:%S") }}</td>
</tr>
<tr>
<th class="p-2 text-dark">Expires</th>
<td class="text-break">
{% if key.expires %}
{% set days_until_expiry = (key.expires - now).days %}
{% if days_until_expiry < 0 %}
<span class="text-danger fw-bold">
{{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
<span class="badge bg-danger text-white ms-2">Expired</span>
</span>
{% elif days_until_expiry <= 30 %}
<span class="text-warning fw-bold">
{{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
<span class="badge bg-warning text-dark ms-2">Expires in {{ days_until_expiry }} days</span>
</span>
{% else %}
{{ key.expires.strftime("%Y-%m-%d %H:%M:%S") }}
{% endif %}
{% else %}
Never
{% endif %}
</td>
</tr>
<tr>
<th class="p-2 text-dark">User ID</th>
<td class="text-break">{{ key.declared_uid or 'Not specified' }}</td>
</tr>
<tr>
<th class="p-2 text-dark">Associated PMCs</th>
<td class="text-break">
{% if key.committees %}
{{ key.committees|map(attribute='name') |join(', ') }}
{% else %}
No PMCs associated
{% endif %}
</td>
</tr>
</tbody>
</table>
<!-- TODO: We could link to a downloadable version of the key instead -->
<details class="mt-3 p-3 bg-light rounded">
<summary class="fw-bold">View whole key</summary>
<pre class="mt-3">{{ key.ascii_armored_key }}</pre>
</details>
<form method="post"
action="{{ as_url(routes.keys.delete) }}"
class="mt-3"
onsubmit="return confirm('Are you sure you want to delete this GPG key?');">
{{ delete_form.hidden_tag() }}
<input type="hidden" name="fingerprint" value="{{ key.fingerprint }}" />
{{ delete_form.submit(class_='btn btn-danger', value='Delete key') }}
</form>
</div>
{% endfor %}
</div>
</div>
{% else %}
<p>
<strong>You haven't added any personal GPG keys yet.</strong>
</p>
{% endif %}
<h3>SSH keys</h3>
{% if user_ssh_keys %}
<div class="mb-5 p-4 bg-light rounded">
<div class="d-grid gap-4">
{% for key in user_ssh_keys %}
<div id="ssh-key-{{ key.fingerprint }}" class="card p-3 border">
<table class="mb-0">
<tbody>
<tr>
<th class="p-2 text-dark">Fingerprint</th>
<td class="text-break">{{ key.fingerprint }}</td>
</tr>
<tr>
<th class="p-2 text-dark">Type</th>
<td class="text-break">{{ key.key.split()[0] }}</td>
</tr>
</tbody>
</table>
<details class="mt-3 p-3 bg-light rounded">
<summary class="fw-bold">View whole key</summary>
<pre class="mt-3">{{ key.key }}</pre>
</details>
<form method="post"
action="{{ as_url(routes.keys.delete) }}"
class="mt-3"
onsubmit="return confirm('Are you sure you want to delete this SSH key?');">
{{ delete_form.hidden_tag() }}
<input type="hidden" name="fingerprint" value="{{ key.fingerprint }}" />
{{ delete_form.submit(class_='btn btn-danger', value='Delete key') }}
</form>
</div>
{% endfor %}
</div>
</div>
{% else %}
<p>
<strong>You haven't added any SSH keys yet.</strong>
</p>
{% endif %}
<h2 id="your-committee-keys">Your committee's keys</h2>
<div class="mb-4">
<a href="{{ as_url(routes.keys.upload) }}"
class="btn btn-outline-primary">Upload a KEYS file</a>
</div>
{% for committee in committees %}
<h3 class="mt-3">{{ committee.display_name }}</h3>
{% if committee.public_signing_keys %}
<div class="table-responsive mb-2">
<table class="table border table-striped table-hover table-sm">
<thead>
<tr>
<th class="px-2" scope="col">Fingerprint</th>
<th class="px-2" scope="col">Email</th>
<th class="px-2" scope="col">Apache UID</th>
</tr>
</thead>
<tbody>
{% for key in committee.public_signing_keys %}
<tr>
<td class="text-break font-monospace px-2">
<a href="{{ as_url(routes.keys.show_gpg_key, fingerprint=key.fingerprint) }}">{{ key.fingerprint[:16]|upper }}</a>
</td>
<td class="text-break px-2">{{ key.declared_uid or 'Not specified' }}</td>
<td class="text-break px-2">{{ key.apache_uid }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<form method="post"
action="{{ as_url(routes.keys.update_committee_keys, committee_name=committee.name) }}"
class="mb-4 d-inline-block">
{{ update_committee_keys_form.hidden_tag() }}
{{ update_committee_keys_form.submit(class_='btn btn-sm btn-outline-secondary') }}
</form>
{% else %}
<p class="mb-4">No keys uploaded for this committee yet.</p>
{% endif %}
{% endfor %}
{% endblock content %}