mirror of
https://github.com/simonw/datasette.git
synced 2026-06-05 00:26:57 +02:00
Fixed two bugs preventing the create token UI and tests from working: 1. **Template variable mismatch**: create_token.html was using undefined variables - Changed `all_permissions` → `all_actions` - Changed `database_permissions` → `database_actions` - Changed `resource_permissions` → `child_actions` These match what CreateTokenView.shared() actually provides to the template. 2. **Action abbreviation bug**: app.py:685 was checking the wrong dictionary - Changed `self.permissions.get(action)` → `self.actions.get(action)` The abbreviate_action() function needs to look up Action objects (which have the `abbr` attribute), not Permission objects. This bug prevented action names like "view-instance" from being abbreviated to "vi" in token restrictions. Refs #2534 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
124 lines
3.9 KiB
HTML
124 lines
3.9 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Create an API token{% endblock %}
|
|
|
|
{% block extra_head %}
|
|
<style type="text/css">
|
|
#restrict-permissions label {
|
|
display: inline;
|
|
width: 90%;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
|
|
<h1>Create an API token</h1>
|
|
|
|
<p>This token will allow API access with the same abilities as your current user, <strong>{{ request.actor.id }}</strong></p>
|
|
|
|
{% if token %}
|
|
<div>
|
|
<h2>Your API token</h2>
|
|
<form>
|
|
<input type="text" class="copyable" style="width: 40%" value="{{ token }}">
|
|
<span class="copy-link-wrapper"></span>
|
|
</form>
|
|
<!--- show token in a <details> -->
|
|
<details style="margin-top: 1em">
|
|
<summary>Token details</summary>
|
|
<pre>{{ token_bits|tojson(4) }}</pre>
|
|
</details>
|
|
</div>
|
|
<h2>Create another token</h2>
|
|
{% endif %}
|
|
|
|
{% if errors %}
|
|
{% for error in errors %}
|
|
<p class="message-error">{{ error }}</p>
|
|
{% endfor %}
|
|
{% endif %}
|
|
|
|
<form class="core" action="{{ urls.path('-/create-token') }}" method="post">
|
|
<div>
|
|
<div class="select-wrapper" style="width: unset">
|
|
<select name="expire_type">
|
|
<option value="">Token never expires</option>
|
|
<option value="minutes">Expires after X minutes</option>
|
|
<option value="hours">Expires after X hours</option>
|
|
<option value="days">Expires after X days</option>
|
|
</select>
|
|
</div>
|
|
<input type="text" name="expire_duration" style="width: 10%">
|
|
<input type="hidden" name="csrftoken" value="{{ csrftoken() }}">
|
|
<input type="submit" value="Create token">
|
|
|
|
<details style="margin-top: 1em" id="restrict-permissions">
|
|
<summary style="cursor: pointer;">Restrict actions that can be performed using this token</summary>
|
|
<h2>All databases and tables</h2>
|
|
<ul>
|
|
{% for permission in all_actions %}
|
|
<li><label><input type="checkbox" name="all:{{ permission }}"> {{ permission }}</label></li>
|
|
{% endfor %}
|
|
</ul>
|
|
|
|
{% for database in database_with_tables %}
|
|
<h2>All tables in "{{ database.name }}"</h2>
|
|
<ul>
|
|
{% for permission in database_actions %}
|
|
<li><label><input type="checkbox" name="database:{{ database.encoded }}:{{ permission }}"> {{ permission }}</label></li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% endfor %}
|
|
<h2>Specific tables</h2>
|
|
{% for database in database_with_tables %}
|
|
{% for table in database.tables %}
|
|
<h3>{{ database.name }}: {{ table.name }}</h3>
|
|
<ul>
|
|
{% for permission in child_actions %}
|
|
<li><label><input type="checkbox" name="resource:{{ database.encoded }}:{{ table.encoded }}:{{ permission }}"> {{ permission }}</label></li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% endfor %}
|
|
{% endfor %}
|
|
</details>
|
|
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
var expireDuration = document.querySelector('input[name="expire_duration"]');
|
|
expireDuration.style.display = 'none';
|
|
var expireType = document.querySelector('select[name="expire_type"]');
|
|
function showHideExpireDuration() {
|
|
if (expireType.value) {
|
|
expireDuration.style.display = 'inline';
|
|
expireDuration.setAttribute("placeholder", expireType.value.replace("Expires after X ", ""));
|
|
} else {
|
|
expireDuration.style.display = 'none';
|
|
}
|
|
}
|
|
showHideExpireDuration();
|
|
expireType.addEventListener('change', showHideExpireDuration);
|
|
var copyInput = document.querySelector(".copyable");
|
|
if (copyInput) {
|
|
var wrapper = document.querySelector(".copy-link-wrapper");
|
|
var button = document.createElement("button");
|
|
button.className = "copyable-copy-button";
|
|
button.setAttribute("type", "button");
|
|
button.innerHTML = "Copy to clipboard";
|
|
button.onclick = (ev) => {
|
|
ev.preventDefault();
|
|
copyInput.select();
|
|
document.execCommand("copy");
|
|
button.innerHTML = "Copied!";
|
|
setTimeout(() => {
|
|
button.innerHTML = "Copy to clipboard";
|
|
}, 1500);
|
|
};
|
|
wrapper.appendChild(button);
|
|
wrapper.insertAdjacentElement("afterbegin", button);
|
|
}
|
|
</script>
|
|
|
|
{% endblock %}
|