From dc9387a929b3febf3aae368d78c923822562f403 Mon Sep 17 00:00:00 2001 From: amvanbaren Date: Thu, 16 May 2024 18:53:05 +0300 Subject: [PATCH] Use public profile - Use public profile when checking Eclipse publisher agreement - Use public profile when checking if publisher is compliant - Add testGetPublicProfile unit test back in --- .../java/org/eclipse/openvsx/UserAPI.java | 6 +- .../eclipse/openvsx/admin/AdminService.java | 7 +- .../openvsx/eclipse/EclipseService.java | 66 ++++++++++++++++--- .../eclipse/PublisherComplianceChecker.java | 12 ++-- .../openvsx/eclipse/EclipseServiceTest.java | 16 +++++ 5 files changed, 79 insertions(+), 28 deletions(-) diff --git a/server/src/main/java/org/eclipse/openvsx/UserAPI.java b/server/src/main/java/org/eclipse/openvsx/UserAPI.java index d4fb2e228..78ff380c8 100644 --- a/server/src/main/java/org/eclipse/openvsx/UserAPI.java +++ b/server/src/main/java/org/eclipse/openvsx/UserAPI.java @@ -112,11 +112,7 @@ public UserJson getUserData() { json.role = user.getRole(); json.tokensUrl = createApiUrl(serverUrl, "user", "tokens"); json.createTokenUrl = createApiUrl(serverUrl, "user", "token", "create"); - try { - eclipse.enrichUserJson(json, user); - } catch (ErrorResultException e) { - logger.error("Failed to enrich UserJson", e); - } + eclipse.enrichUserJson(json, user); return json; } diff --git a/server/src/main/java/org/eclipse/openvsx/admin/AdminService.java b/server/src/main/java/org/eclipse/openvsx/admin/AdminService.java index c6a4d5a56..5628a5ef7 100644 --- a/server/src/main/java/org/eclipse/openvsx/admin/AdminService.java +++ b/server/src/main/java/org/eclipse/openvsx/admin/AdminService.java @@ -265,12 +265,7 @@ public UserPublishInfoJson getUserPublishInfo(String provider, String loginName) var userPublishInfo = new UserPublishInfoJson(); userPublishInfo.user = user.toUserJson(); - try { - eclipse.enrichUserJson(userPublishInfo.user, user); - } catch (ErrorResultException e) { - userPublishInfo.user.error = e.getMessage(); - } - + eclipse.adminEnrichUserJson(userPublishInfo.user, user); userPublishInfo.activeAccessTokenNum = (int) repositories.countActiveAccessTokens(user); var extVersions = repositories.findLatestVersions(user); var types = new String[]{DOWNLOAD, MANIFEST, ICON, README, LICENSE, CHANGELOG, VSIXMANIFEST}; diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java index bd00033da..95a1e65f5 100644 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java +++ b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java @@ -114,18 +114,47 @@ public void checkPublisherAgreement(UserData user) { if (personId == null) { throw new ErrorResultException("You must log in with an Eclipse Foundation account and sign a Publisher Agreement before publishing any extension."); } - var agreement = getPublisherAgreement(user); - if (agreement == null || agreement.version == null) { + var profile = getPublicProfile(personId); + if (profile.publisherAgreements == null || profile.publisherAgreements.openVsx == null + || profile.publisherAgreements.openVsx.version == null) { throw new ErrorResultException("You must sign a Publisher Agreement with the Eclipse Foundation before publishing any extension."); } - if (!publisherAgreementVersion.equals(agreement.version)) { + if (!publisherAgreementVersion.equals(profile.publisherAgreements.openVsx.version)) { throw new ErrorResultException("Your Publisher Agreement with the Eclipse Foundation is outdated (version " - + agreement.version + "). The current version is " + + profile.publisherAgreements.openVsx.version + "). The current version is " + publisherAgreementVersion + "."); } }); } + /** + * Get the publicly available user profile. + */ + public EclipseProfile getPublicProfile(String personId) { + checkApiUrl(); + var urlTemplate = eclipseApiUrl + "account/profile/{personId}"; + var uriVariables = Map.of("personId", personId); + var headers = new HttpHeaders(); + headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); + var request = new HttpEntity(headers); + + try { + var response = restTemplate.exchange(urlTemplate, HttpMethod.GET, request, String.class, uriVariables); + return parseEclipseProfile(response); + } catch (RestClientException exc) { + if (exc instanceof HttpStatusCodeException) { + var status = ((HttpStatusCodeException) exc).getStatusCode(); + if (status == HttpStatus.NOT_FOUND) + throw new ErrorResultException("No Eclipse profile data available for user: " + personId); + } + + var url = UriComponentsBuilder.fromUriString(urlTemplate).build(uriVariables); + logger.error("Get request failed with URL: " + url, exc); + throw new ErrorResultException("Request for retrieving user profile failed: " + exc.getMessage(), + HttpStatus.INTERNAL_SERVER_ERROR); + } + } + /** * Update the given user data with a profile obtained from Eclipse API. */ @@ -151,7 +180,6 @@ public void enrichUserJson(UserJson json, UserData user) { } var usableToken = true; - ErrorResultException exception = null; try { // Add information on the publisher agreement var agreement = getPublisherAgreement(user); @@ -167,7 +195,7 @@ else if (publisherAgreementVersion.equals(agreement.version)) if(e.getStatus() == HttpStatus.FORBIDDEN) { usableToken = false; } else { - exception = e; + logger.info("Failed to enrich UserJson", e); } } @@ -182,10 +210,30 @@ else if (publisherAgreementVersion.equals(agreement.version)) else json.additionalLogins.add(eclipseLogin); } + } + + public void adminEnrichUserJson(UserJson json, UserData user) { + if (!isActive()) { + return; + } + + json.publisherAgreement = new UserJson.PublisherAgreement(); + var personId = user.getEclipsePersonId(); + if (personId == null) { + json.publisherAgreement.status = "none"; + return; + } - // Throw exception at end of method, so that JSON data is fully enriched - if(exception != null) { - throw exception; + try { + var profile = getPublicProfile(personId); + if (profile.publisherAgreements == null || profile.publisherAgreements.openVsx == null || StringUtils.isEmpty(profile.publisherAgreements.openVsx.version)) + json.publisherAgreement.status = "none"; + else if (publisherAgreementVersion.equals(profile.publisherAgreements.openVsx.version)) + json.publisherAgreement.status = "signed"; + else + json.publisherAgreement.status = "outdated"; + } catch (ErrorResultException e) { + logger.error("Failed to get public profile", e); } } diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java b/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java index 786181450..c89d99314 100644 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java +++ b/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java @@ -86,14 +86,10 @@ private boolean isCompliant(UserData user) { return false; } - var json = new UserJson(); - try { - eclipseService.enrichUserJson(json, user); - return json.publisherAgreement.status == null || !json.publisherAgreement.status.equals("none"); - } catch(ErrorResultException e) { - // no way to determine whether the user has a publisher agreement - return true; - } + var profile = eclipseService.getPublicProfile(user.getEclipsePersonId()); + return profile.publisherAgreements != null + && profile.publisherAgreements.openVsx != null + && profile.publisherAgreements.openVsx.version != null; } private void deactivateExtensions(Streamable accessTokens) { diff --git a/server/src/test/java/org/eclipse/openvsx/eclipse/EclipseServiceTest.java b/server/src/test/java/org/eclipse/openvsx/eclipse/EclipseServiceTest.java index 88157da3d..8018061cc 100644 --- a/server/src/test/java/org/eclipse/openvsx/eclipse/EclipseServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/eclipse/EclipseServiceTest.java @@ -90,6 +90,22 @@ public void setup() { eclipse.eclipseApiUrl = "https://test.openvsx.eclipse.org/"; } + @Test + public void testGetPublicProfile() throws Exception { + var urlTemplate = "https://test.openvsx.eclipse.org/account/profile/{personId}"; + Mockito.when(restTemplate.exchange(eq(urlTemplate), eq(HttpMethod.GET), any(HttpEntity.class), eq(String.class), eq(Map.of("personId", "test")))) + .thenReturn(mockProfileResponse()); + + var profile = eclipse.getPublicProfile("test"); + + assertThat(profile).isNotNull(); + assertThat(profile.name).isEqualTo("test"); + assertThat(profile.githubHandle).isEqualTo("test"); + assertThat(profile.publisherAgreements).isNotNull(); + assertThat(profile.publisherAgreements.openVsx).isNotNull(); + assertThat(profile.publisherAgreements.openVsx.version).isEqualTo("1"); + } + @Test public void testGetUserProfile() throws Exception { Mockito.when(restTemplate.exchange(any(RequestEntity.class), eq(String.class)))