mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-18 15:33:02 +00:00
add token lists to token page
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
# Copyright (c) Jupyter Development Team.
|
# Copyright (c) Jupyter Development Team.
|
||||||
# Distributed under the terms of the Modified BSD License.
|
# Distributed under the terms of the Modified BSD License.
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
from http.client import responses
|
from http.client import responses
|
||||||
|
|
||||||
from jinja2 import TemplateNotFound
|
from jinja2 import TemplateNotFound
|
||||||
@@ -217,7 +218,66 @@ class TokenPageHandler(BaseHandler):
|
|||||||
|
|
||||||
@web.authenticated
|
@web.authenticated
|
||||||
def get(self):
|
def get(self):
|
||||||
html = self.render_template('token.html')
|
never = datetime(1900, 1, 1)
|
||||||
|
|
||||||
|
user = self.get_current_user()
|
||||||
|
def sort_key(token):
|
||||||
|
return (
|
||||||
|
token.last_activity or never,
|
||||||
|
token.created or never,
|
||||||
|
)
|
||||||
|
api_tokens = sorted(user.api_tokens, key=sort_key, reverse=True)
|
||||||
|
|
||||||
|
# group oauth client tokens by client id
|
||||||
|
from collections import defaultdict
|
||||||
|
oauth_tokens = defaultdict(list)
|
||||||
|
for token in user.oauth_tokens:
|
||||||
|
if not token.client_id:
|
||||||
|
# token should have been deleted when client was deleted
|
||||||
|
self.log.warning("Deleting stale oauth token for %s", user.name)
|
||||||
|
self.db.delete(token)
|
||||||
|
self.db.commit()
|
||||||
|
continue
|
||||||
|
oauth_tokens[token.client_id].append(token)
|
||||||
|
|
||||||
|
# get the earliest created and latest last_activity
|
||||||
|
# timestamp for a given oauth client
|
||||||
|
oauth_clients = []
|
||||||
|
for client_id, tokens in oauth_tokens.items():
|
||||||
|
created = tokens[0].created
|
||||||
|
last_activity = tokens[0].last_activity
|
||||||
|
for token in tokens[1:]:
|
||||||
|
if token.created < created:
|
||||||
|
created = token.created
|
||||||
|
if (
|
||||||
|
last_activity is None or
|
||||||
|
(token.last_activity and token.last_activity > last_activity)
|
||||||
|
):
|
||||||
|
last_activity = token.last_activity
|
||||||
|
oauth_clients.append({
|
||||||
|
'client': token.client,
|
||||||
|
'description': token.client.description or token.client.client_id,
|
||||||
|
'created': created,
|
||||||
|
'last_activity': last_activity,
|
||||||
|
'tokens': tokens,
|
||||||
|
'token_ids': ','.join(token.api_id for token in tokens),
|
||||||
|
'token_count': len(tokens),
|
||||||
|
})
|
||||||
|
|
||||||
|
# sort oauth clients by last activity, created
|
||||||
|
def sort_key(client):
|
||||||
|
return (
|
||||||
|
client['last_activity'] or never,
|
||||||
|
client['created'] or never,
|
||||||
|
)
|
||||||
|
|
||||||
|
oauth_clients = sorted(oauth_clients, key=sort_key, reverse=True)
|
||||||
|
|
||||||
|
html = self.render_template(
|
||||||
|
'token.html',
|
||||||
|
api_tokens=api_tokens,
|
||||||
|
oauth_clients=oauth_clients,
|
||||||
|
)
|
||||||
self.finish(html)
|
self.finish(html)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,13 +1,20 @@
|
|||||||
// Copyright (c) Jupyter Development Team.
|
// Copyright (c) Jupyter Development Team.
|
||||||
// Distributed under the terms of the Modified BSD License.
|
// Distributed under the terms of the Modified BSD License.
|
||||||
|
|
||||||
require(["jquery", "jhapi"], function ($, JHAPI) {
|
require(["jquery", "jhapi", "moment"], function ($, JHAPI, moment) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var base_url = window.jhdata.base_url;
|
var base_url = window.jhdata.base_url;
|
||||||
var user = window.jhdata.user;
|
var user = window.jhdata.user;
|
||||||
var api = new JHAPI(base_url);
|
var api = new JHAPI(base_url);
|
||||||
|
|
||||||
|
$(".time-col").map(function (i, el) {
|
||||||
|
// convert ISO datestamps to nice momentjs ones
|
||||||
|
el = $(el);
|
||||||
|
let m = moment(new Date(el.text().trim()));
|
||||||
|
el.text(m.isValid() ? m.fromNow() : "Never");
|
||||||
|
});
|
||||||
|
|
||||||
$("#request-token").click(function () {
|
$("#request-token").click(function () {
|
||||||
api.request_token({
|
api.request_token({
|
||||||
success: function (reply) {
|
success: function (reply) {
|
||||||
|
@@ -30,6 +30,84 @@
|
|||||||
</div>
|
</div>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if api_tokens %}
|
||||||
|
<div class="row">
|
||||||
|
<h2>API Tokens</h2>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Note</td>
|
||||||
|
<td>Last used</td>
|
||||||
|
<td>Created</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for token in api_tokens %}
|
||||||
|
<tr class="token-row" data-token-id="{{token.api_id}}"">
|
||||||
|
{% block token_row scoped %}
|
||||||
|
<td class="note-col col-sm-5">{{token.note}}</td>
|
||||||
|
<td class="time-col col-sm-3">
|
||||||
|
{%- if token.last_activity -%}
|
||||||
|
{{ token.last_activity.isoformat() + 'Z' }}
|
||||||
|
{%- else -%}
|
||||||
|
Never
|
||||||
|
{%- endif -%}
|
||||||
|
</td>
|
||||||
|
<td class="time-col col-sm-3">
|
||||||
|
{{ token.created.isoformat() + 'Z' }}
|
||||||
|
</td>
|
||||||
|
<td class="col-sm-1 text-center">
|
||||||
|
<a role="button" class="delete-token-btn btn btn-xs btn-danger">revoke</a>
|
||||||
|
</td>
|
||||||
|
{% endblock token_row %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if oauth_clients %}
|
||||||
|
<div class="row">
|
||||||
|
<h2>Authorized OAuth Applications</h2>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td>Application</td>
|
||||||
|
<td>Tokens</td>
|
||||||
|
<td>Last used</td>
|
||||||
|
<td>First authorized</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for client in oauth_clients %}
|
||||||
|
<tr class="oauth-client-row"
|
||||||
|
data-token-ids="{{ client['token_ids'] }}"">
|
||||||
|
{% block client_row scoped %}
|
||||||
|
<td class="note-col col-sm-4">{{ client['description'] }}</td>
|
||||||
|
<td class="col-sm-1">
|
||||||
|
{{ client['token_count'] }}
|
||||||
|
</td>
|
||||||
|
<td class="time-col col-sm-3">
|
||||||
|
{%- if client['last_activity'] -%}
|
||||||
|
{{ client['last_activity'].isoformat() + 'Z' }}
|
||||||
|
{%- else -%}
|
||||||
|
Never
|
||||||
|
{%- endif -%}
|
||||||
|
</td>
|
||||||
|
<td class="time-col col-sm-3">
|
||||||
|
{{ client['created'].isoformat() + 'Z' }}
|
||||||
|
</td>
|
||||||
|
<td class="col-sm-1 text-center">
|
||||||
|
<a role="button" class="delete-token-btn btn btn-xs btn-danger">revoke</a>
|
||||||
|
</td>
|
||||||
|
{% endblock client_row %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user