Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add frontend for JWT refresh tokens #3273

Open
wants to merge 1 commit into
base: jwt-refresh-token-endpoint
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/3273.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add frontend for managing JWT refresh tokens
4 changes: 2 additions & 2 deletions python/nav/models/sql/changes/sc.05.13.0001.sql
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ CREATE TABLE manage.JWTRefreshToken (
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL UNIQUE,
description VARCHAR,
expires TIMESTAMP NOT NULL,
activates TIMESTAMP NOT NULL,
expires TIMESTAMPTZ NOT NULL,
activates TIMESTAMPTZ NOT NULL,
hash VARCHAR NOT NULL
);
66 changes: 66 additions & 0 deletions python/nav/web/templates/useradmin/jwt_created.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{% extends "useradmin/base.html" %}

{% block base_header_additional_head %}
<link rel="stylesheet" href="{{ STATIC_URL }}css/nav/multi-select.css">
<style>
#edit-token-form input[type=submit] { float:left; margin-right: 1.25rem }
label[for='id_endpoints'] { display: none; }
</style>
<script>
require(['libs/jquery.multi-select'], function(){
$(function(){
/* Add multiselect for selecting endpoints */
var msSelector = '#id_endpoints';
$(msSelector).multiSelect({
selectableHeader: 'Available endpoints <a id="select-all-endpoints" class="right">Select all</a>',
selectionHeader: 'Selected endpoints <a id="remove-all-endpoints" class="right">Remove all</a>',
afterInit: function(){
$('#select-all-endpoints').click(function(){ $(msSelector).multiSelect('select_all') });
$('#remove-all-endpoints').click(function(){ $(msSelector).multiSelect('deselect_all') });
}
});

/* This is the code for closing the content dropdown */
$('#confirm-token-delete .close-button').click(function () {
$(document).foundation('dropdown', 'close', $(this).parents('.f-dropdown:first'));
});
});
});
</script>

<style>
.token-card { max-width: 470px; overflow-wrap: break-word; }
.token-card .label { cursor: help; margin-bottom: 1px; }
.token-card code { display: inline-block; width: 100%; }
</style>
{% endblock %}


{% block content %}

Back to
<a href="{% url 'useradmin-jwt_list' %}">token list</a>

<div id="form-panel" class="token-card panel white">

<h3>
{{ object.name }}
</h3>

<code id="token" style="display:block; white-space:pre-wrap">{{ token }}</code>

<button onclick="myFunction()">Copy to clipboard</button>

<script>
function myFunction() {
var copyText = document.getElementById("token");
navigator.clipboard.writeText(copyText.innerHTML);
}
</script>


<div class="float-clear"></div>

</div>

{% endblock %}
53 changes: 53 additions & 0 deletions python/nav/web/templates/useradmin/jwt_detail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{% extends "useradmin/base.html" %}
{% load info %}

