acme-proxy-admin 0.6.1

The operation layer and web admin panel of acme-proxy (internal crate, no semver promise)
Documentation
{#- One order, its authorizations and their challenges, plus revoke and delete.

    The swap target of both order mutations. `detail` is
    `admin::render_order_detail_json` verbatim: `{order, authorizations}`. -#}
<div id="order-card">
  {% include "partials/_flash.html" %}

  <div class="panel">
    <dl class="fields">
      <dt>Order</dt><dd><code>{{ detail.order.id }}</code></dd>
      <dt>Profile</dt><dd><code>{{ detail.order.profile }}</code></dd>
      <dt>Account</dt>
      <dd><a href="/ui/accounts/{{ detail.order.accountId }}"><code>{{ detail.order.accountId }}</code></a></dd>
      <dt>Status</dt>
      <dd>
        <span class="badge {{ detail.order.status }}">{{ detail.order.status }}</span>
        {% if detail.order.revokedAt %}<span class="badge revoked">revoked</span>{% endif %}
      </dd>
      <dt>Identifiers</dt>
      <dd class="wrap-anywhere">
        {%- for identifier in detail.order.identifiers -%}
          {% if not loop.first %}, {% endif %}<code>{{ identifier.value }}</code>
        {%- endfor -%}
      </dd>
      <dt>Created</dt><dd>{{ detail.order.createdAt }}</dd>
      <dt>Expires</dt><dd>{{ detail.order.expires }}</dd>
      {% if detail.order.notBefore %}<dt>Not before</dt><dd>{{ detail.order.notBefore }}</dd>{% endif %}
      {% if detail.order.notAfter %}<dt>Not after</dt><dd>{{ detail.order.notAfter }}</dd>{% endif %}
      {% if detail.order.replaces %}
        <dt>Replaces</dt><dd class="wrap-anywhere"><code>{{ detail.order.replaces }}</code></dd>
      {% endif %}
      {#- The issued leaf, described three ways. `certSerial` is what an abuse
          report or an audit-trail search (`/api/audit?certSerial=`) is keyed
          on, so it belongs on the one page that names the order it came from;
          `certNotAfter` is the leaf's own expiry, which is a different date
          from the `Not after` above (that one is the window the client asked
          for). Both are absent on an order that never issued. -#}
      {% if detail.order.certSerial %}
        <dt>Serial</dt>
        <dd class="wrap-anywhere"><code>{{ detail.order.certSerial }}</code></dd>
      {% endif %}
      {#- The trail for this order, and -- once issued -- for its certificate by
          serial, which also finds a revocation attempt made by somebody who
          only held the certificate and never the order. -#}
      <dt>Audit trail</dt>
      <dd>
        <a href="/ui/audit?orderId={{ detail.order.id }}">Rows for this order</a>
        {%- if detail.order.certSerial %}
          · <a href="/ui/audit?certSerial={{ detail.order.certSerial }}">Rows for this certificate</a>
        {%- endif %}
      </dd>
      {% if detail.order.certNotAfter %}
        <dt>Certificate expires</dt><dd>{{ detail.order.certNotAfter }}</dd>
      {% endif %}
      {#- The PEM, not `detail.order.certificate`, which is the ACME URL: that
          is reachable only by signed POST-as-GET, so a browser handed it gets
          nothing and printing it was a dead string. -#}
      {% if detail.order.certificatePem %}
        <dt>Certificate</dt>
        <dd>
          <pre class="pem">{{ detail.order.certificatePem }}</pre>
          <a class="button" href="/ui/orders/{{ detail.order.id }}/chain.pem"
             download>Download chain</a>
        </dd>
      {% endif %}
      {% if detail.order.revokedAt %}
        <dt>Revoked</dt>
        <dd>
          {{ detail.order.revokedAt }}
          {% if detail.order.revocationReason is defined %}
            <span class="muted">(reason {{ detail.order.revocationReason }})</span>
          {% endif %}
        </dd>
      {% endif %}
      {% if detail.order.error %}
        <dt>Error</dt>
        <dd class="wrap-anywhere"><code>{{ detail.order.error.detail | default(detail.order.error.type) }}</code></dd>
      {% endif %}
    </dl>
  </div>

  <div class="panel">
    <h2>Authorizations</h2>
    {% for authz in detail.authorizations %}
      <div class="panel">
        <dl class="fields">
          <dt>Identifier</dt>
          <dd>
            <code>{% if authz.wildcard %}*.{% endif %}{{ authz.identifier.value }}</code>
          </dd>
          <dt>Status</dt><dd><span class="badge {{ authz.status }}">{{ authz.status }}</span></dd>
          <dt>Expires</dt><dd>{{ authz.expires }}</dd>
        </dl>
        <div class="table-scroll">
          <table>
            <thead>
              <tr><th>Challenge</th><th>Status</th><th>Token</th><th>Validated</th></tr>
            </thead>
            <tbody>
              {% for challenge in authz.challenges %}
                <tr>
                  <td><code>{{ challenge.type }}</code></td>
                  <td><span class="badge {{ challenge.status }}">{{ challenge.status }}</span></td>
                  <td class="wrap-anywhere"><code>{{ challenge.token }}</code></td>
                  <td class="small">
                    {%- if challenge.validated -%}
                      {{ challenge.validated }}
                    {%- else -%}
                      <span class="muted">—</span>
                    {%- endif -%}
                  </td>
                </tr>
              {% else %}
                <tr><td class="empty" colspan="4">No challenge.</td></tr>
              {% endfor %}
            </tbody>
          </table>
        </div>
      </div>
    {% else %}
      <p class="muted">This order has no authorization.</p>
    {% endfor %}
  </div>

  {#- Hidden below the operator tier; see `pages::fragment_context`. -#}
  {% if can_write %}
  <div class="panel">
    <h2>Danger zone</h2>
    {#- Revocation is not confirm-gated in the CLI because it only ever
        tightens trust; here it still asks, because a browser click is easier
        to make by accident than a typed command. -#}
    <form class="row" hx-post="/ui/orders/{{ detail.order.id }}/revoke"
          hx-target="#order-card"
          hx-confirm="Revoke this certificate? The CA's CRL is regenerated immediately.">
      <div class="field">
        <label for="reason">Revocation reason (RFC 5280 §5.3.1)</label>
        <select id="reason" name="reason">
          <option value="">0 — unspecified</option>
          <option value="1">1 — key compromise</option>
          <option value="2">2 — CA compromise</option>
          <option value="3">3 — affiliation changed</option>
          <option value="4">4 — superseded</option>
          <option value="5">5 — cessation of operation</option>
          <option value="6">6 — certificate hold</option>
          <option value="9">9 — privilege withdrawn</option>
        </select>
      </div>
      <div>
        <button type="submit" class="danger"
                {% if detail.order.revokedAt or detail.order.status != "valid" %}disabled{% endif %}>
          Revoke
        </button>
      </div>
    </form>
    <div class="actions">
      {#- Success answers with a redirect, since the page this button lives on
          is the thing being deleted; a refusal answers with this card, hence
          the target. Disabled while the certificate is live -- the handler
          refuses it anyway. -#}
      <button class="danger"
              hx-delete="/ui/orders/{{ detail.order.id }}"
              hx-target="#order-card"
              {% if live_certificate %}disabled{% endif %}
              hx-confirm="Delete this order and its {{ detail.authorizations | length }} authorization(s)? This cannot be undone.">
        Delete
      </button>
    </div>
    {% if live_certificate %}
      <p class="small">
        Cannot be deleted while its certificate is live — this order is the
        only record of it, and without it the certificate could never be
        revoked. Revoke it first, or wait for it to expire.
      </p>
    {% endif %}
  </div>
  {% endif %}
</div>