diff --git a/swh/web/api/views/origin_save.py b/swh/web/api/views/origin_save.py --- a/swh/web/api/views/origin_save.py +++ b/swh/web/api/views/origin_save.py @@ -1,10 +1,11 @@ -# Copyright (C) 2018-2019 The Software Heritage developers +# Copyright (C) 2018-2021 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 from swh.web.api.apidoc import api_doc, format_docstring from swh.web.api.apiurls import api_route +from swh.web.auth.utils import SWH_AMBASSADOR_PERMISSION from swh.web.common.origin_save import ( create_save_origin_request, get_save_origin_requests, @@ -83,7 +84,10 @@ """ if request.method == "POST": - sor = create_save_origin_request(visit_type, origin_url) + bypass_pending_review = request.user.is_authenticated and request.user.has_perm( + SWH_AMBASSADOR_PERMISSION + ) + sor = create_save_origin_request(visit_type, origin_url, bypass_pending_review) del sor["id"] else: sor = get_save_origin_requests(visit_type, origin_url) diff --git a/swh/web/auth/utils.py b/swh/web/auth/utils.py --- a/swh/web/auth/utils.py +++ b/swh/web/auth/utils.py @@ -12,6 +12,8 @@ OIDC_SWH_WEB_CLIENT_ID = "swh-web" +SWH_AMBASSADOR_PERMISSION = "swh.ambassador" + def _get_fernet(password: bytes, salt: bytes) -> Fernet: """ diff --git a/swh/web/common/origin_save.py b/swh/web/common/origin_save.py --- a/swh/web/common/origin_save.py +++ b/swh/web/common/origin_save.py @@ -72,7 +72,7 @@ return [origin.url for origin in SaveUnauthorizedOrigin.objects.all()] -def can_save_origin(origin_url: str) -> str: +def can_save_origin(origin_url: str, bypass_pending_review: bool = False) -> str: """ Check if a software origin can be saved into the archive. @@ -99,8 +99,14 @@ if origin_url.startswith(url_prefix): return SAVE_REQUEST_ACCEPTED - # otherwise, the origin url needs to be manually verified - return SAVE_REQUEST_PENDING + # otherwise, the origin url needs to be manually verified if the user + # that submitted it does not have special permission + if bypass_pending_review: + # mark the origin URL as trusted in that case + SaveAuthorizedOrigin.objects.get_or_create(url=origin_url) + return SAVE_REQUEST_ACCEPTED + else: + return SAVE_REQUEST_PENDING # map visit type to scheduler task @@ -332,7 +338,7 @@ def create_save_origin_request( - visit_type: str, origin_url: str + visit_type: str, origin_url: str, bypass_pending_review: bool = False ) -> SaveOriginRequestInfo: """ Create a loading task to save a software origin into the archive. @@ -374,7 +380,7 @@ _check_origin_url_valid(origin_url) _check_origin_exists(origin_url) # if all checks passed so far, we can try and save the origin - save_request_status = can_save_origin(origin_url) + save_request_status = can_save_origin(origin_url, bypass_pending_review) task = None # if the origin save request is accepted, create a scheduler diff --git a/swh/web/tests/api/views/test_origin_save.py b/swh/web/tests/api/views/test_origin_save.py --- a/swh/web/tests/api/views/test_origin_save.py +++ b/swh/web/tests/api/views/test_origin_save.py @@ -7,8 +7,10 @@ import pytest +from django.core.exceptions import ObjectDoesNotExist from django.utils import timezone +from swh.web.auth.utils import SWH_AMBASSADOR_PERMISSION from swh.web.common.models import ( SAVE_REQUEST_ACCEPTED, SAVE_REQUEST_PENDING, @@ -433,3 +435,54 @@ ) check_api_post_responses(api_client, url, status_code=500) + + +@pytest.fixture +def origin_to_review(): + return "https://git.example.org/user/project" + + +def test_create_save_request_pending_review_anonymous_user( + api_client, origin_to_review, requests_mock +): + + # see swh.web.common.origin_save.origin_exists + requests_mock.head( + origin_to_review, status_code=200, + ) + + url = reverse( + "api-1-save-origin", + url_args={"visit_type": "git", "origin_url": origin_to_review}, + ) + + response = check_api_post_responses(api_client, url, status_code=200) + + assert response.data["save_request_status"] == SAVE_REQUEST_PENDING + + with pytest.raises(ObjectDoesNotExist): + SaveAuthorizedOrigin.objects.get(url=origin_to_review) + + +def test_create_save_request_accepted_ambassador_user( + api_client, origin_to_review, requests_mock, keycloak_oidc, mocker +): + + keycloak_oidc.realm_permissions += [SWH_AMBASSADOR_PERMISSION] + oidc_profile = keycloak_oidc.login() + api_client.credentials(HTTP_AUTHORIZATION=f"Bearer {oidc_profile['refresh_token']}") + + # see swh.web.common.origin_save.origin_exists + requests_mock.head( + origin_to_review, status_code=200, + ) + + check_created_save_request_status( + api_client, + mocker, + origin_to_review, + expected_request_status=SAVE_REQUEST_ACCEPTED, + expected_task_status=SAVE_TASK_NOT_YET_SCHEDULED, + ) + + assert SaveAuthorizedOrigin.objects.get(url=origin_to_review)