datasette-auth-tokens/datasette_auth_tokens/templates/create_api_token.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%;
}
form div label {
/* Defaults to width: 15% which looks bad */
width: auto;
}
</style>
{% endblock %}
{% block content %}
<h1>Create an API token</h1>
{% if tokens_exist %}<p><a href=".">Manage existing tokens</a></p>{% endif %}
<p>This token will allow API access with the same abilities as your current user, <strong>{{ request.actor.username or 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>
<p class="message-warning">For security reasons this token will only appear here once. <strong>Copy it somewhere safe</strong>.</p>
<!--- 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 action="{{ urls.path('-/api/tokens/create') }}" method="post">
<div>
<div style="margin-bottom: 0.5em">
<input type="text" name="description" placeholder="Optional token description" style="width: 40%">
</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">
<p style="margin-top: 1em" id="token-summary"></p>
<h2>All databases and tables</h2>
<ul>
{% for permission in all_permissions %}
<li><label><input type="checkbox" name="all:{{ permission.name }}"> {{ permission.name }}</label> - {{ permission.description }}</li>
{% endfor %}
</ul>
{% for database in database_with_tables %}
<h2>All tables in "{{ database.name }}"</h2>
<ul>
{% for permission in database_permissions %}
<li><label><input type="checkbox" name="database:{{ database.encoded }}:{{ permission.name }}"> {{ permission.name }}</label> - {{ permission.description }}</li>
{% endfor %}
</ul>
{% endfor %}
{% if database_with_tables %}
<h2>Specific tables in specific databases</h2>
{% for database in database_with_tables %}
{% for table in database.tables %}
<h3>{{ database.name }}: {{ table.name }}</h3>
<ul>
{% for permission in resource_permissions %}
<li><label><input type="checkbox" name="resource:{{ database.encoded }}:{{ table.encoded }}:{{ permission.name }}"> {{ permission.name }}</label> - {{ permission.description }}</li>
{% endfor %}
</ul>
{% endfor %}
{% endfor %}
{% endif %}
</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';
}
}
function updateTokenSummary() {
// Get all selected checkbox values on page
var allChecked = document.querySelectorAll('input[type="checkbox"]:checked');
var checkedValues = [];
for (var i = 0; i < allChecked.length; i++) {
checkedValues.push(humanPermission(allChecked[i].name));
}
var tokenSummary = document.querySelector('#token-summary');
if (checkedValues.length == 0) {
tokenSummary.innerHTML = 'Token will have all of the permissions of your current user';
} else {
// Build a <ul> of checkedValues
let html = ['<ul style="margin-top: 0.5em">'];
checkedValues.forEach(function(value) {
html.push('<li style="list-style-type: disc; margin-left: 1em;">' + value + '</li>');
});
html.push('</ul>');
tokenSummary.innerHTML = 'Token will be restricted to: ' + html.join('');
}
}
updateTokenSummary();
function humanPermission(permission) {
// database:messages:view-database becomes "messages database: view-database"
// resource:messages:t2:view-table becomes "messages/t2 table: view-table"
var parts = permission.split(':');
var type = parts[0];
var db = parts[1];
var action = parts[parts.length - 1];
if (type == 'all') {
return 'all databases and tables: ' + action;
} else if (type == 'database') {
return db + ' database: ' + action;
} else if (type == 'resource') {
var table = parts[2];
return db + '/' + table + ' table: ' + action;
}
}
// On any input change on page, update token summary
document.addEventListener('change', updateTokenSummary);
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 %}