diff --git a/tests/unit/packaging/test_models.py b/tests/unit/packaging/test_models.py index c4fce0c55775..f214af20062d 100644 --- a/tests/unit/packaging/test_models.py +++ b/tests/unit/packaging/test_models.py @@ -758,6 +758,33 @@ def test_trusted_published_all(self, db_session): assert release.trusted_published + @pytest.mark.parametrize( + "url, publisher_url, expected", + [ + ("xpto.com", "https://pub/url/", False), # Totally different + ("https://pub/", "https://pub/url/", False), # Missing parts + ("https://pub/url/", "https://pub/url/", True), # Exactly the same + ("https://pub/url/blah.md", "https://pub/url/", True), # Additonal parts + ("https://pub/url", "https://pub/url/", True), # Missing trailing slash + ("https://pub/url/", "https://pub/url", True), # Extratrailing slash + ], + ) + def test_is_url_verified(self, db_session, url, publisher_url, expected): + project = DBProjectFactory.create() + release = DBReleaseFactory.create(project=project) + release_file = DBFileFactory.create( + release=release, + filename=f"{release.project.name}-{release.version}.tar.gz", + python_version="source", + ) + DBFileEventFactory.create( + source=release_file, + tag="fake:event", + additional={"publisher_url": publisher_url}, + ) + + assert project.is_verified_url(url) is expected + def test_trusted_published_mixed(self, db_session): release = DBReleaseFactory.create() rfile_1 = DBFileFactory.create( diff --git a/warehouse/locale/messages.pot b/warehouse/locale/messages.pot index 7ca6dceb826d..17938a78c475 100644 --- a/warehouse/locale/messages.pot +++ b/warehouse/locale/messages.pot @@ -776,7 +776,7 @@ msgstr "" #: warehouse/templates/includes/accounts/profile-actions.html:30 #: warehouse/templates/includes/accounts/profile-callout.html:18 #: warehouse/templates/includes/hash-modal.html:23 -#: warehouse/templates/includes/packaging/project-data.html:106 +#: warehouse/templates/includes/packaging/project-data.html:125 #: warehouse/templates/index.html:100 warehouse/templates/index.html:104 #: warehouse/templates/manage/account.html:228 #: warehouse/templates/manage/account.html:234 @@ -3077,47 +3077,48 @@ msgstr "" msgid "Members" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:16 +#: warehouse/templates/includes/packaging/project-data.html:27 msgid "Verified details" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:31 +#: warehouse/templates/includes/packaging/project-data.html:30 +#: warehouse/templates/includes/packaging/project-data.html:73 +msgid "Project links" +msgstr "" + +#: warehouse/templates/includes/packaging/project-data.html:52 msgid "Maintainers" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:33 +#: warehouse/templates/includes/packaging/project-data.html:54 msgid "Avatar for {username} from gravatar.com" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:49 +#: warehouse/templates/includes/packaging/project-data.html:70 msgid "Unverified details" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:52 -msgid "Project links" -msgstr "" - -#: warehouse/templates/includes/packaging/project-data.html:66 +#: warehouse/templates/includes/packaging/project-data.html:85 msgid "GitHub Statistics" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:74 +#: warehouse/templates/includes/packaging/project-data.html:93 msgid "Stars:" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:82 +#: warehouse/templates/includes/packaging/project-data.html:101 msgid "Forks:" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:90 +#: warehouse/templates/includes/packaging/project-data.html:109 msgid "Open issues:" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:98 +#: warehouse/templates/includes/packaging/project-data.html:117 msgid "Open PRs:" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:106 +#: warehouse/templates/includes/packaging/project-data.html:125 #, python-format msgid "" "View statistics for this project via our public dataset on Google BigQuery" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:113 +#: warehouse/templates/includes/packaging/project-data.html:132 msgid "Meta" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:115 +#: warehouse/templates/includes/packaging/project-data.html:134 msgid "License:" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:118 -#: warehouse/templates/includes/packaging/project-data.html:120 +#: warehouse/templates/includes/packaging/project-data.html:137 +#: warehouse/templates/includes/packaging/project-data.html:139 msgid "Author:" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:123 -#: warehouse/templates/includes/packaging/project-data.html:125 +#: warehouse/templates/includes/packaging/project-data.html:142 +#: warehouse/templates/includes/packaging/project-data.html:144 #: warehouse/templates/pages/help.html:610 msgid "Maintainer:" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:130 +#: warehouse/templates/includes/packaging/project-data.html:149 msgid "Tags" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:140 +#: warehouse/templates/includes/packaging/project-data.html:159 msgid "Requires:" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:145 +#: warehouse/templates/includes/packaging/project-data.html:164 msgid "Provides-Extra:" msgstr "" -#: warehouse/templates/includes/packaging/project-data.html:153 +#: warehouse/templates/includes/packaging/project-data.html:172 #: warehouse/templates/pages/classifiers.html:16 #: warehouse/templates/pages/classifiers.html:21 #: warehouse/templates/pages/sitemap.html:39 diff --git a/warehouse/packaging/models.py b/warehouse/packaging/models.py index dd4ddca38c68..086cbe029ccd 100644 --- a/warehouse/packaging/models.py +++ b/warehouse/packaging/models.py @@ -33,6 +33,7 @@ Text, UniqueConstraint, func, + literal, or_, orm, sql, @@ -399,6 +400,23 @@ def latest_version(self): .first() ) + def is_verified_url(self, url): + url = url.rstrip("/") + "/" # Ensure a single trailing slash + return ( + orm.object_session(self) + .query(File.Event) + .join(File) + .join(Release) + .join(Project) + .filter(Project.id == self.id) + .filter( + literal(url).ilike( + File.Event.additional.op("->>")("publisher_url") + "%" + ) + ) + .scalar() + ) is not None + class DependencyKind(enum.IntEnum): requires = 1 diff --git a/warehouse/templates/includes/packaging/project-data.html b/warehouse/templates/includes/packaging/project-data.html index b262a3df46b4..0e4d61bfef32 100644 --- a/warehouse/templates/includes/packaging/project-data.html +++ b/warehouse/templates/includes/packaging/project-data.html @@ -12,9 +12,30 @@ # limitations under the License. -#} +{% set verified_urls, unverified_urls = [],[] %} +{% for name, url in release.urls.items() %} + {% if is_valid_uri(url) %} + {% if name in ["Source", "Homepage", "Issues"] and project.is_verified_url(url) %} + {{ verified_urls.append((name, url)) }} + {% else %} + {{ unverified_urls.append((name, url)) }} + {% endif %} + {% endif %} +{% endfor %} +