diff --git a/cypress/integration/api-tokens.spec.js b/cypress/integration/api-tokens.spec.js --- a/cypress/integration/api-tokens.spec.js +++ b/cypress/integration/api-tokens.spec.js @@ -8,7 +8,7 @@ describe('Test API tokens UI', function() { it('should ask for user to login', function() { - cy.visit(this.Urls.api_tokens(), {failOnStatusCode: false}); + cy.visit(`${this.Urls.oidc_profile()}#tokens`, {failOnStatusCode: false}); cy.location().should(loc => { expect(loc.pathname).to.eq(this.Urls.oidc_login()); }); @@ -29,7 +29,7 @@ // the tested UI should not be accessible for standard Django users // but we need a user logged in for testing it cy.adminLogin(); - cy.visit(Urls.api_tokens()); + cy.visit(`${Urls.oidc_profile()}#tokens`); } function generateToken(Urls, status, tokenValue = '') { diff --git a/swh/web/api/urls.py b/swh/web/api/urls.py --- a/swh/web/api/urls.py +++ b/swh/web/api/urls.py @@ -3,10 +3,6 @@ # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information -from django.conf.urls import url -from django.contrib.auth.decorators import login_required -from django.shortcuts import render - from swh.web.api.apiurls import APIUrls import swh.web.api.views.content # noqa import swh.web.api.views.directory # noqa @@ -21,11 +17,4 @@ import swh.web.api.views.stat # noqa import swh.web.api.views.vault # noqa - -@login_required(login_url="/oidc/login/", redirect_field_name="next_path") -def _tokens_view(request): - return render(request, "api/tokens.html") - - urlpatterns = APIUrls.get_url_patterns() -urlpatterns.append(url(r"^tokens/$", _tokens_view, name="api-tokens")) diff --git a/swh/web/assets/src/bundles/auth/index.js b/swh/web/assets/src/bundles/auth/index.js --- a/swh/web/assets/src/bundles/auth/index.js +++ b/swh/web/assets/src/bundles/auth/index.js @@ -5,7 +5,7 @@ * See top-level LICENSE file for more information */ -import {handleFetchError, csrfPost} from 'utils/functions'; +import {handleFetchError, csrfPost, removeUrlFragment} from 'utils/functions'; import './auth.css'; let apiTokensTable; @@ -169,7 +169,7 @@ }); } -export function initApiTokensPage() { +export function initProfilePage() { $(document).ready(() => { apiTokensTable = $('#swh-bearer-tokens-table') .on('error.dt', (e, settings, techNote, message) => { @@ -212,5 +212,15 @@ scrollY: '50vh', scrollCollapse: true }); + $('#swh-oidc-profile-tokens-tab').on('shown.bs.tab', () => { + apiTokensTable.draw(); + window.location.hash = '#tokens'; + }); + $('#swh-oidc-profile-account-tab').on('shown.bs.tab', () => { + removeUrlFragment(); + }); + if (window.location.hash === '#tokens') { + $('.nav-tabs a[href="#swh-oidc-profile-tokens"]').tab('show'); + } }); } diff --git a/swh/web/auth/views.py b/swh/web/auth/views.py --- a/swh/web/auth/views.py +++ b/swh/web/auth/views.py @@ -13,6 +13,7 @@ from django.conf.urls import url from django.contrib.auth import authenticate, login, logout +from django.contrib.auth.decorators import login_required from django.core.cache import cache from django.core.paginator import Paginator from django.http import HttpRequest @@ -23,6 +24,7 @@ HttpResponseServerError, JsonResponse, ) +from django.shortcuts import render from django.views.decorators.http import require_http_methods from swh.web.auth.models import OIDCUser, OIDCUserOfflineTokens @@ -216,6 +218,11 @@ return HttpResponse(status=401) +@login_required(login_url="/oidc/login/", redirect_field_name="next_path") +def _oidc_profile_view(request: HttpRequest) -> HttpResponse: + return render(request, "auth/profile.html") + + urlpatterns = [ url(r"^oidc/login/$", oidc_login, name="oidc-login"), url(r"^oidc/login-complete/$", oidc_login_complete, name="oidc-login-complete"), @@ -240,4 +247,5 @@ oidc_revoke_bearer_tokens, name="oidc-revoke-bearer-tokens", ), + url(r"^oidc/profile/$", _oidc_profile_view, name="oidc-profile",), ] diff --git a/swh/web/common/utils.py b/swh/web/common/utils.py --- a/swh/web/common/utils.py +++ b/swh/web/common/utils.py @@ -260,6 +260,7 @@ "swh_client_config": config["client_config"], "oidc_enabled": bool(config["keycloak"]["server_url"]), "browsers_supported_image_mimes": browsers_supported_image_mimes, + "keycloak": config["keycloak"], } diff --git a/swh/web/templates/api/tokens.html b/swh/web/templates/api/tokens.html deleted file mode 100644 --- a/swh/web/templates/api/tokens.html +++ /dev/null @@ -1,57 +0,0 @@ -{% extends "layout.html" %} - -{% comment %} -Copyright (C) 2020 The Software Heritage developers -See the AUTHORS file at the top-level directory of this distribution -License: GNU Affero General Public License version 3, or any later version -See top-level LICENSE file for more information -{% endcomment %} - -{% load render_bundle from webpack_loader %} -{% load swh_templatetags %} - -{% block title %} Web API bearer tokens – Software Heritage API {% endblock %} - -{% block header %} -{% render_bundle 'auth' %} -{% endblock %} - -{% block navbar-content %} -
- That interface enables to manage bearer tokens for Web API authentication. - A token has to be sent in HTTP authorization headers to make authenticated API requests. -
-
- For instance when using curl
proceed as follows:
-
curl -H "Authorization: Bearer ${TOKEN}" {{ request.scheme }}://{{ request.META.HTTP_HOST }}/api/...- - -
Creation date | -Actions | -
---|
+ Below are the details of your user account. + You can edit your personal information in the + + Software Heritage Account Management + interface. +
+Username | +{{ user.username }} | +
---|---|
First name | +{{ user.first_name }} | +
Last name | +{{ user.last_name }} | +
{{ user.email }} | +|
Permissions: | +
+ {% for perm in user.get_all_permissions %}
+ {{ perm }} + {% endfor %} + |
+
+ That interface enables to manage bearer tokens for Web API authentication. + A token has to be sent in HTTP authorization headers to make authenticated API requests. +
+
+ For instance when using curl
proceed as follows:
+
curl -H "Authorization: Bearer ${TOKEN}" {{ request.scheme }}://{{ request.META.HTTP_HOST }}/api/...+ +
Creation date | +Actions | +
---|