From a596db555c47efa9c63347d444142d064c1f813e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20G=C3=B3mez?= Date: Thu, 30 Jul 2026 10:03:20 +0200 Subject: [PATCH 1/2] feat: publish the server as a consumable library Publish the java component (the plain jar, which the Spring Boot plugin already builds, plus real dependency metadata in the POM and Gradle module metadata) alongside the existing bootJar artifact, which is not consumable as a dependency. Mirror the dependency-management plugin's effective managed versions as dependency constraints: they apply only inside this project, so a consumer resolving the library would otherwise find versionless dependencies. No behavior change for this build; resolution is identical. --- server/build.gradle | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/server/build.gradle b/server/build.gradle index 836326b11..ed23f5f0a 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'java' + id 'java-library' id 'scala' id 'jacoco' alias(libs.plugins.jooq.codegen) @@ -166,6 +166,26 @@ dependencies { rewrite libs.rewrite.java } +// The io.spring.dependency-management plugin resolves the versionless dependencies +// above only within this project; library consumers -- via the published metadata or +// a composite build -- would find dependencies without any version at all. Mirror the +// effective managed versions (Spring Boot BOM + the overrides above) as dependency +// constraints so consumers can resolve the same baseline. For coordinates declared +// above with an explicit version, the declared pin wins over the BOM version, like it +// does in this project's own resolution. +dependencies { + constraints { + def declared = [:] + ['api', 'implementation', 'compileOnly', 'runtimeOnly'] + .collectMany { configurations.findByName(it)?.dependencies ?: [] } + .findAll { it.version } + .each { declared["${it.group}:${it.name}" as String] = it.version } + (dependencyManagement.managedVersions + declared).each { ga, pin -> + api "${ga}:${pin}" + } + } +} + configurations.configureEach { resolutionStrategy { // due to security vulnerabilities in commons-compress < 1.26.0 @@ -217,6 +237,10 @@ rewrite { publishing { publications { maven(MavenPublication) { + // Publish the java component so the server is consumable as a library: + // the plain jar (classifier 'plain') plus dependency metadata in the POM. + // The executable bootJar (no classifier) remains the main artifact. + from components.java artifact ('scripts/run-server.sh') artifact bootJar } From b0cc7f2da381843b58c7479e515f7167e74b4bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jordi=20G=C3=B3mez?= Date: Thu, 30 Jul 2026 10:06:38 +0200 Subject: [PATCH 2/2] feat: extract the Eclipse publisher agreement behind deployment seams The org.eclipse.openvsx.eclipse package moves to EclipseFdn/open-vsx.org, which consumes this server as a library and registers the code through Spring Boot auto-configuration. Upstream keeps two small, generic seams any deployment could implement: PublisherAgreementService (publishing gate, user/admin profile enrichment, admin revocation; no-op by default) and OAuth2LoginHandler (per-registration login handling, post-login token capture and redirect), replacing the hardcoded 'eclipse' branches in the security package. The agreement-only POST /user/publisher-agreement endpoint moves out entirely. UserData's eclipse columns and the UserJson.PublisherAgreement DTO stay to avoid schema and API shape changes. --- .../eclipse/openvsx/LocalRegistryService.java | 13 +- .../java/org/eclipse/openvsx/UserAPI.java | 36 +- .../eclipse/openvsx/admin/AdminService.java | 18 +- .../openvsx/eclipse/EclipseProfile.java | 186 ------ .../openvsx/eclipse/EclipseService.java | 539 ------------------ .../openvsx/eclipse/EclipseTokenService.java | 160 ------ .../openvsx/eclipse/PublisherAgreement.java | 21 - .../eclipse/PublisherAgreementResponse.java | 54 -- .../eclipse/PublisherComplianceChecker.java | 123 ---- .../openvsx/eclipse/SignAgreementParam.java | 54 -- .../publish/PublisherAgreementService.java | 48 ++ .../openvsx/security/CodedAuthException.java | 2 - .../CustomAuthenticationSuccessHandler.java | 20 +- .../openvsx/security/OAuth2LoginHandler.java | 50 ++ .../openvsx/security/OAuth2UserServices.java | 76 +-- .../openvsx/security/SecurityConfig.java | 10 +- .../openvsx/LocalRegistryServiceTest.java | 8 +- .../org/eclipse/openvsx/RegistryAPITest.java | 20 +- .../java/org/eclipse/openvsx/UserAPITest.java | 20 +- .../openvsx/adapter/VSCodeAPITest.java | 17 +- .../eclipse/openvsx/admin/AdminAPITest.java | 23 +- .../openvsx/eclipse/EclipseServiceTest.java | 503 ---------------- .../openvsx/web/SitemapControllerTest.java | 9 +- .../eclipse/profile-allowed-response.json | 43 -- .../eclipse/profile-outdated-response.json | 43 -- .../openvsx/eclipse/profile-response.json | 43 -- ...publisher-agreement-outdated-response.json | 12 - .../eclipse/publisher-agreement-response.json | 12 - 28 files changed, 170 insertions(+), 1993 deletions(-) delete mode 100644 server/src/main/java/org/eclipse/openvsx/eclipse/EclipseProfile.java delete mode 100644 server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java delete mode 100644 server/src/main/java/org/eclipse/openvsx/eclipse/EclipseTokenService.java delete mode 100644 server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreement.java delete mode 100644 server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreementResponse.java delete mode 100644 server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java delete mode 100644 server/src/main/java/org/eclipse/openvsx/eclipse/SignAgreementParam.java create mode 100644 server/src/main/java/org/eclipse/openvsx/publish/PublisherAgreementService.java create mode 100644 server/src/main/java/org/eclipse/openvsx/security/OAuth2LoginHandler.java delete mode 100644 server/src/test/java/org/eclipse/openvsx/eclipse/EclipseServiceTest.java delete mode 100644 server/src/test/resources/org/eclipse/openvsx/eclipse/profile-allowed-response.json delete mode 100644 server/src/test/resources/org/eclipse/openvsx/eclipse/profile-outdated-response.json delete mode 100644 server/src/test/resources/org/eclipse/openvsx/eclipse/profile-response.json delete mode 100644 server/src/test/resources/org/eclipse/openvsx/eclipse/publisher-agreement-outdated-response.json delete mode 100644 server/src/test/resources/org/eclipse/openvsx/eclipse/publisher-agreement-response.json diff --git a/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java b/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java index c7918fea4..9cead9c6b 100644 --- a/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java +++ b/server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java @@ -30,10 +30,10 @@ import org.eclipse.openvsx.accesstoken.AccessTokenService; import org.eclipse.openvsx.cache.CacheService; -import org.eclipse.openvsx.eclipse.EclipseService; import org.eclipse.openvsx.entities.*; import org.eclipse.openvsx.json.*; import org.eclipse.openvsx.publish.ExtensionVersionIntegrityService; +import org.eclipse.openvsx.publish.PublisherAgreementService; import org.eclipse.openvsx.publish.PublishingConfig; import org.eclipse.openvsx.repositories.RepositoryService; import org.eclipse.openvsx.search.ExtensionSearch; @@ -73,7 +73,7 @@ public class LocalRegistryService implements IExtensionRegistry { private final SearchUtilService search; private final ExtensionValidator validator; private final StorageUtilService storageUtil; - private final EclipseService eclipse; + private final PublisherAgreementService publisherAgreement; private final CacheService cache; private final ExtensionVersionIntegrityService integrityService; private final SimilarityCheckService similarityCheckService; @@ -89,7 +89,7 @@ public LocalRegistryService( SearchUtilService search, ExtensionValidator validator, StorageUtilService storageUtil, - EclipseService eclipse, + @Nullable PublisherAgreementService publisherAgreement, CacheService cache, ExtensionVersionIntegrityService integrityService, @Nullable SimilarityCheckService similarityCheckService, @@ -104,7 +104,8 @@ public LocalRegistryService( this.search = search; this.validator = validator; this.storageUtil = storageUtil; - this.eclipse = eclipse; + this.publisherAgreement = publisherAgreement != null ? publisherAgreement : new PublisherAgreementService() { + }; this.cache = cache; this.integrityService = integrityService; this.similarityCheckService = similarityCheckService; @@ -719,7 +720,7 @@ public ResultJson createNamespace(NamespaceJson json, UserData user) { throw new ErrorResultException(namespaceIssue.get().toString()); } - eclipse.checkPublisherAgreement(user); + publisherAgreement.checkPublisherAgreement(user); var namespaceName = repositories.findNamespaceName(json.getName()); if (namespaceName != null) { throw new ErrorResultException("Namespace already exists: " + namespaceName); @@ -790,7 +791,7 @@ public ExtensionJson publish(InputStream content, String tokenValue) throws Erro } // Check whether the user has a valid publisher agreement - eclipse.checkPublisherAgreement(token.getUser()); + publisherAgreement.checkPublisherAgreement(token.getUser()); var extVersion = extensions.publishVersion(content, token); var json = toExtensionVersionJson(extVersion, null, true); diff --git a/server/src/main/java/org/eclipse/openvsx/UserAPI.java b/server/src/main/java/org/eclipse/openvsx/UserAPI.java index f3fbc6ec4..6190fe1ed 100644 --- a/server/src/main/java/org/eclipse/openvsx/UserAPI.java +++ b/server/src/main/java/org/eclipse/openvsx/UserAPI.java @@ -15,6 +15,7 @@ import java.util.stream.Collectors; import jakarta.servlet.http.HttpServletRequest; +import org.jspecify.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Pageable; @@ -35,7 +36,6 @@ import org.springframework.web.server.ResponseStatusException; import org.eclipse.openvsx.accesstoken.AccessTokenService; -import org.eclipse.openvsx.eclipse.EclipseService; import org.eclipse.openvsx.entities.ExtensionVersion; import org.eclipse.openvsx.entities.NamespaceMembership; import org.eclipse.openvsx.entities.ScanStatus; @@ -54,6 +54,7 @@ import org.eclipse.openvsx.json.TargetPlatformVersionJson; import org.eclipse.openvsx.json.UsageStatsListJson; import org.eclipse.openvsx.json.UserJson; +import org.eclipse.openvsx.publish.PublisherAgreementService; import org.eclipse.openvsx.repositories.ExtensionScanRepository; import org.eclipse.openvsx.repositories.RepositoryService; import org.eclipse.openvsx.security.CodedAuthException; @@ -86,7 +87,7 @@ public class UserAPI { private final RepositoryService repositories; private final UserService users; private final AccessTokenService tokens; - private final EclipseService eclipse; + private final PublisherAgreementService publisherAgreement; private final StorageUtilService storageUtil; private final LocalRegistryService local; private final ExtensionService extensions; @@ -96,7 +97,7 @@ public UserAPI( RepositoryService repositories, UserService users, AccessTokenService tokens, - EclipseService eclipse, + @Nullable PublisherAgreementService publisherAgreement, StorageUtilService storageUtil, LocalRegistryService local, ExtensionService extensions, @@ -105,7 +106,8 @@ public UserAPI( this.repositories = repositories; this.users = users; this.tokens = tokens; - this.eclipse = eclipse; + this.publisherAgreement = publisherAgreement != null ? publisherAgreement : new PublisherAgreementService() { + }; this.storageUtil = storageUtil; this.local = local; this.extensions = extensions; @@ -165,7 +167,7 @@ public UserJson getUserData() { json.setRole(user.getRoleAsString()); json.setTokensUrl(createApiUrl(serverUrl, "user", "tokens")); json.setCreateTokenUrl(createApiUrl(serverUrl, "user", "token", "create")); - eclipse.enrichUserJsonWithPublisherAgreement(json, user); + publisherAgreement.enrichUserJsonWithPublisherAgreement(json, user); return json; } @@ -687,28 +689,4 @@ public List getUsersStartWith(@PathVariable String name) { .toList(); } - @PostMapping( - path = "/user/publisher-agreement", - produces = MediaType.APPLICATION_JSON_VALUE - ) - public ResponseEntity signPublisherAgreement() { - var user = users.findLoggedInUser(); - if (user == null) { - return new ResponseEntity<>(HttpStatus.FORBIDDEN); - } - try { - var agreement = eclipse.signPublisherAgreement(user); - var json = user.toUserJson(); - var serverUrl = UrlUtil.getBaseUrl(); - json.setRole(user.getRoleAsString()); - json.setTokensUrl(createApiUrl(serverUrl, "user", "tokens")); - json.setCreateTokenUrl(createApiUrl(serverUrl, "user", "token", "create")); - eclipse.enrichUserJson(json, user, agreement); - - return ResponseEntity.ok(json); - } catch (ErrorResultException exc) { - return exc.toResponseEntity(UserJson.class); - } - } - } 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 b7ca2394b..52eb08c31 100644 --- a/server/src/main/java/org/eclipse/openvsx/admin/AdminService.java +++ b/server/src/main/java/org/eclipse/openvsx/admin/AdminService.java @@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils; import org.jobrunr.scheduling.JobRequestScheduler; import org.jobrunr.scheduling.cron.Cron; +import org.jspecify.annotations.Nullable; import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.context.event.EventListener; import org.springframework.data.domain.Page; @@ -35,7 +36,6 @@ import org.eclipse.openvsx.UserService; import org.eclipse.openvsx.accesstoken.AccessTokenService; import org.eclipse.openvsx.cache.CacheService; -import org.eclipse.openvsx.eclipse.EclipseService; import org.eclipse.openvsx.entities.AdminStatistics; import org.eclipse.openvsx.entities.Extension; import org.eclipse.openvsx.entities.ExtensionReview; @@ -51,6 +51,7 @@ import org.eclipse.openvsx.json.UserRelationshipsJson; import org.eclipse.openvsx.mail.MailService; import org.eclipse.openvsx.migration.HandlerJobRequest; +import org.eclipse.openvsx.publish.PublisherAgreementService; import org.eclipse.openvsx.repositories.RepositoryService; import org.eclipse.openvsx.search.SearchUtilService; import org.eclipse.openvsx.storage.StorageUtilService; @@ -74,7 +75,7 @@ public class AdminService { private final AccessTokenService tokens; private final ExtensionValidator validator; private final SearchUtilService search; - private final EclipseService eclipse; + private final PublisherAgreementService publisherAgreement; private final StorageUtilService storageUtil; private final CacheService cache; private final JobRequestScheduler scheduler; @@ -89,7 +90,7 @@ public AdminService( AccessTokenService tokens, ExtensionValidator validator, SearchUtilService search, - EclipseService eclipse, + @Nullable PublisherAgreementService publisherAgreement, StorageUtilService storageUtil, CacheService cache, JobRequestScheduler scheduler, @@ -103,7 +104,8 @@ public AdminService( this.tokens = tokens; this.validator = validator; this.search = search; - this.eclipse = eclipse; + this.publisherAgreement = publisherAgreement != null ? publisherAgreement : new PublisherAgreementService() { + }; this.storageUtil = storageUtil; this.cache = cache; this.scheduler = scheduler; @@ -393,7 +395,7 @@ public UserPublishInfoJson getUserPublishInfo(String provider, String loginName) var userJson = user.toUserJson(); userJson.setRole(user.getRoleAsString()); userPublishInfo.setUser(userJson); - eclipse.adminEnrichUserJson(userPublishInfo.getUser(), user); + publisherAgreement.adminEnrichUserJson(userPublishInfo.getUser(), user); userPublishInfo.setActiveAccessTokenNum((int) repositories.countActiveAccessTokens(user)); var extVersions = repositories.findLatestVersions(user); var types = new String[] { DOWNLOAD, MANIFEST, ICON, README, LICENSE, CHANGELOG, VSIXMANIFEST }; @@ -471,10 +473,8 @@ public ResultJson revokePublisherContributions(String provider, String loginName throw new ErrorResultException(userNotFoundMessage(loginName), HttpStatus.NOT_FOUND); } - // Send a DELETE request to the Eclipse publisher agreement API - if (eclipse.isActive() && user.getEclipsePersonId() != null) { - eclipse.revokePublisherAgreement(user, admin); - } + // Revoke the user's publisher agreement, if the deployment has any + publisherAgreement.revokePublisherAgreement(user, admin); var accessTokens = repositories.findAccessTokens(user); var affectedExtensions = new LinkedHashSet(); diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseProfile.java b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseProfile.java deleted file mode 100644 index 8551112a2..000000000 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseProfile.java +++ /dev/null @@ -1,186 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 TypeFox and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ -package org.eclipse.openvsx.eclipse; - -import java.util.List; -import java.util.Optional; - -import com.fasterxml.jackson.annotation.JsonProperty; -import tools.jackson.core.JacksonException; -import tools.jackson.core.JsonParser; -import tools.jackson.core.JsonToken; -import tools.jackson.core.type.TypeReference; -import tools.jackson.databind.DeserializationContext; -import tools.jackson.databind.ValueDeserializer; -import tools.jackson.databind.annotation.JsonDeserialize; - -public class EclipseProfile { - - private String uid; - - private String name; - - private String mail; - - private String picture; - - @JsonProperty("first_name") - private String firstName; - - @JsonProperty("last_name") - private String lastName; - - @JsonProperty("full_name") - private String fullName; - - @JsonProperty("github_handle") - private String githubHandle; - - @JsonProperty("twitter_handle") - private String twitterHandle; - - @JsonProperty("publisher_agreements") - @JsonDeserialize(using = PublisherAgreements.Deserializer.class) - private PublisherAgreements publisherAgreements; - - public String getUid() { - return uid; - } - - public void setUid(String uid) { - this.uid = uid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getMail() { - return mail; - } - - public void setMail(String mail) { - this.mail = mail; - } - - public String getPicture() { - return picture; - } - - public void setPicture(String picture) { - this.picture = picture; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } - - public String getFullName() { - return fullName; - } - - public void setFullName(String fullName) { - this.fullName = fullName; - } - - public String getGithubHandle() { - return githubHandle; - } - - public void setGithubHandle(String githubHandle) { - this.githubHandle = githubHandle; - } - - public String getTwitterHandle() { - return twitterHandle; - } - - public void setTwitterHandle(String twitterHandle) { - this.twitterHandle = twitterHandle; - } - - public PublisherAgreements getPublisherAgreements() { - return publisherAgreements; - } - - public void setPublisherAgreements(PublisherAgreements publisherAgreements) { - this.publisherAgreements = publisherAgreements; - } - - public Optional getOpenVsxPublisherAgreement() { - if (publisherAgreements != null && publisherAgreements.getOpenVsx() != null) { - return Optional.of(publisherAgreements.getOpenVsx()); - } else { - return Optional.empty(); - } - } - - public static class PublisherAgreements { - - @JsonProperty("open-vsx") - private PublisherAgreement openVsx; - - public PublisherAgreement getOpenVsx() { - return openVsx; - } - - public void setOpenVsx(PublisherAgreement openVsx) { - this.openVsx = openVsx; - } - - public static class Deserializer extends ValueDeserializer { - - private static final TypeReference> TYPE_LIST_AGREEMENT = new TypeReference<>() { - }; - - @Override - public PublisherAgreements deserialize(JsonParser p, DeserializationContext ctxt) throws JacksonException { - if (p.currentToken() == JsonToken.START_ARRAY) { - var list = ctxt.readValue(p, TYPE_LIST_AGREEMENT); - var result = new PublisherAgreements(); - if (!list.isEmpty()) { - result.openVsx = list.getFirst(); - } - return result; - } - return ctxt.readValue(p, PublisherAgreements.class); - } - - } - } - - public static class PublisherAgreement { - private String version; - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - } -} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java deleted file mode 100644 index 2dee9bd49..000000000 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseService.java +++ /dev/null @@ -1,539 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 TypeFox and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ -package org.eclipse.openvsx.eclipse; - -import java.net.URI; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeFormatterBuilder; -import java.time.format.DateTimeParseException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -import jakarta.persistence.EntityManager; -import jakarta.transaction.Transactional; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.web.client.HttpStatusCodeException; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; -import tools.jackson.core.JacksonException; -import tools.jackson.core.type.TypeReference; -import tools.jackson.databind.json.JsonMapper; - -import org.eclipse.openvsx.ExtensionService; -import org.eclipse.openvsx.entities.AuthToken; -import org.eclipse.openvsx.entities.UserData; -import org.eclipse.openvsx.json.UserJson; -import org.eclipse.openvsx.util.ErrorResultException; -import org.eclipse.openvsx.util.HttpHeadersUtil; -import org.eclipse.openvsx.util.TimeUtil; - -@Service -public class EclipseService { - - private static final String VAR_PERSON_ID = "personId"; - - public static final DateTimeFormatter CUSTOM_DATE_TIME = new DateTimeFormatterBuilder() - .parseCaseInsensitive() - .append(DateTimeFormatter.ISO_LOCAL_DATE) - .appendLiteral(' ') - .append(DateTimeFormatter.ISO_LOCAL_TIME) - .toFormatter(); - - private static final TypeReference> TYPE_LIST_STRING = new TypeReference<>() { - }; - private static final TypeReference> TYPE_LIST_PROFILE = new TypeReference<>() { - }; - private static final TypeReference> TYPE_LIST_AGREEMENT = new TypeReference<>() { - }; - - protected final Logger logger = LoggerFactory.getLogger(EclipseService.class); - - private final EclipseTokenService tokens; - private final ExtensionService extensions; - private final EntityManager entityManager; - private final RestTemplate restTemplate; - private final JsonMapper jsonMapper; - - @Value("${ovsx.eclipse.base-url:}") - String eclipseApiUrl; - - @Value("${ovsx.eclipse.publisher-agreement.version:}") - String publisherAgreementVersion; - - @Value("${ovsx.eclipse.publisher-agreement.allowed-versions:}") - List publisherAgreementAllowedVersions; - - public EclipseService( - EclipseTokenService tokens, - ExtensionService extensions, - EntityManager entityManager, - RestTemplate restTemplate - ) { - this.tokens = tokens; - this.extensions = extensions; - this.entityManager = entityManager; - this.restTemplate = restTemplate; - this.jsonMapper = JsonMapper.builder().build(); - } - - public boolean isActive() { - return !StringUtils.isEmpty(publisherAgreementVersion) && !publisherAgreementAllowedVersions.isEmpty(); - } - - /** - * Check whether the given user has an active publisher agreement. - * @throws ErrorResultException if the user has no active agreement - */ - public void checkPublisherAgreement(UserData user) { - if (!isActive()) { - return; - } - // Users without authentication provider have been created directly in the DB, - // so we skip the agreement check in this case. - if (user.getProvider() == null) { - return; - } - var personId = user.getEclipsePersonId(); - 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 json = user.toUserJson(); - enrichUserJsonWithPublisherAgreement(json, user); - var publisherAgreement = json.getPublisherAgreement(); - - if (publisherAgreement == null || publisherAgreement.getStatus().equals("none")) { - throw new ErrorResultException( - "You must sign a Publisher Agreement with the Eclipse Foundation before publishing any extension."); - } - - if (!publisherAgreement.getStatus().equals("signed")) { - if (publisherAgreement.getVersion() != null) { - throw new ErrorResultException( - "Your Publisher Agreement with the Eclipse Foundation is outdated (version " - + publisherAgreement.getVersion() + "). The current version is " - + publisherAgreementVersion + "."); - } else { - throw new ErrorResultException("Your Publisher Agreement with the Eclipse Foundation is outdated."); - } - } - } - - /** - * Get the publicly available user profile. - */ - public EclipseProfile getPublicProfile(String personId) { - var urlTemplate = buildApiUrl("account/profile/{personId}"); - var uriVariables = Map.of(VAR_PERSON_ID, personId); - var request = new HttpEntity(HttpHeadersUtil.getAcceptJsonHeaders()); - - 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 + "': " + exc.getMessage()); - } - } - - 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. - */ - @Transactional - public void updateUserData(UserData user, EclipseProfile profile) { - user = entityManager.merge(user); - user.setEclipsePersonId(profile.getName()); - } - - public void enrichUserJsonWithPublisherAgreement(UserJson json, UserData user) { - var usableToken = true; - PublisherAgreement agreement = null; - try { - // Add information on the publisher agreement - agreement = getPublisherAgreement(user); - } catch (ErrorResultException e) { - if (e.getStatus() == HttpStatus.FORBIDDEN) { - usableToken = false; - } else { - logger.warn("Failed to retrieve publisher agreement", e); - } - } - - // If we do not have a valid access token, access the public profile to find a signed OpenVSX publisher agreement. - // Note: this service uses cached data so it might not reflect the actual situation. - if (!usableToken) { - var eclipsePersonId = user.getEclipsePersonId(); - if (eclipsePersonId != null) { - try { - var profile = getPublicProfile(user.getEclipsePersonId()); - var publisherAgreement = profile.getOpenVsxPublisherAgreement(); - if (publisherAgreement.isPresent()) { - agreement = new PublisherAgreement(true, null, publisherAgreement.get().getVersion(), null); - } - } catch (ErrorResultException e) { - // public profile could not be retrieved for the user, could be blocked. - logger.warn(e.getMessage()); - } - } - } - - enrichUserJson(json, user, agreement, usableToken); - } - - public void enrichUserJson(UserJson json, UserData user, PublisherAgreement agreement) { - enrichUserJson(json, user, agreement, true); - } - - /** - * Enrich the given JSON user data with Eclipse-specific information. - */ - private void enrichUserJson(UserJson json, UserData user, PublisherAgreement agreement, boolean usableToken) { - if (!isActive()) { - return; - } - - var publisherAgreement = new UserJson.PublisherAgreement(); - publisherAgreement.setStatus("none"); - json.setPublisherAgreement(publisherAgreement); - - var personId = user.getEclipsePersonId(); - if (personId == null) { - return; - } - - if (agreement != null && agreement.isActive() && agreement.version() != null) { - var status = publisherAgreementAllowedVersions.contains(agreement.version()) ? "signed" : "outdated"; - publisherAgreement.setStatus(status); - } - - if (agreement != null) { - publisherAgreement.setVersion(agreement.version()); - } - - if (agreement != null && agreement.timestamp() != null) { - publisherAgreement.setTimestamp(TimeUtil.toUTCString(agreement.timestamp())); - } - - // Report user as logged in only if there is a usable token: - // we need the token to access the Eclipse REST API - if (usableToken) { - var eclipseLogin = new UserJson(); - eclipseLogin.setProvider("eclipse"); - eclipseLogin.setLoginName(personId); - if (json.getAdditionalLogins() == null) { - json.setAdditionalLogins(new ArrayList<>(List.of(eclipseLogin))); - } else { - json.getAdditionalLogins().add(eclipseLogin); - } - } - } - - public void adminEnrichUserJson(UserJson json, UserData user) { - if (!isActive()) { - return; - } - - var publisherAgreement = new UserJson.PublisherAgreement(); - var personId = user.getEclipsePersonId(); - if (personId == null) { - publisherAgreement.setStatus("none"); - return; - } - - try { - var profile = getPublicProfile(personId); - var openVsxPublisherAgreement = profile.getOpenVsxPublisherAgreement(); - if (openVsxPublisherAgreement.isEmpty() - || StringUtils.isEmpty(openVsxPublisherAgreement.get().getVersion())) { - publisherAgreement.setStatus("none"); - } else if (publisherAgreementAllowedVersions.contains(openVsxPublisherAgreement.get().getVersion())) { - publisherAgreement.setStatus("signed"); - } else { - publisherAgreement.setStatus("outdated"); - } - - json.setPublisherAgreement(publisherAgreement); - } catch (ErrorResultException e) { - logger.error("Failed to get public profile", e); - } - } - - /** - * Get the user profile available through an access token. - */ - public EclipseProfile getUserProfile(String accessToken) { - var requestUrl = buildApiUrl("openvsx/profile"); - var headers = HttpHeadersUtil.getAcceptJsonHeaders(); - headers.setBearerAuth(accessToken); - var request = new RequestEntity<>(headers, HttpMethod.GET, URI.create(requestUrl)); - - try { - var response = restTemplate.exchange(request, String.class); - return parseEclipseProfile(response); - } catch (RestClientException exc) { - logger.error("Get request failed with URL: {}", requestUrl, exc); - throw new ErrorResultException( - "Request for retrieving user profile failed: " + exc.getMessage(), - HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - private EclipseProfile parseEclipseProfile(ResponseEntity response) { - var json = response.getBody(); - if (json == null) { - return new EclipseProfile(); - } - - try { - if (json.startsWith("[\"")) { - var error = jsonMapper.readValue(json, TYPE_LIST_STRING); - logger.error("Profile request failed:\n{}", json); - throw new ErrorResultException( - "Request to the Eclipse Foundation server failed: " + error, - HttpStatus.INTERNAL_SERVER_ERROR); - } else if (json.startsWith("[")) { - var profileList = jsonMapper.readValue(json, TYPE_LIST_PROFILE); - if (profileList.isEmpty()) { - throw new ErrorResultException( - "No Eclipse user profile available.", - HttpStatus.INTERNAL_SERVER_ERROR); - } - return profileList.getFirst(); - } else { - return jsonMapper.readValue(json, EclipseProfile.class); - } - } catch (JacksonException exc) { - logger.error("Failed to parse JSON response ({}):\n{}", response.getStatusCode(), json, exc); - throw new ErrorResultException( - "Parsing Eclipse user profile failed: " + exc.getMessage(), - HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - /** - * Get the publisher agreement of the given user with the user's current access token. - */ - public PublisherAgreement getPublisherAgreement(UserData user) { - var eclipseToken = checkEclipseToken(user); - var personId = user.getEclipsePersonId(); - if (StringUtils.isEmpty(personId)) { - return null; - } - var urlTemplate = buildApiUrl("openvsx/publisher_agreement/{personId}"); - var uriVariables = Map.of(VAR_PERSON_ID, personId); - var headers = HttpHeadersUtil.getAcceptJsonHeaders(); - headers.setBearerAuth(eclipseToken.accessToken()); - var request = new HttpEntity<>(headers); - - try { - var json = restTemplate.exchange(urlTemplate, HttpMethod.GET, request, String.class, uriVariables); - return parseAgreementResponse(json); - } catch (RestClientException exc) { - HttpStatusCode status = HttpStatus.INTERNAL_SERVER_ERROR; - if (exc instanceof HttpStatusCodeException) { - status = ((HttpStatusCodeException) exc).getStatusCode(); - // The endpoint yields 404 if the specified user has not signed a publisher agreement - if (status == HttpStatus.NOT_FOUND) { - return null; - } - } - - var url = UriComponentsBuilder.fromUriString(urlTemplate).build(uriVariables); - logger.error("Get request failed with URL: {}", url, exc); - throw new ErrorResultException( - "Request for retrieving publisher agreement failed: " + exc.getMessage(), - status); - } - } - - private static final Pattern STATUS_400_MESSAGE = Pattern - .compile("400 Bad Request: \\[\\[\"(?[^\"]+)\"]]"); - - /** - * Sign the publisher agreement on behalf of the given user. - */ - public PublisherAgreement signPublisherAgreement(UserData user) { - var requestUrl = buildApiUrl("openvsx/publisher_agreement"); - var eclipseToken = checkEclipseToken(user); - var headers = HttpHeadersUtil.getAcceptJsonHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setBearerAuth(eclipseToken.accessToken()); - var data = new SignAgreementParam(publisherAgreementVersion, user.getLoginName()); - var request = new HttpEntity<>(data, headers); - - try { - var json = restTemplate.postForEntity(requestUrl, request, String.class); - - // The request was successful: reactivate all previously published extensions - extensions.reactivateExtensions(user); - - // Parse the response and store the publisher agreement metadata - return parseAgreementResponse(json); - } catch (RestClientException exc) { - String message = exc.getMessage(); - var statusCode = HttpStatus.INTERNAL_SERVER_ERROR; - if (exc instanceof HttpStatusCodeException) { - var excStatus = ((HttpStatusCodeException) exc).getStatusCode(); - // The endpoint yields 409 if the specified user has already signed a publisher agreement - if (excStatus == HttpStatus.CONFLICT) { - message = "A publisher agreement is already present for user " + user.getLoginName() + "."; - statusCode = HttpStatus.BAD_REQUEST; - } else if (excStatus == HttpStatus.BAD_REQUEST) { - var matcher = STATUS_400_MESSAGE.matcher(exc.getMessage()); - if (matcher.matches()) { - message = matcher.group("message"); - } - } - } - if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR) { - message = "Request for signing publisher agreement failed: " + message; - } - - String payload; - try { - payload = jsonMapper.writeValueAsString(data); - } catch (JacksonException exc2) { - payload = "<" + exc2.getMessage() + ">"; - } - logger.error("Post request failed with URL: {} Payload: {}", requestUrl, payload, exc); - throw new ErrorResultException(message, statusCode); - } - } - - private PublisherAgreement parseAgreementResponse(ResponseEntity response) { - var json = response.getBody(); - if (json == null) { - return null; - } - - try { - PublisherAgreementResponse agreementResponse; - if (json.startsWith("[\"")) { - var error = jsonMapper.readValue(json, TYPE_LIST_STRING); - logger.error("Publisher agreement request failed:\n{}", json); - throw new ErrorResultException( - "Request to the Eclipse Foundation server failed: " + error, - HttpStatus.INTERNAL_SERVER_ERROR); - } else if (json.startsWith("[")) { - var profileList = jsonMapper.readValue(json, TYPE_LIST_AGREEMENT); - if (profileList.isEmpty()) { - throw new ErrorResultException( - "No publisher agreement available.", - HttpStatus.INTERNAL_SERVER_ERROR); - } - agreementResponse = profileList.getFirst(); - } else { - agreementResponse = jsonMapper.readValue(json, PublisherAgreementResponse.class); - } - - var timestamp = parseDate(agreementResponse.effectiveDate); - return new PublisherAgreement( - TimeUtil.getCurrentUTC().isAfter(timestamp), - agreementResponse.documentID, - agreementResponse.version, - timestamp); - } catch (JacksonException exc) { - logger.error("Failed to parse JSON response ({}):\n{}", response.getStatusCode(), json, exc); - throw new ErrorResultException( - "Parsing publisher agreement response failed: " + exc.getMessage(), - HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - private LocalDateTime parseDate(String dateString) { - try { - return LocalDateTime.parse(dateString, CUSTOM_DATE_TIME); - } catch (DateTimeParseException exc) { - logger.error("Failed to parse timestamp.", exc); - return null; - } - } - - /** - * Revoke the given user's publisher agreement. If an admin user is given, - * the admin's access token is used for the Eclipse API request, otherwise - * the access token of the target user is used. - */ - public void revokePublisherAgreement(UserData user, UserData admin) { - checkEclipseData(user); - - var eclipseToken = admin == null ? checkEclipseToken(user) : checkEclipseToken(admin); - var headers = new HttpHeaders(); - headers.setBearerAuth(eclipseToken.accessToken()); - var request = new HttpEntity<>(headers); - var urlTemplate = buildApiUrl("openvsx/publisher_agreement/{personId}"); - var uriVariables = Map.of(VAR_PERSON_ID, user.getEclipsePersonId()); - - try { - var requestCallback = restTemplate.httpEntityCallback(request); - restTemplate.execute(urlTemplate, HttpMethod.DELETE, requestCallback, null, uriVariables); - } catch (RestClientException exc) { - var url = UriComponentsBuilder.fromUriString(urlTemplate).build(uriVariables); - logger.error("Delete request failed with URL: {}", url, exc); - throw new ErrorResultException( - "Request for revoking publisher agreement failed: " + exc.getMessage(), - HttpStatus.INTERNAL_SERVER_ERROR); - } - } - - private void checkApiUrl() { - if (StringUtils.isEmpty(eclipseApiUrl)) { - throw new ErrorResultException("Missing URL for Eclipse API."); - } - } - - private String buildApiUrl(String path) { - checkApiUrl(); - - var baseUrl = eclipseApiUrl; - if (eclipseApiUrl.charAt(eclipseApiUrl.length() - 1) != '/') { - baseUrl += '/'; - } - - return baseUrl + path; - } - - private AuthToken checkEclipseToken(UserData user) { - var eclipseToken = tokens.getActiveEclipseToken(user); - if (eclipseToken == null || StringUtils.isEmpty(eclipseToken.accessToken())) { - throw new ErrorResultException("Authorization by Eclipse required.", HttpStatus.FORBIDDEN); - } - return eclipseToken; - } - - private void checkEclipseData(UserData user) { - if (StringUtils.isEmpty(user.getEclipsePersonId())) { - throw new ErrorResultException( - "Eclipse person ID is unavailable for user: " - + user.getProvider() + "/" + user.getLoginName()); - } - } -} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseTokenService.java b/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseTokenService.java deleted file mode 100644 index 0c6352c3d..000000000 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/EclipseTokenService.java +++ /dev/null @@ -1,160 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 TypeFox and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ -package org.eclipse.openvsx.eclipse; - -import java.time.Instant; -import java.util.List; -import java.util.Optional; - -import jakarta.persistence.EntityManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.util.Pair; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.security.oauth2.core.OAuth2AccessToken.TokenType; -import org.springframework.security.oauth2.core.OAuth2RefreshToken; -import org.springframework.stereotype.Service; -import org.springframework.transaction.support.TransactionTemplate; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; -import tools.jackson.core.JacksonException; -import tools.jackson.databind.json.JsonMapper; - -import org.eclipse.openvsx.entities.AuthToken; -import org.eclipse.openvsx.entities.UserData; - -@Service -public class EclipseTokenService { - - protected final Logger logger = LoggerFactory.getLogger(EclipseTokenService.class); - - private final TransactionTemplate transactions; - private final EntityManager entityManager; - private final ClientRegistrationRepository clientRegistrationRepository; - private final JsonMapper jsonMapper; - - public EclipseTokenService( - TransactionTemplate transactions, - EntityManager entityManager, - @Autowired(required = false) ClientRegistrationRepository clientRegistrationRepository - ) { - this.transactions = transactions; - this.entityManager = entityManager; - this.clientRegistrationRepository = clientRegistrationRepository; - this.jsonMapper = JsonMapper.builder().build(); - } - - public AuthToken updateEclipseToken(long userId, OAuth2AccessToken accessToken, OAuth2RefreshToken refreshToken) { - var token = toAuthToken(accessToken, refreshToken); - return transactions.execute(status -> { - var userData = entityManager.find(UserData.class, userId); - userData.setEclipseToken(token); - return token; - }); - } - - private AuthToken toAuthToken(OAuth2AccessToken accessToken, OAuth2RefreshToken refreshToken) { - if (accessToken == null) { - return null; - } - - String refresh = null; - Instant refreshExpiresAt = null; - if (refreshToken != null) { - refresh = refreshToken.getTokenValue(); - refreshExpiresAt = refreshToken.getExpiresAt(); - } - - return new AuthToken( - accessToken.getTokenValue(), - accessToken.getIssuedAt(), - accessToken.getExpiresAt(), - accessToken.getScopes(), - refresh, - refreshExpiresAt); - } - - public AuthToken getActiveEclipseToken(UserData userData) { - var token = userData.getEclipseToken(); - if (token != null && isExpired(token.expiresAt())) { - OAuth2AccessToken newAccessToken = null; - OAuth2RefreshToken newRefreshToken = null; - var newTokens = refreshEclipseToken(token); - if (newTokens != null) { - newAccessToken = newTokens.getFirst(); - newRefreshToken = newTokens.getSecond(); - } - - return updateEclipseToken(userData.getId(), newAccessToken, newRefreshToken); - } - return token; - } - - private boolean isExpired(Instant instant) { - return instant != null && Instant.now().isAfter(instant); - } - - private Pair refreshEclipseToken(AuthToken token) { - if (token.refreshToken() == null || isExpired(token.refreshExpiresAt())) { - return null; - } - - var reg = Optional.ofNullable(clientRegistrationRepository).map(repo -> repo.findByRegistrationId("eclipse")) - .orElse(null); - if (reg == null) { - logger.error("Eclipse client not registered"); - return null; - } - - var tokenUri = reg.getProviderDetails().getTokenUri(); - - var headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); - headers.setAccept(List.of(MediaType.APPLICATION_JSON)); - - var data = new LinkedMultiValueMap<>(); - data.add("grant_type", "refresh_token"); - data.add("client_id", reg.getClientId()); - data.add("client_secret", reg.getClientSecret()); - data.add("refresh_token", token.refreshToken()); - - try { - var request = new HttpEntity<>(data, headers); - var restTemplate = new RestTemplate(); - var response = restTemplate.postForObject(tokenUri, request, String.class); - var root = jsonMapper.readTree(response); - var newTokenValue = root.get("access_token").asString(); - var newRefreshTokenValue = root.get("refresh_token").asString(); - var expires_in = root.get("expires_in").asLong(); - - var issuedAt = Instant.now(); - var expiresAt = issuedAt.plusSeconds(expires_in); - - var newToken = new OAuth2AccessToken(TokenType.BEARER, newTokenValue, issuedAt, expiresAt); - var newRefreshToken = new OAuth2RefreshToken(newRefreshTokenValue, issuedAt); - return Pair.of(newToken, newRefreshToken); - } catch (HttpClientErrorException.BadRequest exc) { - // keycloak sends a 400 status response if the refresh call failed - logger.warn("Eclipse token could not be refreshed: {}", exc.getMessage()); - } catch (RestClientException exc) { - logger.error("Post request failed with URL: {}", tokenUri, exc); - } catch (JacksonException exc) { - logger.error("Invalid JSON data received from URL: {}", tokenUri, exc); - } - return null; - } -} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreement.java b/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreement.java deleted file mode 100644 index f03671852..000000000 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreement.java +++ /dev/null @@ -1,21 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 TypeFox and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ -package org.eclipse.openvsx.eclipse; - -import java.time.LocalDateTime; - -/** - * - * @param isActive - * @param documentId - * @param version Version of the last signed publisher agreement. - * @param timestamp Timestamp of the last signed publisher agreement. - */ -public record PublisherAgreement(boolean isActive, String documentId, String version, LocalDateTime timestamp) {} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreementResponse.java b/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreementResponse.java deleted file mode 100644 index 5b6d4e46e..000000000 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherAgreementResponse.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 TypeFox and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ -package org.eclipse.openvsx.eclipse; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * https://eclipsefdn.github.io/openvsx-publisher-agreement-specs/#/paths/~1publisher_agreement/post - */ -class PublisherAgreementResponse { - - /** Unique identifier for an addressable object in the API. */ - @JsonProperty("PersonID") - String personID; - - /** Unique identifier for an addressable object in the API. */ - @JsonProperty("DocumentID") - String documentID; - - /** The version number for the current document. */ - @JsonProperty("Version") - String version; - - /** Date string in the RFC 3339 format. */ - @JsonProperty("EffectiveDate") - String effectiveDate; - - /** Date string in the RFC 3339 format. */ - @JsonProperty("ReceivedDate") - String receivedDate; - - /** The signed document as a blob entity. */ - @JsonProperty("ScannedDocumentBLOB") - String scannedDocumentBLOB; - - /** The MIME type for the posted document blob. */ - @JsonProperty("ScannedDocumentMime") - String scannedDocumentMime; - - /** The name of the document being posted. */ - @JsonProperty("ScannedDocumentFileName") - String scannedDocumentFileName; - - /** Comment about the document being posted. */ - @JsonProperty("Comments") - String comments; -} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java b/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java deleted file mode 100644 index 1bd842b99..000000000 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/PublisherComplianceChecker.java +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 TypeFox and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ -package org.eclipse.openvsx.eclipse; - -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -import jakarta.persistence.EntityManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.event.ApplicationStartedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; -import org.springframework.transaction.support.TransactionTemplate; - -import org.eclipse.openvsx.ExtensionService; -import org.eclipse.openvsx.entities.Extension; -import org.eclipse.openvsx.entities.PersonalAccessToken; -import org.eclipse.openvsx.entities.UserData; -import org.eclipse.openvsx.repositories.RepositoryService; -import org.eclipse.openvsx.util.NamingUtil; - -@Component -public class PublisherComplianceChecker { - - protected final Logger logger = LoggerFactory.getLogger(PublisherComplianceChecker.class); - - private final TransactionTemplate transactions; - private final EntityManager entityManager; - private final RepositoryService repositories; - private final ExtensionService extensions; - private final EclipseService eclipseService; - - @Value("${ovsx.eclipse.check-compliance-on-start:false}") - boolean checkCompliance; - - public PublisherComplianceChecker( - TransactionTemplate transactions, - EntityManager entityManager, - RepositoryService repositories, - ExtensionService extensions, - EclipseService eclipseService - ) { - this.transactions = transactions; - this.entityManager = entityManager; - this.repositories = repositories; - this.extensions = extensions; - this.eclipseService = eclipseService; - } - - @EventListener - public void checkPublishers(ApplicationStartedEvent event) { - if (!checkCompliance || !eclipseService.isActive()) { - return; - } - - var publisherTokens = repositories.findAllAccessTokens().stream() - .collect(Collectors.groupingBy(PersonalAccessToken::getUser)); - publisherTokens.keySet().forEach(user -> { - var accessTokens = publisherTokens.get(user); - if (!accessTokens.isEmpty() && !isCompliant(user)) { - // Found a non-compliant publisher: deactivate all extension versions - transactions.execute(status -> { - deactivateExtensions(accessTokens); - return null; - }); - } - }); - } - - private boolean isCompliant(UserData user) { - // Users without authentication provider have been created directly in the DB, - // so we skip the agreement check in this case. - if (user.getProvider() == null) { - return true; - } - if (user.getEclipsePersonId() == null) { - // The user has never logged in with Eclipse - return false; - } - - var profile = eclipseService.getPublicProfile(user.getEclipsePersonId()); - return Optional.of(profile) - .map(EclipseProfile::getPublisherAgreements) - .map(EclipseProfile.PublisherAgreements::getOpenVsx) - .map(EclipseProfile.PublisherAgreement::getVersion) - .isPresent(); - } - - private void deactivateExtensions(List accessTokens) { - var affectedExtensions = new LinkedHashSet(); - for (var accessToken : accessTokens) { - var versions = repositories.findVersionsByAccessToken(accessToken, true); - for (var version : versions) { - version.setActive(false); - entityManager.merge(version); - var extension = version.getExtension(); - affectedExtensions.add(extension); - logger.atInfo() - .setMessage("Deactivated: {} - {}") - .addArgument(() -> accessToken.getUser().getLoginName()) - .addArgument(() -> NamingUtil.toLogFormat(version)) - .log(); - } - } - - // Update affected extensions - for (var extension : affectedExtensions) { - extensions.updateExtension(extension); - entityManager.merge(extension); - } - } -} diff --git a/server/src/main/java/org/eclipse/openvsx/eclipse/SignAgreementParam.java b/server/src/main/java/org/eclipse/openvsx/eclipse/SignAgreementParam.java deleted file mode 100644 index 6e03e658e..000000000 --- a/server/src/main/java/org/eclipse/openvsx/eclipse/SignAgreementParam.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 TypeFox and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ -package org.eclipse.openvsx.eclipse; - -import com.fasterxml.jackson.annotation.JsonProperty; - -/** - * https://eclipsefdn.github.io/openvsx-publisher-agreement-specs/#/paths/~1publisher_agreement/post - */ -public class SignAgreementParam { - - /** - * The version number of the document/agreement. - */ - private String version; - - /** - * The GitHub username of the user. This must match what the Eclipse Foundation has on file - * for the user to successfully sign the publisher agreement. - */ - @JsonProperty("github_handle") - private String githubHandle; - - public SignAgreementParam() { - } - - public SignAgreementParam(String version, String githubHandle) { - this.version = version; - this.githubHandle = githubHandle; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getGithubHandle() { - return githubHandle; - } - - public void setGithubHandle(String githubHandle) { - this.githubHandle = githubHandle; - } -} diff --git a/server/src/main/java/org/eclipse/openvsx/publish/PublisherAgreementService.java b/server/src/main/java/org/eclipse/openvsx/publish/PublisherAgreementService.java new file mode 100644 index 000000000..f05c6247b --- /dev/null +++ b/server/src/main/java/org/eclipse/openvsx/publish/PublisherAgreementService.java @@ -0,0 +1,48 @@ +/******************************************************************************** + * Copyright (c) 2026 Eclipse Foundation and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +package org.eclipse.openvsx.publish; + +import org.eclipse.openvsx.entities.UserData; +import org.eclipse.openvsx.json.UserJson; + +/** + * Deployment-specific publisher agreement rules. All methods default to no-ops; + * registries that require publishers to sign an agreement contribute an + * implementation, typically through Spring Boot auto-configuration. + */ +public interface PublisherAgreementService { + + /** + * Check whether the given user is allowed to publish extensions. + * @throws org.eclipse.openvsx.util.ErrorResultException if publishing is not allowed + */ + default void checkPublisherAgreement(UserData user) { + } + + /** + * Add agreement status to the user's own profile data. + */ + default void enrichUserJsonWithPublisherAgreement(UserJson json, UserData user) { + } + + /** + * Add agreement status to the admin view of a user. + */ + default void adminEnrichUserJson(UserJson json, UserData user) { + } + + /** + * Revoke the user's agreement; called when an admin revokes a publisher's + * contributions. Implementations decide themselves whether there is anything + * to revoke for the given user. + */ + default void revokePublisherAgreement(UserData user, UserData admin) { + } +} diff --git a/server/src/main/java/org/eclipse/openvsx/security/CodedAuthException.java b/server/src/main/java/org/eclipse/openvsx/security/CodedAuthException.java index 80465b762..80147dad2 100644 --- a/server/src/main/java/org/eclipse/openvsx/security/CodedAuthException.java +++ b/server/src/main/java/org/eclipse/openvsx/security/CodedAuthException.java @@ -23,8 +23,6 @@ public class CodedAuthException extends AuthenticationException { public static final String INVALID_GITHUB_USER = "invalid-github-user"; public static final String INVALID_USER = "invalid-user"; public static final String NEED_MAIN_LOGIN = "need-main-login"; - public static final String ECLIPSE_MISSING_GITHUB_ID = "eclipse-missing-github-id"; - public static final String ECLIPSE_MISMATCH_GITHUB_ID = "eclipse-mismatch-github-id"; @Serial private static final long serialVersionUID = 1L; diff --git a/server/src/main/java/org/eclipse/openvsx/security/CustomAuthenticationSuccessHandler.java b/server/src/main/java/org/eclipse/openvsx/security/CustomAuthenticationSuccessHandler.java index e4c860c8a..891186cd0 100644 --- a/server/src/main/java/org/eclipse/openvsx/security/CustomAuthenticationSuccessHandler.java +++ b/server/src/main/java/org/eclipse/openvsx/security/CustomAuthenticationSuccessHandler.java @@ -9,18 +9,21 @@ ********************************************************************************/ package org.eclipse.openvsx.security; +import java.util.List; + import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; -import org.eclipse.openvsx.util.UrlUtil; - public class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { - public CustomAuthenticationSuccessHandler(String defaultTargetUrl) { + private final List loginHandlers; + + public CustomAuthenticationSuccessHandler(String defaultTargetUrl, List loginHandlers) { setDefaultTargetUrl(defaultTargetUrl); + this.loginHandlers = loginHandlers; } @Override @@ -31,9 +34,14 @@ protected String determineTargetUrl( ) { if (authentication instanceof OAuth2AuthenticationToken) { var token = (OAuth2AuthenticationToken) authentication; - // Redirect to user profile page after login to Eclipse - if ("eclipse".equals(token.getAuthorizedClientRegistrationId())) { - return UrlUtil.createApiUrl(getDefaultTargetUrl(), "user-settings", "profile"); + var registrationId = token.getAuthorizedClientRegistrationId(); + var targetUrl = loginHandlers.stream() + .filter(handler -> handler.getRegistrationId().equals(registrationId)) + .map(handler -> handler.getSuccessRedirectUrl(getDefaultTargetUrl())) + .filter(url -> url != null) + .findFirst(); + if (targetUrl.isPresent()) { + return targetUrl.get(); } } return determineTargetUrl(request, response); diff --git a/server/src/main/java/org/eclipse/openvsx/security/OAuth2LoginHandler.java b/server/src/main/java/org/eclipse/openvsx/security/OAuth2LoginHandler.java new file mode 100644 index 000000000..0cf4c4f0d --- /dev/null +++ b/server/src/main/java/org/eclipse/openvsx/security/OAuth2LoginHandler.java @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 Eclipse Foundation and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +package org.eclipse.openvsx.security; + +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.security.oauth2.core.OAuth2RefreshToken; + +/** + * Deployment-specific handling of logins for a single OAuth2 client registration, + * e.g. a provider that links a secondary account to the logged-in user instead of + * creating one. Registrations without a handler go through the generic + * attribute-mapping flow. + */ +public interface OAuth2LoginHandler { + + /** + * The client registration id this handler applies to. + */ + String getRegistrationId(); + + /** + * Load the principal for a login with this provider. + */ + IdPrincipal loadUser(OAuth2UserRequest userRequest); + + /** + * Called after a successful login with this provider. + */ + default void authenticationSucceeded( + IdPrincipal principal, + OAuth2AccessToken accessToken, + OAuth2RefreshToken refreshToken + ) { + } + + /** + * Target URL to redirect to after a successful login, or {@code null} for the default. + */ + default String getSuccessRedirectUrl(String defaultTargetUrl) { + return null; + } +} diff --git a/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java b/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java index 4a7b7eb53..57284cdce 100644 --- a/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java +++ b/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java @@ -10,14 +10,15 @@ package org.eclipse.openvsx.security; import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; -import jakarta.persistence.EntityManager; import org.apache.commons.lang3.StringUtils; import org.springframework.context.event.EventListener; -import org.springframework.security.authentication.AuthenticationServiceException; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; @@ -29,10 +30,7 @@ import org.springframework.stereotype.Service; import org.eclipse.openvsx.UserService; -import org.eclipse.openvsx.eclipse.EclipseService; -import org.eclipse.openvsx.eclipse.EclipseTokenService; import org.eclipse.openvsx.entities.UserData; -import org.eclipse.openvsx.util.ErrorResultException; import static java.util.Collections.emptyList; import static org.eclipse.openvsx.security.CodedAuthException.*; @@ -42,25 +40,20 @@ public class OAuth2UserServices { private final UserService users; - private final EclipseTokenService tokens; - private final EntityManager entityManager; - private final EclipseService eclipse; private final OAuth2AttributesConfig attributesConfig; + private final Map loginHandlers; private final DefaultOAuth2UserService springOAuth2UserService; private final OidcUserService springOidcUserService; public OAuth2UserServices( UserService users, - EclipseTokenService tokens, - EntityManager entityManager, - EclipseService eclipse, - OAuth2AttributesConfig attributesConfig + OAuth2AttributesConfig attributesConfig, + List loginHandlers ) { this.users = users; - this.tokens = tokens; - this.entityManager = entityManager; - this.eclipse = eclipse; this.attributesConfig = attributesConfig; + this.loginHandlers = loginHandlers.stream() + .collect(Collectors.toMap(OAuth2LoginHandler::getRegistrationId, Function.identity())); springOAuth2UserService = new DefaultOAuth2UserService(); springOidcUserService = new OidcUserService(); } @@ -78,19 +71,17 @@ public void authenticationSucceeded(AuthenticationSuccessEvent event) { // `ExtendedOAuth2UserServices.loadUser` was processed. if (event.getSource() instanceof OAuth2LoginAuthenticationToken) { var auth = (OAuth2LoginAuthenticationToken) event.getSource(); - var registrationId = auth.getClientRegistration().getRegistrationId(); - if (registrationId.equals("eclipse")) { + var handler = loginHandlers.get(auth.getClientRegistration().getRegistrationId()); + if (handler != null) { var idPrincipal = (IdPrincipal) auth.getPrincipal(); - tokens.updateEclipseToken(idPrincipal.getId(), auth.getAccessToken(), auth.getRefreshToken()); + handler.authenticationSucceeded(idPrincipal, auth.getAccessToken(), auth.getRefreshToken()); } } } public IdPrincipal loadUser(OAuth2UserRequest userRequest) { - return switch (userRequest.getClientRegistration().getRegistrationId()) { - case "eclipse" -> loadEclipseUser(userRequest); - default -> loadGenericUser(userRequest); - }; + var handler = loginHandlers.get(userRequest.getClientRegistration().getRegistrationId()); + return handler != null ? handler.loadUser(userRequest) : loadGenericUser(userRequest); } public boolean canLogin() { @@ -117,45 +108,6 @@ private IdPrincipal loadGenericUser(OAuth2UserRequest userRequest) { return new IdPrincipal(userData.getId(), userData.getAuthId(), getAuthorities(userData)); } - private IdPrincipal loadEclipseUser(OAuth2UserRequest userRequest) { - var authentication = SecurityContextHolder.getContext().getAuthentication(); - if (authentication == null) { - throw new CodedAuthException( - "Please log in with GitHub before connecting your Eclipse account.", - NEED_MAIN_LOGIN); - } - if (!(authentication.getPrincipal() instanceof IdPrincipal)) { - throw new CodedAuthException("The current authentication is invalid.", NEED_MAIN_LOGIN); - } - var principal = (IdPrincipal) authentication.getPrincipal(); - var userData = entityManager.find(UserData.class, principal.getId()); - if (userData == null) { - throw new CodedAuthException("The current authentication has no backing data.", NEED_MAIN_LOGIN); - } - try { - var accessToken = userRequest.getAccessToken().getTokenValue(); - var profile = eclipse.getUserProfile(accessToken); - if (StringUtils.isEmpty(profile.getGithubHandle())) { - throw new CodedAuthException( - "Your Eclipse profile is missing a GitHub username.", - ECLIPSE_MISSING_GITHUB_ID); - } - if (!profile.getGithubHandle().equalsIgnoreCase(userData.getLoginName())) { - throw new CodedAuthException( - "The GitHub username setting in your Eclipse profile (" - + profile.getGithubHandle() - + ") does not match your GitHub authentication (" - + userData.getLoginName() + ").", - ECLIPSE_MISMATCH_GITHUB_ID); - } - - eclipse.updateUserData(userData, profile); - return principal; - } catch (ErrorResultException exc) { - throw new AuthenticationServiceException(exc.getMessage(), exc); - } - } - private Collection getAuthorities(UserData userData) { var role = userData.getRole(); if (role == null) { diff --git a/server/src/main/java/org/eclipse/openvsx/security/SecurityConfig.java b/server/src/main/java/org/eclipse/openvsx/security/SecurityConfig.java index c911a7f2e..c8c250d72 100644 --- a/server/src/main/java/org/eclipse/openvsx/security/SecurityConfig.java +++ b/server/src/main/java/org/eclipse/openvsx/security/SecurityConfig.java @@ -9,6 +9,8 @@ ********************************************************************************/ package org.eclipse.openvsx.security; +import java.util.List; + import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -34,7 +36,11 @@ public class SecurityConfig { String[] additionalRoutes; @Bean - public SecurityFilterChain filterChain(HttpSecurity http, OAuth2UserServices userServices) throws Exception { + public SecurityFilterChain filterChain( + HttpSecurity http, + OAuth2UserServices userServices, + List loginHandlers + ) throws Exception { var filterChain = http.authorizeHttpRequests( registry -> registry .requestMatchers( @@ -93,7 +99,7 @@ public SecurityFilterChain filterChain(HttpSecurity http, OAuth2UserServices use var redirectUrl = StringUtils.isEmpty(webuiUrl) ? "/" : webuiUrl; filterChain.oauth2Login(configurer -> { configurer.defaultSuccessUrl(redirectUrl); - configurer.successHandler(new CustomAuthenticationSuccessHandler(redirectUrl)); + configurer.successHandler(new CustomAuthenticationSuccessHandler(redirectUrl, loginHandlers)); configurer.failureUrl(redirectUrl + "?auth-error"); configurer.userInfoEndpoint( customizer -> customizer.oidcUserService(userServices.getOidc()) diff --git a/server/src/test/java/org/eclipse/openvsx/LocalRegistryServiceTest.java b/server/src/test/java/org/eclipse/openvsx/LocalRegistryServiceTest.java index 7ba73e88d..9e5092641 100644 --- a/server/src/test/java/org/eclipse/openvsx/LocalRegistryServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/LocalRegistryServiceTest.java @@ -25,12 +25,12 @@ import org.eclipse.openvsx.accesstoken.AccessTokenService; import org.eclipse.openvsx.cache.CacheService; -import org.eclipse.openvsx.eclipse.EclipseService; import org.eclipse.openvsx.entities.Namespace; import org.eclipse.openvsx.entities.NamespaceMembership; import org.eclipse.openvsx.entities.UserData; import org.eclipse.openvsx.json.NamespaceJson; import org.eclipse.openvsx.publish.ExtensionVersionIntegrityService; +import org.eclipse.openvsx.publish.PublisherAgreementService; import org.eclipse.openvsx.publish.PublishingConfig; import org.eclipse.openvsx.repositories.RepositoryService; import org.eclipse.openvsx.search.SearchUtilService; @@ -78,7 +78,7 @@ class LocalRegistryServiceTest { StorageUtilService storageUtilService; @Mock - EclipseService eclipse; + PublisherAgreementService publisherAgreement; @Mock CacheService cacheService; @@ -103,13 +103,13 @@ void setUp() { searchUtilService, validator, storageUtilService, - eclipse, + publisherAgreement, cacheService, integrityService, similarityCheckService, new PublishingConfig()); - doNothing().when(eclipse).checkPublisherAgreement(any()); + doNothing().when(publisherAgreement).checkPublisherAgreement(any()); } @Test diff --git a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java index 6ae23557a..7c67611d8 100644 --- a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java @@ -55,8 +55,6 @@ import org.eclipse.openvsx.cache.CacheService; import org.eclipse.openvsx.cache.ExtensionJsonCacheKeyGenerator; import org.eclipse.openvsx.cache.LatestExtensionVersionCacheKeyGenerator; -import org.eclipse.openvsx.eclipse.EclipseService; -import org.eclipse.openvsx.eclipse.EclipseTokenService; import org.eclipse.openvsx.entities.*; import org.eclipse.openvsx.extension_control.ExtensionControlService; import org.eclipse.openvsx.json.*; @@ -102,7 +100,6 @@ DownloadCountService.class, ExtensionDownloadMetrics.class, CacheService.class, - EclipseService.class, PublishExtensionVersionService.class, SimpleMeterRegistry.class, JobRequestScheduler.class, @@ -2944,21 +2941,9 @@ AccessTokenService tokenService( @Bean OAuth2UserServices oauth2UserServices( UserService users, - EclipseTokenService eclipseTokenService, - EntityManager entityManager, - EclipseService eclipse, OAuth2AttributesConfig attributesConfig ) { - return new OAuth2UserServices(users, eclipseTokenService, entityManager, eclipse, attributesConfig); - } - - @Bean - EclipseTokenService eclipseTokenService( - TransactionTemplate transactions, - EntityManager entityManager, - ClientRegistrationRepository clientRegistrationRepository - ) { - return new EclipseTokenService(transactions, entityManager, clientRegistrationRepository); + return new OAuth2UserServices(users, attributesConfig, List.of()); } @Bean @@ -2972,7 +2957,6 @@ LocalRegistryService localRegistryService( SearchUtilService search, ExtensionValidator validator, StorageUtilService storageUtil, - EclipseService eclipse, CacheService cache, ExtensionVersionIntegrityService integrityService, SimilarityCheckService similarityCheckService, @@ -2988,7 +2972,7 @@ LocalRegistryService localRegistryService( search, validator, storageUtil, - eclipse, + null, cache, integrityService, similarityCheckService, diff --git a/server/src/test/java/org/eclipse/openvsx/UserAPITest.java b/server/src/test/java/org/eclipse/openvsx/UserAPITest.java index 923cf870d..0f0527b1c 100644 --- a/server/src/test/java/org/eclipse/openvsx/UserAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/UserAPITest.java @@ -41,8 +41,6 @@ import org.eclipse.openvsx.accesstoken.AccessTokenService; import org.eclipse.openvsx.cache.CacheService; import org.eclipse.openvsx.cache.LatestExtensionVersionCacheKeyGenerator; -import org.eclipse.openvsx.eclipse.EclipseService; -import org.eclipse.openvsx.eclipse.EclipseTokenService; import org.eclipse.openvsx.entities.*; import org.eclipse.openvsx.json.*; import org.eclipse.openvsx.mail.MailService; @@ -78,7 +76,6 @@ @WebMvcTest(UserAPI.class) @MockitoBean( types = { - EclipseService.class, ClientRegistrationRepository.class, StorageUtilService.class, CacheService.class, @@ -1081,21 +1078,9 @@ AccessTokenService accessTokenService( @Bean OAuth2UserServices oauth2UserServices( UserService users, - EclipseTokenService eclipseTokenService, - EntityManager entityManager, - EclipseService eclipse, OAuth2AttributesConfig attributesConfig ) { - return new OAuth2UserServices(users, eclipseTokenService, entityManager, eclipse, attributesConfig); - } - - @Bean - EclipseTokenService eclipseTokenService( - TransactionTemplate transactions, - EntityManager entityManager, - ClientRegistrationRepository clientRegistrationRepository - ) { - return new EclipseTokenService(transactions, entityManager, clientRegistrationRepository); + return new OAuth2UserServices(users, attributesConfig, List.of()); } @Bean @@ -1114,7 +1099,6 @@ LocalRegistryService localRegistryService( SearchUtilService search, ExtensionValidator validator, StorageUtilService storageUtil, - EclipseService eclipse, CacheService cache, ExtensionVersionIntegrityService integrityService, SimilarityCheckService similarityCheckService @@ -1129,7 +1113,7 @@ LocalRegistryService localRegistryService( search, validator, storageUtil, - eclipse, + null, cache, integrityService, similarityCheckService, diff --git a/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java b/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java index f5e03203f..f826ad8bc 100644 --- a/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java @@ -44,8 +44,6 @@ import org.eclipse.openvsx.cache.CacheService; import org.eclipse.openvsx.cache.FilesCacheKeyGenerator; import org.eclipse.openvsx.cache.LatestExtensionVersionCacheKeyGenerator; -import org.eclipse.openvsx.eclipse.EclipseService; -import org.eclipse.openvsx.eclipse.EclipseTokenService; import org.eclipse.openvsx.entities.*; import org.eclipse.openvsx.metrics.ExtensionDownloadMetrics; import org.eclipse.openvsx.publish.ExtensionVersionIntegrityService; @@ -78,7 +76,6 @@ CacheService.class, UpstreamVSCodeService.class, VSCodeIdService.class, - EclipseService.class, ExtensionValidator.class, SimpleMeterRegistry.class, FileCacheDurationConfig.class, @@ -1351,21 +1348,9 @@ TransactionTemplate transactionTemplate() { @Bean OAuth2UserServices oauth2UserServices( UserService users, - EclipseTokenService eclipseTokenService, - EntityManager entityManager, - EclipseService eclipse, OAuth2AttributesConfig attributesConfig ) { - return new OAuth2UserServices(users, eclipseTokenService, entityManager, eclipse, attributesConfig); - } - - @Bean - EclipseTokenService eclipseTokenService( - TransactionTemplate transactions, - EntityManager entityManager, - ClientRegistrationRepository clientRegistrationRepository - ) { - return new EclipseTokenService(transactions, entityManager, clientRegistrationRepository); + return new OAuth2UserServices(users, attributesConfig, List.of()); } @Bean diff --git a/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java b/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java index 2c08efb1f..5d76226a4 100644 --- a/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java @@ -53,8 +53,6 @@ import org.eclipse.openvsx.adapter.VSCodeIdService; import org.eclipse.openvsx.cache.CacheService; import org.eclipse.openvsx.cache.LatestExtensionVersionCacheKeyGenerator; -import org.eclipse.openvsx.eclipse.EclipseService; -import org.eclipse.openvsx.eclipse.EclipseTokenService; import org.eclipse.openvsx.entities.AdminStatistics; import org.eclipse.openvsx.entities.Extension; import org.eclipse.openvsx.entities.ExtensionReview; @@ -130,7 +128,6 @@ CacheService.class, PublishExtensionVersionHandler.class, SearchUtilService.class, - EclipseService.class, SimpleMeterRegistry.class, FileCacheDurationConfig.class, MailService.class, @@ -2216,21 +2213,9 @@ AccessTokenService tokenService( @Bean OAuth2UserServices oauth2UserServices( UserService users, - EclipseTokenService eclipseTokenService, - EntityManager entityManager, - EclipseService eclipse, OAuth2AttributesConfig attributesConfig ) { - return new OAuth2UserServices(users, eclipseTokenService, entityManager, eclipse, attributesConfig); - } - - @Bean - EclipseTokenService eclipseTokenService( - TransactionTemplate transactions, - EntityManager entityManager, - ClientRegistrationRepository clientRegistrationRepository - ) { - return new EclipseTokenService(transactions, entityManager, clientRegistrationRepository); + return new OAuth2UserServices(users, attributesConfig, List.of()); } @Bean @@ -2242,7 +2227,6 @@ AdminService adminService( AccessTokenService tokenService, ExtensionValidator validator, SearchUtilService search, - EclipseService eclipse, StorageUtilService storageUtil, CacheService cache, JobRequestScheduler scheduler, @@ -2257,7 +2241,7 @@ AdminService adminService( tokenService, validator, search, - eclipse, + null, storageUtil, cache, scheduler, @@ -2276,7 +2260,6 @@ LocalRegistryService localRegistryService( SearchUtilService search, ExtensionValidator validator, StorageUtilService storageUtil, - EclipseService eclipse, CacheService cache, ExtensionVersionIntegrityService integrityService, SimilarityCheckService similarityCheckService @@ -2291,7 +2274,7 @@ LocalRegistryService localRegistryService( search, validator, storageUtil, - eclipse, + null, cache, integrityService, similarityCheckService, diff --git a/server/src/test/java/org/eclipse/openvsx/eclipse/EclipseServiceTest.java b/server/src/test/java/org/eclipse/openvsx/eclipse/EclipseServiceTest.java deleted file mode 100644 index 83f867579..000000000 --- a/server/src/test/java/org/eclipse/openvsx/eclipse/EclipseServiceTest.java +++ /dev/null @@ -1,503 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2020 TypeFox and others - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ -package org.eclipse.openvsx.eclipse; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -import io.micrometer.core.instrument.simple.SimpleMeterRegistry; -import jakarta.persistence.EntityManager; -import org.jobrunr.scheduling.JobRequestScheduler; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.data.util.Streamable; -import org.springframework.http.*; -import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.transaction.support.TransactionTemplate; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestTemplate; - -import org.eclipse.openvsx.ExtensionService; -import org.eclipse.openvsx.ExtensionValidator; -import org.eclipse.openvsx.MockTransactionTemplate; -import org.eclipse.openvsx.UserService; -import org.eclipse.openvsx.adapter.VSCodeIdService; -import org.eclipse.openvsx.cache.CacheService; -import org.eclipse.openvsx.cache.LatestExtensionVersionCacheKeyGenerator; -import org.eclipse.openvsx.entities.*; -import org.eclipse.openvsx.metrics.ExtensionDownloadMetrics; -import org.eclipse.openvsx.publish.PublishExtensionVersionHandler; -import org.eclipse.openvsx.publish.PublishingConfig; -import org.eclipse.openvsx.repositories.RepositoryService; -import org.eclipse.openvsx.scanning.ExtensionScanPersistenceService; -import org.eclipse.openvsx.scanning.ExtensionScanService; -import org.eclipse.openvsx.search.SearchUtilService; -import org.eclipse.openvsx.storage.*; -import org.eclipse.openvsx.storage.log.DownloadCountService; -import org.eclipse.openvsx.util.ErrorResultException; -import org.eclipse.openvsx.util.LogService; -import org.eclipse.openvsx.util.TargetPlatform; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; - -@ExtendWith(SpringExtension.class) -@MockitoBean( - types = { - EntityManager.class, - SearchUtilService.class, - GoogleCloudStorageService.class, - AzureBlobStorageService.class, - AwsStorageService.class, - VSCodeIdService.class, - DownloadCountService.class, - ExtensionDownloadMetrics.class, - CacheService.class, - UserService.class, - PublishExtensionVersionHandler.class, - SimpleMeterRegistry.class, - FileCacheDurationConfig.class, - JobRequestScheduler.class, - CdnServiceConfig.class, - ExtensionScanService.class, - ExtensionScanPersistenceService.class, - LogService.class - } -) -class EclipseServiceTest { - - private static final String PUBLIC_PROFILE_URL = "https://test.openvsx.eclipse.org/account/profile/{personId}"; - private static final String PUBLISHER_AGREEMENT_URL = "https://test.openvsx.eclipse.org/openvsx/publisher_agreement/{personId}"; - - @MockitoBean - RepositoryService repositories; - - @MockitoBean - EclipseTokenService tokens; - - @MockitoBean - RestTemplate restTemplate; - - @Autowired - EclipseService eclipse; - - @BeforeEach - void setup() { - eclipse.publisherAgreementAllowedVersions = List.of("1", "1.0", "1.1"); - eclipse.publisherAgreementVersion = "1.1"; - eclipse.eclipseApiUrl = "https://test.openvsx.eclipse.org/"; - } - - @Test - void testGetPublicProfile() throws Exception { - Mockito.when( - restTemplate.exchange( - eq(PUBLIC_PROFILE_URL), - 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.getName()).isEqualTo("test"); - assertThat(profile.getGithubHandle()).isEqualTo("test"); - assertThat(profile.getPublisherAgreements()).isNotNull(); - assertThat(profile.getPublisherAgreements().getOpenVsx()).isNotNull(); - assertThat(profile.getPublisherAgreements().getOpenVsx().getVersion()).isEqualTo("1.1"); - } - - @Test - void testGetUserProfile() throws Exception { - Mockito.when(restTemplate.exchange(any(RequestEntity.class), eq(String.class))) - .thenReturn(mockProfileResponse()); - - var profile = eclipse.getUserProfile("12345"); - - assertThat(profile).isNotNull(); - - assertThat(profile.getName()).isEqualTo("test"); - assertThat(profile.getGithubHandle()).isEqualTo("test"); - assertThat(profile.getPublisherAgreements()).isNotNull(); - assertThat(profile.getPublisherAgreements().getOpenVsx()).isNotNull(); - assertThat(profile.getPublisherAgreements().getOpenVsx().getVersion()).isEqualTo("1.1"); - } - - @Test - void testGetPublisherAgreement() throws Exception { - var user = mockUser(); - user.setEclipsePersonId("test"); - - Mockito.when( - restTemplate.exchange( - eq(PUBLISHER_AGREEMENT_URL), - eq(HttpMethod.GET), - any(HttpEntity.class), - eq(String.class), - eq(Map.of("personId", "test")))) - .thenReturn(mockAgreementResponse()); - - var agreement = eclipse.getPublisherAgreement(user); - assertThat(agreement).isNotNull(); - assertThat(agreement.isActive()).isTrue(); - assertThat(agreement.documentId()).isEqualTo("abcd"); - assertThat(agreement.version()).isEqualTo("1.1"); - assertThat(agreement.timestamp()).isEqualTo(LocalDateTime.of(2020, 10, 9, 5, 10, 32)); - } - - @Test - void testCheckPublisherOutdatedAgreement() throws Exception { - var user = mockUser(); - user.setEclipsePersonId("test"); - - Mockito.when( - restTemplate.exchange( - eq(PUBLISHER_AGREEMENT_URL), - eq(HttpMethod.GET), - any(HttpEntity.class), - eq(String.class), - eq(Map.of("personId", "test")))) - .thenReturn(mockOutdatedAgreementResponse()); - - try { - eclipse.checkPublisherAgreement(user); - fail("Expected an ErrorResultException"); - } catch (ErrorResultException exc) { - assertThat(exc.getMessage()).isEqualTo( - "Your Publisher Agreement with the Eclipse Foundation is outdated (version 0.1). The current version is 1.1."); - } - } - - @Test - void testCheckPublisherOutdatedAgreementNoToken() throws Exception { - var user = mockUserNoToken(); - user.setEclipsePersonId("test"); - - Mockito.when( - restTemplate.exchange( - eq(PUBLIC_PROFILE_URL), - eq(HttpMethod.GET), - any(HttpEntity.class), - eq(String.class), - eq(Map.of("personId", "test")))) - .thenReturn(mockOutdatedProfileResponse()); - - try { - eclipse.checkPublisherAgreement(user); - fail("Expected an ErrorResultException"); - } catch (ErrorResultException exc) { - assertThat(exc.getMessage()).isEqualTo( - "Your Publisher Agreement with the Eclipse Foundation is outdated (version 0.1). The current version is 1.1."); - } - } - - @Test - void testCheckPublisherAgreementAllowed() throws Exception { - var user = mockUser(); - user.setEclipsePersonId("test"); - - Mockito.when( - restTemplate.exchange( - eq(PUBLISHER_AGREEMENT_URL), - eq(HttpMethod.GET), - any(HttpEntity.class), - eq(String.class), - eq(Map.of("personId", "test")))) - .thenReturn(mockAgreementResponse()); - - eclipse.checkPublisherAgreement(user); - } - - @Test - void testCheckPublisherAgreementAllowedNoToken() throws Exception { - var user = mockUserNoToken(); - user.setEclipsePersonId("test"); - - Mockito.when( - restTemplate.exchange( - eq(PUBLIC_PROFILE_URL), - eq(HttpMethod.GET), - any(HttpEntity.class), - eq(String.class), - eq(Map.of("personId", "test")))) - .thenReturn(mockAllowedProfileResponse()); - - eclipse.checkPublisherAgreement(user); - } - - @Test - void testGetPublisherAgreementNotFound() throws Exception { - var user = mockUser(); - user.setEclipsePersonId("test"); - - var urlTemplate = "https://test.openvsx.eclipse.org/openvsx/publisher_agreement/{personId}"; - Mockito.when( - restTemplate.exchange( - eq(urlTemplate), - eq(HttpMethod.GET), - any(HttpEntity.class), - eq(String.class), - eq(Map.of("personId", "test")))) - .thenThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)); - - var agreement = eclipse.getPublisherAgreement(user); - assertThat(agreement).isNull(); - } - - @Test - void testGetPublisherAgreementNotAuthenticated() throws Exception { - var user = mockUser(); - - var agreement = eclipse.getPublisherAgreement(user); - - assertThat(agreement).isNull(); - } - - @Test - void testSignPublisherAgreement() throws Exception { - var user = mockUser(); - Mockito.when(restTemplate.postForEntity(any(String.class), any(), eq(String.class))) - .thenReturn(mockAgreementResponse()); - Mockito.when(repositories.findVersionsByUser(user, false)) - .thenReturn(Streamable.empty()); - - var agreement = eclipse.signPublisherAgreement(user); - assertThat(agreement).isNotNull(); - assertThat(agreement.isActive()).isTrue(); - assertThat(agreement.documentId()).isEqualTo("abcd"); - assertThat(agreement.version()).isEqualTo("1.1"); - assertThat(agreement.timestamp()).isEqualTo(LocalDateTime.of(2020, 10, 9, 5, 10, 32)); - } - - @Test - void testSignPublisherAgreementReactivateExtension() throws Exception { - var user = mockUser(); - Mockito.when(restTemplate.postForEntity(any(String.class), any(), eq(String.class))) - .thenReturn(mockAgreementResponse()); - var namespace = new Namespace(); - namespace.setName("foo"); - var extension = new Extension(); - extension.setName("bar"); - extension.setNamespace(namespace); - var extVersion = new ExtensionVersion(); - extVersion.setVersion("1.0.0"); - extVersion.setTargetPlatform(TargetPlatform.NAME_UNIVERSAL); - extVersion.setExtension(extension); - extension.getVersions().add(extVersion); - Mockito.when(repositories.findVersionsByUser(user, false)) - .thenReturn(Streamable.of(extVersion)); - - var agreement = eclipse.signPublisherAgreement(user); - - assertThat(agreement).isNotNull(); - assertThat(agreement.isActive()).isTrue(); - assertThat(agreement.documentId()).isEqualTo("abcd"); - assertThat(agreement.version()).isEqualTo("1.1"); - assertThat(agreement.timestamp()).isEqualTo(LocalDateTime.of(2020, 10, 9, 5, 10, 32)); - assertThat(extVersion.isActive()).isTrue(); - assertThat(extension.isActive()).isTrue(); - } - - @Test - void testPublisherAgreementAlreadySigned() throws Exception { - var user = mockUser(); - Mockito.when(restTemplate.postForEntity(any(String.class), any(), eq(String.class))) - .thenThrow(new HttpClientErrorException(HttpStatus.CONFLICT)); - - try { - eclipse.signPublisherAgreement(user); - fail("Expected an ErrorResultException"); - } catch (ErrorResultException exc) { - assertThat(exc.getMessage()).isEqualTo("A publisher agreement is already present for user test."); - } - } - - @Test - void testRevokePublisherAgreement() { - var user = mockUser(); - user.setEclipsePersonId("test"); - - eclipse.revokePublisherAgreement(user, null); - } - - @Test - void testRevokePublisherAgreementByAdmin() { - var user = mockUser(); - user.setEclipsePersonId("test"); - - var admin = new UserData(); - admin.setLoginName("admin"); - admin.setEclipseToken(new AuthToken("67890", null, null, null, null, null)); - Mockito.when(tokens.getActiveEclipseToken(admin)) - .thenReturn(admin.getEclipseToken()); - - eclipse.revokePublisherAgreement(user, admin); - } - - private UserData mockUser() { - var user = new UserData(); - user.setLoginName("test"); - user.setProvider("github"); - user.setEclipseToken(new AuthToken("12345", null, null, null, null, null)); - Mockito.when(tokens.getActiveEclipseToken(user)) - .thenReturn(user.getEclipseToken()); - return user; - } - - private UserData mockUserNoToken() { - var user = new UserData(); - user.setLoginName("test"); - user.setProvider("github"); - Mockito.when(tokens.getActiveEclipseToken(user)) - .thenReturn(null); - return user; - } - - private ResponseEntity mockProfileResponse() throws IOException { - try (var stream = getClass().getResourceAsStream("profile-response.json")) { - assert stream != null; - var json = new String(stream.readAllBytes(), StandardCharsets.UTF_8); - return new ResponseEntity<>(json, HttpStatus.OK); - } - } - - private ResponseEntity mockOutdatedProfileResponse() throws IOException { - try (var stream = getClass().getResourceAsStream("profile-outdated-response.json")) { - assert stream != null; - var json = new String(stream.readAllBytes(), StandardCharsets.UTF_8); - return new ResponseEntity<>(json, HttpStatus.OK); - } - } - - private ResponseEntity mockAllowedProfileResponse() throws IOException { - try (var stream = getClass().getResourceAsStream("profile-allowed-response.json")) { - assert stream != null; - var json = new String(stream.readAllBytes(), StandardCharsets.UTF_8); - return new ResponseEntity<>(json, HttpStatus.OK); - } - } - - private ResponseEntity mockAgreementResponse() throws IOException { - try (var stream = getClass().getResourceAsStream("publisher-agreement-response.json")) { - assert stream != null; - var json = new String(stream.readAllBytes(), StandardCharsets.UTF_8); - return new ResponseEntity<>(json, HttpStatus.OK); - } - } - - private ResponseEntity mockOutdatedAgreementResponse() throws IOException { - try (var stream = getClass().getResourceAsStream("publisher-agreement-outdated-response.json")) { - assert stream != null; - var json = new String(stream.readAllBytes(), StandardCharsets.UTF_8); - return new ResponseEntity<>(json, HttpStatus.OK); - } - } - - @TestConfiguration - static class TestConfig { - @Bean - TransactionTemplate transactionTemplate() { - return new MockTransactionTemplate(); - } - - @Bean - EclipseService eclipseService( - EclipseTokenService tokens, - ExtensionService extensions, - EntityManager entityManager, - RestTemplate restTemplate - ) { - return new EclipseService(tokens, extensions, entityManager, restTemplate); - } - - @Bean - ExtensionService extensionService( - EntityManager entityManager, - RepositoryService repositories, - SearchUtilService search, - CacheService cache, - LogService logs, - PublishExtensionVersionHandler publishHandler, - JobRequestScheduler scheduler, - ExtensionScanService extensionScanService, - ExtensionScanPersistenceService scanPersistenceService - ) { - return new ExtensionService( - new PublishingConfig(), - entityManager, - repositories, - search, - cache, - logs, - publishHandler, - scheduler, - extensionScanService, - scanPersistenceService); - } - - @Bean - ExtensionValidator extensionValidator() { - return new ExtensionValidator(); - } - - @Bean - StorageUtilService storageUtilService( - RepositoryService repositories, - GoogleCloudStorageService googleStorage, - AzureBlobStorageService azureStorage, - LocalStorageService localStorage, - AwsStorageService awsStorage, - DownloadCountService downloadCountService, - ExtensionDownloadMetrics downloadMetrics, - SearchUtilService search, - CacheService cache, - EntityManager entityManager, - FileCacheDurationConfig fileCacheDurationConfig, - CdnServiceConfig cdnServiceConfig - ) { - return new StorageUtilService( - repositories, - googleStorage, - azureStorage, - localStorage, - awsStorage, - downloadCountService, - downloadMetrics, - search, - cache, - entityManager, - fileCacheDurationConfig, - cdnServiceConfig); - } - - @Bean - LocalStorageService localStorageService() { - return new LocalStorageService(); - } - - @Bean - LatestExtensionVersionCacheKeyGenerator latestExtensionVersionCacheKeyGenerator() { - return new LatestExtensionVersionCacheKeyGenerator(); - } - } -} diff --git a/server/src/test/java/org/eclipse/openvsx/web/SitemapControllerTest.java b/server/src/test/java/org/eclipse/openvsx/web/SitemapControllerTest.java index 93407b6d1..31cc76f98 100644 --- a/server/src/test/java/org/eclipse/openvsx/web/SitemapControllerTest.java +++ b/server/src/test/java/org/eclipse/openvsx/web/SitemapControllerTest.java @@ -24,8 +24,6 @@ import org.springframework.test.web.servlet.MockMvc; import org.eclipse.openvsx.UserService; -import org.eclipse.openvsx.eclipse.EclipseService; -import org.eclipse.openvsx.eclipse.EclipseTokenService; import org.eclipse.openvsx.repositories.RepositoryService; import org.eclipse.openvsx.security.OAuth2AttributesConfig; import org.eclipse.openvsx.security.OAuth2UserServices; @@ -38,10 +36,8 @@ @WebMvcTest(SitemapController.class) @MockitoBean( types = { - EclipseService.class, SimpleMeterRegistry.class, UserService.class, - EclipseTokenService.class, EntityManager.class } ) @@ -78,12 +74,9 @@ static class TestConfig { @Bean OAuth2UserServices oauth2UserServices( UserService users, - EclipseTokenService eclipseTokenService, - EntityManager entityManager, - EclipseService eclipse, OAuth2AttributesConfig attributesConfig ) { - return new OAuth2UserServices(users, eclipseTokenService, entityManager, eclipse, attributesConfig); + return new OAuth2UserServices(users, attributesConfig, java.util.List.of()); } @Bean diff --git a/server/src/test/resources/org/eclipse/openvsx/eclipse/profile-allowed-response.json b/server/src/test/resources/org/eclipse/openvsx/eclipse/profile-allowed-response.json deleted file mode 100644 index 42beb6416..000000000 --- a/server/src/test/resources/org/eclipse/openvsx/eclipse/profile-allowed-response.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - { - "uid": "98765", - "name": "test", - "mail": null, - "picture": "http://my-profile-picture.com", - "eca": { - "signed": true, - "can_contribute_spec_project": true - }, - "publisher_agreements": { - "open-vsx": { - "version": "1" - } - }, - "is_committer": true, - "friends": { - "friend_id": null - }, - "first_name": "Foo", - "last_name": "Bar", - "full_name": "Foo Bar", - "github_handle": "test", - "twitter_handle": "test", - "org": "Test", - "job_title": "Software Engineer", - "website": "http://test.com", - "country": { - "code": null, - "name": null - }, - "bio": "Bla bla bla.", - "interests": [ - "Software Engineering" - ], - "working_groups_interests": [], - "forums_url": "https://api.eclipse.org/account/profile/test/forum", - "projects_url": "https://api.eclipse.org/account/profile/test/projects", - "gerrit_url": "https://api.eclipse.org/account/profile/test/gerrit", - "mailinglist_url": "https://api.eclipse.org/account/profile/test/mailing-list", - "mpc_favorites_url": "https://api.eclipse.org/marketplace/favorites/?name=test" - } -] diff --git a/server/src/test/resources/org/eclipse/openvsx/eclipse/profile-outdated-response.json b/server/src/test/resources/org/eclipse/openvsx/eclipse/profile-outdated-response.json deleted file mode 100644 index d43d1085d..000000000 --- a/server/src/test/resources/org/eclipse/openvsx/eclipse/profile-outdated-response.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - { - "uid": "98765", - "name": "test", - "mail": null, - "picture": "http://my-profile-picture.com", - "eca": { - "signed": true, - "can_contribute_spec_project": true - }, - "publisher_agreements": { - "open-vsx": { - "version": "0.1" - } - }, - "is_committer": true, - "friends": { - "friend_id": null - }, - "first_name": "Foo", - "last_name": "Bar", - "full_name": "Foo Bar", - "github_handle": "test", - "twitter_handle": "test", - "org": "Test", - "job_title": "Software Engineer", - "website": "http://test.com", - "country": { - "code": null, - "name": null - }, - "bio": "Bla bla bla.", - "interests": [ - "Software Engineering" - ], - "working_groups_interests": [], - "forums_url": "https://api.eclipse.org/account/profile/test/forum", - "projects_url": "https://api.eclipse.org/account/profile/test/projects", - "gerrit_url": "https://api.eclipse.org/account/profile/test/gerrit", - "mailinglist_url": "https://api.eclipse.org/account/profile/test/mailing-list", - "mpc_favorites_url": "https://api.eclipse.org/marketplace/favorites/?name=test" - } -] diff --git a/server/src/test/resources/org/eclipse/openvsx/eclipse/profile-response.json b/server/src/test/resources/org/eclipse/openvsx/eclipse/profile-response.json deleted file mode 100644 index b352a80db..000000000 --- a/server/src/test/resources/org/eclipse/openvsx/eclipse/profile-response.json +++ /dev/null @@ -1,43 +0,0 @@ -[ - { - "uid": "98765", - "name": "test", - "mail": null, - "picture": "http://my-profile-picture.com", - "eca": { - "signed": true, - "can_contribute_spec_project": true - }, - "publisher_agreements": { - "open-vsx": { - "version": "1.1" - } - }, - "is_committer": true, - "friends": { - "friend_id": null - }, - "first_name": "Foo", - "last_name": "Bar", - "full_name": "Foo Bar", - "github_handle": "test", - "twitter_handle": "test", - "org": "Test", - "job_title": "Software Engineer", - "website": "http://test.com", - "country": { - "code": null, - "name": null - }, - "bio": "Bla bla bla.", - "interests": [ - "Software Engineering" - ], - "working_groups_interests": [], - "forums_url": "https://api.eclipse.org/account/profile/test/forum", - "projects_url": "https://api.eclipse.org/account/profile/test/projects", - "gerrit_url": "https://api.eclipse.org/account/profile/test/gerrit", - "mailinglist_url": "https://api.eclipse.org/account/profile/test/mailing-list", - "mpc_favorites_url": "https://api.eclipse.org/marketplace/favorites/?name=test" - } -] diff --git a/server/src/test/resources/org/eclipse/openvsx/eclipse/publisher-agreement-outdated-response.json b/server/src/test/resources/org/eclipse/openvsx/eclipse/publisher-agreement-outdated-response.json deleted file mode 100644 index da7a90d34..000000000 --- a/server/src/test/resources/org/eclipse/openvsx/eclipse/publisher-agreement-outdated-response.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "PersonID": "test", - "DocumentID": "abcd", - "Version": "0.1", - "EffectiveDate": "2020-10-09 05:10:32", - "ReceivedDate": "2020-10-09", - "ExpirationDate": null, - "ScannedDocumentBLOB": null, - "ScannedDocumentMime": "application/json", - "ScannedDocumentBytes": "117", - "ScannedDocumentFileName": "openvsx-publisher-agreement.json" -} diff --git a/server/src/test/resources/org/eclipse/openvsx/eclipse/publisher-agreement-response.json b/server/src/test/resources/org/eclipse/openvsx/eclipse/publisher-agreement-response.json deleted file mode 100644 index 2579c6453..000000000 --- a/server/src/test/resources/org/eclipse/openvsx/eclipse/publisher-agreement-response.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "PersonID": "test", - "DocumentID": "abcd", - "Version": "1.1", - "EffectiveDate": "2020-10-09 05:10:32", - "ReceivedDate": "2020-10-09", - "ExpirationDate": null, - "ScannedDocumentBLOB": null, - "ScannedDocumentMime": "application/json", - "ScannedDocumentBytes": "117", - "ScannedDocumentFileName": "openvsx-publisher-agreement.json" -}