Page MenuHomeSoftware Heritage

D8420.diff
No OneTemporary

D8420.diff

diff --git a/requirements-swh.txt b/requirements-swh.txt
--- a/requirements-swh.txt
+++ b/requirements-swh.txt
@@ -1,4 +1,4 @@
-swh.auth[django] >= 0.5.3
+swh.auth[django] >= 0.6.7
swh.core >= 0.0.95
swh.counters >= 0.5.1
swh.indexer >= 2.0.0
diff --git a/swh/web/add_forge_now/admin_views.py b/swh/web/add_forge_now/admin_views.py
--- a/swh/web/add_forge_now/admin_views.py
+++ b/swh/web/add_forge_now/admin_views.py
@@ -3,7 +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 import settings
from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import render
@@ -11,7 +10,7 @@
from swh.web.auth.utils import is_add_forge_now_moderator
-@user_passes_test(is_add_forge_now_moderator, login_url=settings.LOGIN_URL)
+@user_passes_test(is_add_forge_now_moderator)
def add_forge_now_requests_moderation_dashboard(request):
"""Moderation dashboard to allow listing current requests."""
return render(
@@ -21,7 +20,7 @@
)
-@user_passes_test(is_add_forge_now_moderator, login_url=settings.LOGIN_URL)
+@user_passes_test(is_add_forge_now_moderator)
def add_forge_now_request_dashboard(request, request_id):
"""Moderation dashboard to allow listing current requests."""
return render(
diff --git a/swh/web/add_forge_now/templates/add-forge-creation-form.html b/swh/web/add_forge_now/templates/add-forge-creation-form.html
--- a/swh/web/add_forge_now/templates/add-forge-creation-form.html
+++ b/swh/web/add_forge_now/templates/add-forge-creation-form.html
@@ -13,12 +13,11 @@
<p class="text-primary">
You must be logged in to submit an add forge request. Please
<a id="loginLink" class="link-primary"
- {% if oidc_enabled and 'remote_user' in request.GET %}
- href="{% url 'oidc-login' %}?next={% url 'forge-add-create' %}"
- {% else %}
- href="{% url 'login' %}?next={% url 'forge-add-create' %}"
- {% endif %}>log in</a>
+ href="{% url login_url %}?next={% url 'forge-add-create' %}">
+ log in
+ </a>
</p>
+
{% else %}
<form method="POST" action="{% url 'api-1-add-forge-request-create' %}"
diff --git a/swh/web/add_forge_now/views.py b/swh/web/add_forge_now/views.py
--- a/swh/web/add_forge_now/views.py
+++ b/swh/web/add_forge_now/views.py
@@ -5,7 +5,6 @@
from typing import Any, Dict, List
-from django.conf import settings
from django.contrib.auth.decorators import user_passes_test
from django.core.paginator import Paginator
from django.db.models import Q
@@ -116,11 +115,7 @@
)
-@user_passes_test(
- is_add_forge_now_moderator,
- redirect_field_name="next_path",
- login_url=settings.LOGIN_URL,
-)
+@user_passes_test(is_add_forge_now_moderator)
def create_request_message_source(request: HttpRequest, id: int) -> HttpResponse:
"""View to retrieve the message source for a given request history entry"""
diff --git a/swh/web/api/templates/api.html b/swh/web/api/templates/api.html
--- a/swh/web/api/templates/api.html
+++ b/swh/web/api/templates/api.html
@@ -38,9 +38,11 @@
<li class="list-inline-item">
<a href="#rate-limiting">Rate limiting</a>
</li>
- <li class="list-inline-item">
- <a href="#authentication">Authentication</a>
- </li>
+ {% if oidc_enabled %}
+ <li class="list-inline-item">
+ <a href="#authentication">Authentication</a>
+ </li>
+ {% endif %}
</ul>
<h4 id="endpoint-index">Endpoint index</h4>
@@ -277,28 +279,30 @@
X-RateLimit-Remaining: 119
X-RateLimit-Reset: 1620639052</code></pre>
- <h4 id="authentication">Authentication</h4>
- <p>
- It is possible to perform authenticated requests to the Web API through the use of a bearer token
- sent in HTTP Authorization headers.
- <br/>
- To obtain such a token, an account to the
- <a href="{% url 'oidc-login' %}">Software Heritage Authentication service</a> must be created.
- <br/>
- To generate and manage bearer tokens, a dedicated interface is available on the
- <a href="{% url 'oidc-profile' %}#tokens">user profile page</a> once logged in.
- </p>
- <p>
- The following shows how to perform an authenticated request to the Web API using <code>curl</code>.
- <pre>export TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhMTMxYTQ1My1hM2IyLTQwMTUtO...
-curl -H "Authorization: Bearer ${TOKEN}" {{ site_base_url }}api/...</pre>
- </p>
- <p>
- Authenticated requests can be used to lift rate limiting if the user account has the adequate
- permission.
- If you are in such a need, please <a href="https://www.softwareheritage.org/contact/">contact us</a>
- and we will review your request.
- </p>
+ {% if oidc_enabled %}
+ <h4 id="authentication">Authentication</h4>
+ <p>
+ It is possible to perform authenticated requests to the Web API through the use of a bearer token
+ sent in HTTP Authorization headers.
+ <br/>
+ To obtain such a token, an account to the
+ <a href="{% url 'oidc-login' %}">Software Heritage Authentication service</a> must be created.
+ <br/>
+ To generate and manage bearer tokens, a dedicated interface is available on the
+ <a href="{% url 'oidc-profile' %}#tokens">user profile page</a> once logged in.
+ </p>
+ <p>
+ The following shows how to perform an authenticated request to the Web API using <code>curl</code>.
+ <pre>export TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhMTMxYTQ1My1hM2IyLTQwMTUtO...
+ curl -H "Authorization: Bearer ${TOKEN}" {{ site_base_url }}api/...</pre>
+ </p>
+ <p>
+ Authenticated requests can be used to lift rate limiting if the user account has the adequate
+ permission.
+ If you are in such a need, please <a href="https://www.softwareheritage.org/contact/">contact us</a>
+ and we will review your request.
+ </p>
+ {% endif %}
</div>
<script>
diff --git a/swh/web/auth/templates/logout.html b/swh/web/auth/templates/logout.html
--- a/swh/web/auth/templates/logout.html
+++ b/swh/web/auth/templates/logout.html
@@ -16,26 +16,22 @@
{% endblock %}
{% block content %}
-{% if 'next_path' in request.GET %}
+{% if 'next' in request.GET %}
<p>Your authenticated session expired so you have been automatically logged out.</p>
{% else %}
<p>You have been successfully logged out.</p>
{% endif %}
<p>
-{% if oidc_enabled and 'remote_user' in request.GET %}
-{% if 'next_path' in request.GET %}
-<a href="{% url 'oidc-login' %}?next_path={{ request.GET.next_path }}">
+{% if 'next' in request.GET %}
+<a href="{% url login_url %}?next={{ request.GET.next }}">
{% else %}
-<a href="{% url 'oidc-login' %}">
-{% endif %}
-{% else %}
-<a href="{% url 'login' %}">
+<a href="{% url login_url %}">
{% endif %}
Log in</a> again ?</p>
-{% if 'next_path' in request.GET %}
+{% if 'next' in request.GET %}
<p>
Or go back to
-<a href="{{ request.GET.next_path }}">
+<a href="{{ request.GET.next }}">
previous page</a>.
</p>
{% endif %}
diff --git a/swh/web/auth/urls.py b/swh/web/auth/urls.py
--- a/swh/web/auth/urls.py
+++ b/swh/web/auth/urls.py
@@ -15,38 +15,61 @@
oidc_profile_view,
oidc_revoke_bearer_tokens,
)
+from swh.web.config import get_config
-urlpatterns = auth_urlpatterns + [
- url(
- r"^oidc/generate-bearer-token/$",
- oidc_generate_bearer_token,
- name="oidc-generate-bearer-token",
- ),
- url(
- r"^oidc/generate-bearer-token-complete/$",
- oidc_generate_bearer_token_complete,
- name="oidc-generate-bearer-token-complete",
- ),
- url(
- r"^oidc/list-bearer-token/$",
- oidc_list_bearer_tokens,
- name="oidc-list-bearer-tokens",
- ),
- url(
- r"^oidc/get-bearer-token/$",
- oidc_get_bearer_token,
- name="oidc-get-bearer-token",
- ),
- url(
- r"^oidc/revoke-bearer-tokens/$",
- oidc_revoke_bearer_tokens,
- name="oidc-revoke-bearer-tokens",
- ),
+config = get_config()
+
+oidc_enabled = bool(config["keycloak"]["server_url"])
+
+urlpatterns = []
+
+if not oidc_enabled:
+ urlpatterns = [
+ url(
+ r"^login/$",
+ LoginView.as_view(template_name="login.html"),
+ name="login",
+ )
+ ]
+
+if oidc_enabled or config["e2e_tests_mode"]:
+ urlpatterns += auth_urlpatterns + [
+ url(
+ r"^oidc/generate-bearer-token/$",
+ oidc_generate_bearer_token,
+ name="oidc-generate-bearer-token",
+ ),
+ url(
+ r"^oidc/generate-bearer-token-complete/$",
+ oidc_generate_bearer_token_complete,
+ name="oidc-generate-bearer-token-complete",
+ ),
+ url(
+ r"^oidc/list-bearer-token/$",
+ oidc_list_bearer_tokens,
+ name="oidc-list-bearer-tokens",
+ ),
+ url(
+ r"^oidc/get-bearer-token/$",
+ oidc_get_bearer_token,
+ name="oidc-get-bearer-token",
+ ),
+ url(
+ r"^oidc/revoke-bearer-tokens/$",
+ oidc_revoke_bearer_tokens,
+ name="oidc-revoke-bearer-tokens",
+ ),
+ url(
+ r"^oidc/profile/$",
+ oidc_profile_view,
+ name="oidc-profile",
+ ),
+ ]
+
+urlpatterns.append(
url(
- r"^oidc/profile/$",
- oidc_profile_view,
- name="oidc-profile",
- ),
- url(r"^login/$", LoginView.as_view(template_name="login.html"), name="login"),
- url(r"^logout/$", LogoutView.as_view(template_name="logout.html"), name="logout"),
-]
+ r"^logout/$",
+ LogoutView.as_view(template_name="logout.html"),
+ name="logout",
+ )
+)
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
@@ -150,6 +150,6 @@
return HttpResponse(status=401)
-@login_required(login_url="/oidc/login/", redirect_field_name="next_path")
+@login_required(login_url="oidc-login")
def oidc_profile_view(request: HttpRequest) -> HttpResponse:
return render(request, "profile.html")
diff --git a/swh/web/deposit/urls.py b/swh/web/deposit/urls.py
--- a/swh/web/deposit/urls.py
+++ b/swh/web/deposit/urls.py
@@ -7,7 +7,6 @@
import requests
from requests.auth import HTTPBasicAuth
-from django.conf import settings
from django.contrib.auth.decorators import user_passes_test
from django.http import JsonResponse
from django.shortcuts import render
@@ -21,12 +20,12 @@
return user.is_staff or user.has_perm(ADMIN_LIST_DEPOSIT_PERMISSION)
-@user_passes_test(can_list_deposits, login_url=settings.LOGIN_URL)
+@user_passes_test(can_list_deposits)
def admin_deposit(request):
return render(request, "deposit-admin.html")
-@user_passes_test(can_list_deposits, login_url=settings.LOGIN_URL)
+@user_passes_test(can_list_deposits)
def admin_deposit_list(request):
config = get_config()["deposit"]
private_api_url = config["private_api_url"].rstrip("/") + "/"
diff --git a/swh/web/save_code_now/templates/origin-save-help.html b/swh/web/save_code_now/templates/origin-save-help.html
--- a/swh/web/save_code_now/templates/origin-save-help.html
+++ b/swh/web/save_code_now/templates/origin-save-help.html
@@ -47,7 +47,7 @@
Once a save request has been accepted, you can follow its current status in the
<a id="swh-show-origin-save-requests-list" href="#swh-origin-save-requests-list">submitted save requests list</a>.
<br/>
- If you submitted requests while <a href="{% url 'oidc-login' %}">authenticated</a>, you will be able
+ If you submitted requests while <a href="{% url login_url %}">authenticated</a>, you will be able
to only display your own requests.
</p>
</div>
diff --git a/swh/web/settings/common.py b/swh/web/settings/common.py
--- a/swh/web/settings/common.py
+++ b/swh/web/settings/common.py
@@ -80,7 +80,6 @@
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
- "swh.auth.django.middlewares.OIDCSessionExpiredMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"swh.web.utils.middlewares.ThrottlingHeadersMiddleware",
@@ -323,7 +322,26 @@
}
}
-LOGIN_URL = "/login/"
+AUTHENTICATION_BACKENDS = [
+ "django.contrib.auth.backends.ModelBackend",
+]
+
+oidc_enabled = bool(get_config()["keycloak"]["server_url"])
+
+if not oidc_enabled:
+ LOGIN_URL = "login"
+ LOGOUT_URL = "logout"
+else:
+ LOGIN_URL = "oidc-login"
+ LOGOUT_URL = "oidc-logout"
+ AUTHENTICATION_BACKENDS.append(
+ "swh.auth.django.backends.OIDCAuthorizationCodePKCEBackend",
+ )
+ MIDDLEWARE.insert(
+ MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware") + 1,
+ "swh.auth.django.middlewares.OIDCSessionExpiredMiddleware",
+ )
+
LOGIN_REDIRECT_URL = "swh-web-homepage"
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
@@ -337,11 +355,6 @@
CORS_ORIGIN_ALLOW_ALL = True
CORS_URLS_REGEX = r"^/(badge|api)/.*$"
-AUTHENTICATION_BACKENDS = [
- "django.contrib.auth.backends.ModelBackend",
- "swh.auth.django.backends.OIDCAuthorizationCodePKCEBackend",
-]
-
OIDC_SWH_WEB_CLIENT_ID = "swh-web"
SWH_AUTH_SERVER_URL = swh_web_config["keycloak"]["server_url"]
SWH_AUTH_REALM_NAME = swh_web_config["keycloak"]["realm_name"]
diff --git a/swh/web/settings/production.py b/swh/web/settings/production.py
--- a/swh/web/settings/production.py
+++ b/swh/web/settings/production.py
@@ -67,5 +67,6 @@
WEBPACK_LOADER["DEFAULT"]["CACHE"] = not DEBUG
-LOGIN_URL = "/oidc/login/"
-LOGIN_REDIRECT_URL = "/oidc/profile/"
+LOGIN_URL = "oidc-login"
+LOGIN_REDIRECT_URL = "oidc-profile"
+LOGOUT_URL = "oidc-logout"
diff --git a/swh/web/settings/tests.py b/swh/web/settings/tests.py
--- a/swh/web/settings/tests.py
+++ b/swh/web/settings/tests.py
@@ -159,3 +159,6 @@
else:
# Silent DEBUG output when running unit tests
LOGGING["handlers"]["console"]["level"] = "INFO" # type: ignore
+
+LOGIN_URL = "login" if not _pytest else "oidc-login"
+LOGOUT_URL = "logout" if not _pytest else "oidc-logout"
diff --git a/swh/web/tests/auth/test_views.py b/swh/web/tests/auth/test_views.py
--- a/swh/web/tests/auth/test_views.py
+++ b/swh/web/tests/auth/test_views.py
@@ -281,7 +281,7 @@
requesting profile view.
"""
url = reverse("oidc-profile")
- login_url = reverse("oidc-login", query_params={"next_path": url})
+ login_url = reverse("oidc-login", query_params={"next": url})
resp = check_http_get_response(client, url, status_code=302)
assert resp["location"] == login_url
diff --git a/swh/web/tests/conftest.py b/swh/web/tests/conftest.py
--- a/swh/web/tests/conftest.py
+++ b/swh/web/tests/conftest.py
@@ -1226,11 +1226,18 @@
from django.conf import settings
clear_url_caches()
- urlconf = settings.ROOT_URLCONF
- if urlconf in sys.modules:
- reload(sys.modules[urlconf])
- else:
- import_module(urlconf)
+ # force reloading of all URLs as they depend on django settings
+ # and swh-web configuration
+ urlconfs = [settings.ROOT_URLCONF]
+ urlconfs += [f"{app}.urls" for app in settings.SWH_DJANGO_APPS]
+ for urlconf in urlconfs:
+ try:
+ if urlconf in sys.modules:
+ reload(sys.modules[urlconf])
+ else:
+ import_module(urlconf)
+ except ModuleNotFoundError:
+ pass
class SwhSettingsWrapper(SettingsWrapper):
diff --git a/swh/web/tests/deposit/test_views.py b/swh/web/tests/deposit/test_views.py
--- a/swh/web/tests/deposit/test_views.py
+++ b/swh/web/tests/deposit/test_views.py
@@ -7,6 +7,8 @@
import pytest
+from django.conf import settings
+
from swh.web.auth.utils import ADMIN_LIST_DEPOSIT_PERMISSION
from swh.web.config import get_config
from swh.web.tests.helpers import (
@@ -20,7 +22,7 @@
def test_deposit_admin_view_not_available_for_anonymous_user(client):
url = reverse("admin-deposit")
resp = check_html_get_response(client, url, status_code=302)
- assert resp["location"] == reverse("login", query_params={"next": url})
+ assert resp["location"] == reverse(settings.LOGIN_URL, query_params={"next": url})
@pytest.mark.django_db
diff --git a/swh/web/tests/save_code_now/test_origin_save_admin.py b/swh/web/tests/save_code_now/test_origin_save_admin.py
--- a/swh/web/tests/save_code_now/test_origin_save_admin.py
+++ b/swh/web/tests/save_code_now/test_origin_save_admin.py
@@ -7,6 +7,8 @@
import pytest
+from django.conf import settings
+
from swh.web.save_code_now.models import (
SAVE_REQUEST_ACCEPTED,
SAVE_REQUEST_PENDING,
@@ -34,7 +36,7 @@
def check_not_login(client, url):
- login_url = reverse("login", query_params={"next": url})
+ login_url = reverse(settings.LOGIN_URL, query_params={"next": url})
resp = check_http_post_response(client, url, status_code=302)
assert unquote(resp.url) == login_url
diff --git a/swh/web/tests/webapp/test_templates.py b/swh/web/tests/webapp/test_templates.py
--- a/swh/web/tests/webapp/test_templates.py
+++ b/swh/web/tests/webapp/test_templates.py
@@ -9,6 +9,8 @@
from pkg_resources import get_distribution
import pytest
+from django.conf import settings
+
from swh.web.auth.utils import ADMIN_LIST_DEPOSIT_PERMISSION
from swh.web.config import SWH_WEB_SERVER_NAME, SWH_WEB_STAGING_SERVER_NAMES, get_config
from swh.web.tests.django_asserts import assert_contains, assert_not_contains
@@ -56,15 +58,23 @@
assert_contains(resp, reverse("oidc-login"))
-def test_layout_without_oidc_auth_enabled(client, mocker):
+def test_layout_without_oidc_auth_enabled(client, django_settings, mocker):
config = deepcopy(get_config())
config["keycloak"]["server_url"] = ""
- mock_get_config = mocker.patch("swh.web.utils.get_config")
+ mock_get_config = mocker.patch("swh.web.config.get_config")
mock_get_config.return_value = config
+ django_settings.LOGIN_URL = "login"
+ django_settings.LOGOUT_URL = "logout"
+ django_settings.MIDDLEWARE = [
+ mid
+ for mid in django_settings.MIDDLEWARE
+ if mid != "swh.auth.django.middlewares.OIDCSessionExpiredMiddleware"
+ ]
+
url = reverse("swh-web-homepage")
resp = check_http_get_response(client, url, status_code=200)
- assert_contains(resp, reverse("login"))
+ assert_contains(resp, reverse(settings.LOGIN_URL))
def test_layout_swh_web_version_number_display(client):
diff --git a/swh/web/utils/__init__.py b/swh/web/utils/__init__.py
--- a/swh/web/utils/__init__.py
+++ b/swh/web/utils/__init__.py
@@ -319,6 +319,8 @@
"lang": "en",
"sidebar_state": request.COOKIES.get("sidebar-state", "expanded"),
"SWH_DJANGO_APPS": settings.SWH_DJANGO_APPS,
+ "login_url": settings.LOGIN_URL,
+ "logout_url": settings.LOGOUT_URL,
}
diff --git a/swh/web/webapp/templates/layout.html b/swh/web/webapp/templates/layout.html
--- a/swh/web/webapp/templates/layout.html
+++ b/swh/web/webapp/templates/layout.html
@@ -112,27 +112,20 @@
<span id="swh-current-status-description">Operational</span>
<i class="swh-current-status-indicator green"></i>
</a>
- {% url 'logout' as logout_url %}
{% if user.is_authenticated %}
Logged in as
{% if 'OIDC' in user.backend %}
<a id="swh-login" href="{% url 'oidc-profile' %}"><strong>{{ user.username }}</strong></a>,
- <a href= "{% url 'oidc-logout' %}?next_path={% url 'logout' %}?remote_user=1">logout</a>
+ <a href= "{% url 'oidc-logout' %}?next={% url 'logout' %}?remote_user=1">logout</a>
{% else %}
<strong id="swh-login">{{ user.username }}</strong>,
- <a href="{{ logout_url }}">logout</a>
- {% endif %}
- {% elif oidc_enabled %}
- {% if request.path != logout_url %}
- <a id="swh-login" href="{% url 'oidc-login' %}?next_path={{ request.build_absolute_uri }}">login</a>
- {% else %}
- <a id="swh-login" href="{% url 'oidc-login' %}">login</a>
+ <a href="{% url logout_url %}">logout</a>
{% endif %}
{% else %}
- {% if request.path != logout_url %}
- <a id="swh-login" href="{% url 'login' %}?next={{ request.build_absolute_uri }}">login</a>
+ {% if request.resolver_match.url_name != logout_url %}
+ <a id="swh-login" href="{% url login_url %}?next={{ request.build_absolute_uri }}">login</a>
{% else %}
- <a id="swh-login" href="{% url 'login' %}">login</a>
+ <a id="swh-login" href="{% url login_url %}">login</a>
{% endif %}
{% endif %}
</li>

File Metadata

Mime Type
text/plain
Expires
Dec 20 2024, 6:32 AM (11 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3227725

Event Timeline