Skip to content
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ filterwarnings = [
# https://github.com/Pylons/pyramid/issues/3731
'ignore:pkg_resources is deprecated as an API.*:UserWarning:pyramid.asset',
# https://github.com/Pylons/webob/issues/473
'ignore:datetime\.datetime\.utcnow\(\) is deprecated.*:DeprecationWarning:webob.cookies',
'ignore:datetime\.datetime\.utcnow\(\) is deprecated.*:DeprecationWarning:webob.*',
# https://github.com/pypi/warehouse/issues/15454#issuecomment-2599321232
'ignore:Accessing argon2.__version__ is deprecated.*:DeprecationWarning:passlib.handlers.argon2',
# https://github.com/zopefoundation/meta/issues/194
Expand Down
325 changes: 325 additions & 0 deletions tests/functional/manage/test_organization_publishing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
# SPDX-License-Identifier: Apache-2.0

import time

from http import HTTPStatus

import pytest
import responses

from tests.common.db.accounts import UserFactory
from tests.common.db.organizations import OrganizationFactory, OrganizationRoleFactory
from warehouse.organizations.models import OrganizationRoleType
from warehouse.utils.otp import _get_totp


@pytest.mark.usefixtures("_enable_all_oidc_providers")
class TestManageOrganizationPublishing:
@responses.activate
def test_add_pending_github_publisher_to_organization(self, webtest):
"""
An authenticated user who is an organization owner can add a pending
GitHub trusted publisher to their organization.
"""
# Arrange: Create a user with an organization
user = UserFactory.create(
with_verified_primary_email=True,
with_terms_of_service_agreement=True,
clear_pwd="password",
)
organization = OrganizationFactory.create(name="test-organization")
OrganizationRoleFactory.create(
user=user,
organization=organization,
role_name=OrganizationRoleType.Owner,
)

# Mock GitHub API for owner validation
responses.add(
responses.GET,
"https://api.github.com/users/test-owner",
json={
"id": 123456,
"login": "test-owner",
},
status=200,
)

# Act: Log in
login_page = webtest.get("/account/login/", status=HTTPStatus.OK)
login_form = login_page.forms["login-form"]
csrf_token = login_form["csrf_token"].value
login_form["username"] = user.username
login_form["password"] = "password"

# Handle 2FA
two_factor_page = login_form.submit().follow(status=HTTPStatus.OK)
two_factor_form = two_factor_page.forms["totp-auth-form"]
two_factor_form["csrf_token"] = csrf_token
two_factor_form["totp_value"] = (
_get_totp(user.totp_secret).generate(time.time()).decode()
)
two_factor_form.submit().follow(status=HTTPStatus.OK)

# Navigate to organization publishing page
publishing_page = webtest.get(
f"/manage/organization/{organization.normalized_name}/publishing/",
status=HTTPStatus.OK,
)

# Get logged-in CSRF token
logged_in_csrf_token = publishing_page.html.find(
"input", {"name": "csrf_token"}
)["value"]

# Fill out the GitHub pending publisher form
github_form = publishing_page.forms["pending-github-publisher-form"]
github_form["csrf_token"] = logged_in_csrf_token
github_form["project_name"] = "test-org-project"
github_form["owner"] = "test-owner"
github_form["repository"] = "test-repo"
github_form["workflow_filename"] = "release.yml"
github_form["environment"] = "" # Optional field

# Submit the form, redirects back to the same page on success
response = github_form.submit(status=HTTPStatus.SEE_OTHER)
response.follow(status=HTTPStatus.OK)

# Assert: Verify success
# Check flash messages via the JavaScript endpoint
flash_messages = webtest.get(
"/_includes/unauthed/flash-messages/", status=HTTPStatus.OK
)
success_message = flash_messages.html.find(
"span", {"class": "notification-bar__message"}
)
assert success_message is not None
assert "Registered a new pending publisher" in success_message.text
assert "test-org-project" in success_message.text
assert organization.name in success_message.text

def test_add_pending_gitlab_publisher_to_organization(self, webtest):
"""
An authenticated user who is an organization owner can add a pending
GitLab trusted publisher to their organization.
"""
# Arrange: Create a user with an organization
user = UserFactory.create(
with_verified_primary_email=True,
with_terms_of_service_agreement=True,
clear_pwd="password",
)
organization = OrganizationFactory.create(name="test-organization")
OrganizationRoleFactory.create(
user=user,
organization=organization,
role_name=OrganizationRoleType.Owner,
)

# Act: Log in
login_page = webtest.get("/account/login/", status=HTTPStatus.OK)
login_form = login_page.forms["login-form"]
csrf_token = login_form["csrf_token"].value
login_form["username"] = user.username
login_form["password"] = "password"

# Handle 2FA
two_factor_page = login_form.submit().follow(status=HTTPStatus.OK)
two_factor_form = two_factor_page.forms["totp-auth-form"]
two_factor_form["csrf_token"] = csrf_token
two_factor_form["totp_value"] = (
_get_totp(user.totp_secret).generate(time.time()).decode()
)
two_factor_form.submit().follow(status=HTTPStatus.OK)

# Navigate to organization publishing page
publishing_page = webtest.get(
f"/manage/organization/{organization.normalized_name}/publishing/",
status=HTTPStatus.OK,
)

# Get logged-in CSRF token
logged_in_csrf_token = publishing_page.html.find(
"input", {"name": "csrf_token"}
)["value"]

# Fill out the GitLab pending publisher form
gitlab_form = publishing_page.forms["pending-gitlab-publisher-form"]
gitlab_form["csrf_token"] = logged_in_csrf_token
gitlab_form["project_name"] = "test-org-gitlab-project"
gitlab_form["namespace"] = "test-namespace"
gitlab_form["project"] = "test-project"
gitlab_form["workflow_filepath"] = ".gitlab-ci.yml"
gitlab_form["environment"] = "" # Optional field
# issuer_url is a hidden field with default value

# Submit the form, redirects back to the same page on success
response = gitlab_form.submit(status=HTTPStatus.SEE_OTHER)
response.follow(status=HTTPStatus.OK)

# Assert: Verify success
flash_messages = webtest.get(
"/_includes/unauthed/flash-messages/", status=HTTPStatus.OK
)
success_message = flash_messages.html.find(
"span", {"class": "notification-bar__message"}
)
assert success_message is not None
assert "Registered a new pending publisher" in success_message.text
assert "test-org-gitlab-project" in success_message.text
assert organization.name in success_message.text

def test_add_pending_google_publisher_to_organization(self, webtest):
"""
An authenticated user who is an organization owner can add a pending
Google trusted publisher to their organization.
"""
# Arrange: Create a user with an organization
user = UserFactory.create(
with_verified_primary_email=True,
with_terms_of_service_agreement=True,
clear_pwd="password",
)
organization = OrganizationFactory.create(name="test-organization")
OrganizationRoleFactory.create(
user=user,
organization=organization,
role_name=OrganizationRoleType.Owner,
)

# Act: Log in
login_page = webtest.get("/account/login/", status=HTTPStatus.OK)
login_form = login_page.forms["login-form"]
csrf_token = login_form["csrf_token"].value
login_form["username"] = user.username
login_form["password"] = "password"

# Handle 2FA
two_factor_page = login_form.submit().follow(status=HTTPStatus.OK)
two_factor_form = two_factor_page.forms["totp-auth-form"]
two_factor_form["csrf_token"] = csrf_token
two_factor_form["totp_value"] = (
_get_totp(user.totp_secret).generate(time.time()).decode()
)
two_factor_form.submit().follow(status=HTTPStatus.OK)

# Navigate to organization publishing page
publishing_page = webtest.get(
f"/manage/organization/{organization.normalized_name}/publishing/",
status=HTTPStatus.OK,
)

# Get logged-in CSRF token
logged_in_csrf_token = publishing_page.html.find(
"input", {"name": "csrf_token"}
)["value"]

# Fill out the Google pending publisher form
google_form = publishing_page.forms["pending-google-publisher-form"]
google_form["csrf_token"] = logged_in_csrf_token
google_form["project_name"] = "test-org-google-project"
google_form["email"] = "test@example.com"
google_form["sub"] = "" # Optional field

# Submit the form, redirects back to the same page on success
response = google_form.submit(status=HTTPStatus.SEE_OTHER)
response.follow(status=HTTPStatus.OK)

# Assert: Verify success
flash_messages = webtest.get(
"/_includes/unauthed/flash-messages/", status=HTTPStatus.OK
)
success_message = flash_messages.html.find(
"span", {"class": "notification-bar__message"}
)
assert success_message is not None
assert "Registered a new pending publisher" in success_message.text
assert "test-org-google-project" in success_message.text
assert organization.name in success_message.text

@responses.activate
def test_add_pending_activestate_publisher_to_organization(self, webtest):
"""
An authenticated user who is an organization owner can add a pending
ActiveState trusted publisher to their organization.
"""
# Arrange: Create a user with an organization
user = UserFactory.create(
with_verified_primary_email=True,
with_terms_of_service_agreement=True,
clear_pwd="password",
)
organization = OrganizationFactory.create(name="test-organization")
OrganizationRoleFactory.create(
user=user,
organization=organization,
role_name=OrganizationRoleType.Owner,
)

# Mock ActiveState API for organization and actor validation
# The form makes two sequential API calls:
# 1. Organization validation (validate_organization method)
# 2. Actor validation (validate_actor method)
responses.add(
responses.POST,
"https://platform.activestate.com/graphql/v1/graphql",
json={"data": {"organizations": [{"added": "2020-01-01"}]}},
status=200,
)
responses.add(
responses.POST,
"https://platform.activestate.com/graphql/v1/graphql",
json={"data": {"users": [{"user_id": "test-user-id"}]}},
status=200,
)

# Act: Log in
login_page = webtest.get("/account/login/", status=HTTPStatus.OK)
login_form = login_page.forms["login-form"]
csrf_token = login_form["csrf_token"].value
login_form["username"] = user.username
login_form["password"] = "password"

# Handle 2FA
two_factor_page = login_form.submit().follow(status=HTTPStatus.OK)
two_factor_form = two_factor_page.forms["totp-auth-form"]
two_factor_form["csrf_token"] = csrf_token
two_factor_form["totp_value"] = (
_get_totp(user.totp_secret).generate(time.time()).decode()
)
two_factor_form.submit().follow(status=HTTPStatus.OK)

# Navigate to organization publishing page
publishing_page = webtest.get(
f"/manage/organization/{organization.normalized_name}/publishing/",
status=HTTPStatus.OK,
)

# Get logged-in CSRF token
logged_in_csrf_token = publishing_page.html.find(
"input", {"name": "csrf_token"}
)["value"]

# Fill out the ActiveState pending publisher form
activestate_form = publishing_page.forms["pending-activestate-publisher-form"]
activestate_form["csrf_token"] = logged_in_csrf_token
activestate_form["project_name"] = "test-org-activestate-project"
activestate_form["organization"] = "test-activestate-org"
activestate_form["project"] = "test-activestate-project"
activestate_form["actor"] = "test-actor"

# Submit the form, redirects back to the same page on success
response = activestate_form.submit(status=HTTPStatus.SEE_OTHER)
response.follow(status=HTTPStatus.OK)

# Assert: Verify success
flash_messages = webtest.get(
"/_includes/unauthed/flash-messages/", status=HTTPStatus.OK
)
success_message = flash_messages.html.find(
"span", {"class": "notification-bar__message"}
)
assert success_message is not None
assert "Registered a new pending publisher" in success_message.text
assert "test-org-activestate-project" in success_message.text
assert organization.name in success_message.text
Loading