{# Display information about a single token #}


{% block base_header_additional_head %}
<style>
.token-card { max-width: 470px; overflow-wrap: break-word;}
.token-card .label { cursor: help; margin-bottom: 1px; }
.token-card code { display: inline-block; width: 100%;}
</style>
{% endblock %}


{% block content %}

<a href="{% url 'useradmin-jwt_list' %}">Back to token list</a>

<div class="row">
<div class="column small-12">

<div class="token-card panel white">
<a href="{% url 'useradmin-jwt_edit' object.pk %}" class="right">Edit token</a>

<h3>Token details</h3>

<p>
<code>{{ object.name }}</code>
</p>

{% if object.description %}
<h4>Description</h4>
<div class="panel">
<p>
{{ object.description }}
</p>
</div>
{% endif %}
<h4>Active</h4>
<div class="panel">
<p>
{{ object.is_active }}
</p>
</div>

</div> {# end panel #}


</div> {# end column #}
</div> {# end row #}

{% endblock %}
87 changes: 87 additions & 0 deletions python/nav/web/templates/useradmin/jwt_edit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{% extends "useradmin/base.html" %}

{% block base_header_additional_head %}
<link rel="stylesheet" href="{{ STATIC_URL }}css/nav/multi-select.css">
<style>
#edit-token-form input[type=submit] { float:left; margin-right: 1.25rem }
label[for='id_endpoints'] { display: none; }
</style>
<script>
require(['libs/jquery.multi-select'], function(){
$(function(){
/* Add multiselect for selecting endpoints */
var msSelector = '#id_endpoints';
$(msSelector).multiSelect({
selectableHeader: 'Available endpoints <a id="select-all-endpoints" class="right">Select all</a>',
selectionHeader: 'Selected endpoints <a id="remove-all-endpoints" class="right">Remove all</a>',
afterInit: function(){
$('#select-all-endpoints').click(function(){ $(msSelector).multiSelect('select_all') });
$('#remove-all-endpoints').click(function(){ $(msSelector).multiSelect('deselect_all') });
}
});

/* This is the code for closing the content dropdown */
$('#confirm-token-delete .close-button').click(function () {
$(document).foundation('dropdown', 'close', $(this).parents('.f-dropdown:first'));
});
});
});
</script>
{% endblock %}


{% block content %}

Back to
<a href="{% url 'useradmin-jwt_list' %}">token list</a>
{% if object %}
| <a href="{% url 'useradmin-jwt_detail' object.pk %}">token details</a>
{% endif %}

<div id="form-panel" class="panel white">

<h4>
{% if object %}
Edit token
{% else %}
Create new token
{% endif %}
</h4>


{% include 'custom_crispy_templates/flat_form.html' %}


{% if object %}
{# Display button for expiring a token #}
<form action="{% url 'useradmin-jwt_expire' object.pk %}" method="post">
<input type="submit" value="Expire token" class="button small">
</form>

{# Display button for recreating a token #}
<form action="{% url 'useradmin-jwt_recreate' object.pk %}" method="post">
<input type="submit" value="Recreate token" class="button small">
</form>

{# Display form for deleting a token #}
<a href="javascript:void(0);" class="button small alert"
data-options="align:top"
data-dropdown="confirm-token-delete">
Delete token
</a>

<div id="confirm-token-delete" class="f-dropdown content">
<form id="delete-token-form"
action="{% url 'useradmin-jwt_delete' object.pk %}"
method="post">{% csrf_token %}
<input type="submit" value="Yes, delete" class="button small alert expand">
</form>
<span class="button secondary small close-button expand">No, don't delete</span>
</div>
{% endif %}

<div class="float-clear"></div>

</div>

{% endblock %}
110 changes: 110 additions & 0 deletions python/nav/web/templates/useradmin/jwt_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@

{% extends "useradmin/base.html" %}
{% load info %}


{% block base_header_additional_head %}
<style>
#token-list-table .label { cursor: help; margin-bottom: 1px; }
</style>
<script>
require(['libs/jquery.tablesorter.min'], function() {
$(function() {
var $table = $('#token-list-table');
if ($table.length && $table.find('tbody tr').length > 0) {
$table.tablesorter({
sortList: [[2, 0]],
headers: {
5: {sorter: false},
6: {sorter: false},
7: {sorter: false}
}
});
}
});
});
</script>
{% endblock %}


{% block content %}

<div class="tabs">
{% include 'useradmin/tabs.html' %}

<div class="tabcontent">
<a href="{% url 'useradmin-jwt_create' %}" class="button small">
Create new token
</a>

{# Show table with tokens if there are any #}
{% if object_list %}

<table id="token-list-table" class="listtable hover tablesorter">
<caption>
List of issued API tokens
</caption>

<thead>
<tr>
<th>Name</th>
<th>Activates at</th>
<th>Expires at</th>
<th>Status</th>
<th>&nbsp;</th>
</tr>
</thead>

<tbody>
{% for token in object_list %}
<tr>
{# Name #}
<td>
<a href="{% url 'useradmin-jwt_detail' token.pk %}" title="Details">{{ token.name }}</a>
</td>

{# Activates at #}
<td>
{{ token.activates }}
</td>

{# Expires at #}
<td>
{{ token.expires}}
</td>

{# Status #}
<td>
{% if not token.is_active%}
<span class="label warning" title="Token Inactive">
Inactive
</span>
{% else %}
<span class="label success" title="Token Active">
Active
</span>
{% endif %}
</td>

{# Link to token edit #}
<td>
<a href="{% url 'useradmin-jwt_edit' token.pk %}" title="Edit this token">Edit</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>

{% else %}

{# If there are no tokens, show information about creating one #}
<div class="alert-box info">No tokens are issued. Do you want to
<a href="{% url 'useradmin-jwt_create' %}">create one</a>?
</div>

{% endif %}

</div> {# .tabcontent #}
</div> {# .tabs #}

{% endblock %}
49 changes: 49 additions & 0 deletions python/nav/web/templates/useradmin/jwt_not_enabled.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{% extends "useradmin/base.html" %}

{% block base_header_additional_head %}
<link rel="stylesheet" href="{{ STATIC_URL }}css/nav/multi-select.css">
<style>
#edit-token-form input[type=submit] { float:left; margin-right: 1.25rem }
label[for='id_endpoints'] { display: none; }
</style>
<script>
require(['libs/jquery.multi-select'], function(){
$(function(){
/* Add multiselect for selecting endpoints */
var msSelector = '#id_endpoints';
$(msSelector).multiSelect({
selectableHeader: 'Available endpoints <a id="select-all-endpoints" class="right">Select all</a>',
selectionHeader: 'Selected endpoints <a id="remove-all-endpoints" class="right">Remove all</a>',
afterInit: function(){
$('#select-all-endpoints').click(function(){ $(msSelector).multiSelect('select_all') });
$('#remove-all-endpoints').click(function(){ $(msSelector).multiSelect('deselect_all') });
}
});

/* This is the code for closing the content dropdown */
$('#confirm-token-delete .close-button').click(function () {
$(document).foundation('dropdown', 'close', $(this).parents('.f-dropdown:first'));
});
});
});
</script>
{% endblock %}


{% block content %}

Back to
<a href="{% url 'useradmin-jwt_list' %}">token list</a>

<div id="form-panel" class="panel white">


<h4>
You must configure jwt.conf before using this feature.
</h4>

<div class="float-clear"></div>

</div>

{% endblock %}
3 changes: 3 additions & 0 deletions python/nav/web/templates/useradmin/tabs.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
<li{% if active.token_list %} class="tabactive"{% endif %}>
<a href="{% url 'useradmin-token_list' %}">API Token List</a>
</li>
<li{% if active.jwt_list %} class="tabactive"{% endif %}>
<a href="{% url 'useradmin-jwt_list' %}">JWT Token List</a>
</li>
</ul>
2 changes: 1 addition & 1 deletion python/nav/web/templates/useradmin/token_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

{% block base_header_additional_head %}
<style>
.token-card { max-width: 470px; }
.token-card { max-width: 470px; overflow-wrap: break-word; }
.token-card .label { cursor: help; margin-bottom: 1px; }
.token-card code { display: inline-block; width: 100%; }
</style>
Expand Down
Loading
Loading