From 736cbf4602d273f7e45434137c851d237729b35f Mon Sep 17 00:00:00 2001 From: Paul Marechal Date: Sat, 25 Nov 2023 03:07:57 -0500 Subject: [PATCH 1/7] refactor: introduce `AuthUser` interface Replace the references to `OAuth2User` by `AuthUser`. This allows downstream extenders to more easily contribute alternative OAuth2 providers: If the expected data is stored in different attributes it will be possible to bridge it by implementing the proper `AuthUser`. --- .../java/org/eclipse/openvsx/UserService.java | 54 ++++++++++--------- .../eclipse/openvsx/security/AuthUser.java | 48 +++++++++++++++++ .../openvsx/security/OAuth2UserServices.java | 51 ++++++++++++++---- 3 files changed, 119 insertions(+), 34 deletions(-) create mode 100644 server/src/main/java/org/eclipse/openvsx/security/AuthUser.java diff --git a/server/src/main/java/org/eclipse/openvsx/UserService.java b/server/src/main/java/org/eclipse/openvsx/UserService.java index 0740fba56..ca8d274a3 100644 --- a/server/src/main/java/org/eclipse/openvsx/UserService.java +++ b/server/src/main/java/org/eclipse/openvsx/UserService.java @@ -9,13 +9,23 @@ ********************************************************************************/ package org.eclipse.openvsx; -import com.google.common.base.Joiner; +import static org.eclipse.openvsx.cache.CacheService.CACHE_NAMESPACE_DETAILS_JSON; +import static org.eclipse.openvsx.util.UrlUtil.createApiUrl; + +import java.util.Objects; +import java.util.UUID; + import org.eclipse.openvsx.cache.CacheService; -import org.eclipse.openvsx.entities.*; +import org.eclipse.openvsx.entities.FileResource; +import org.eclipse.openvsx.entities.Namespace; +import org.eclipse.openvsx.entities.NamespaceMembership; +import org.eclipse.openvsx.entities.PersonalAccessToken; +import org.eclipse.openvsx.entities.UserData; import org.eclipse.openvsx.json.AccessTokenJson; import org.eclipse.openvsx.json.NamespaceDetailsJson; import org.eclipse.openvsx.json.ResultJson; import org.eclipse.openvsx.repositories.RepositoryService; +import org.eclipse.openvsx.security.AuthUser; import org.eclipse.openvsx.security.IdPrincipal; import org.eclipse.openvsx.storage.StorageUtilService; import org.eclipse.openvsx.util.ErrorResultException; @@ -25,16 +35,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Component; +import com.google.common.base.Joiner; + import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; -import java.util.Objects; -import java.util.UUID; - -import static org.eclipse.openvsx.cache.CacheService.CACHE_NAMESPACE_DETAILS_JSON; -import static org.eclipse.openvsx.util.UrlUtil.createApiUrl; @Component public class UserService { @@ -66,44 +72,44 @@ public UserData findLoggedInUser() { } @Transactional - public UserData registerNewUser(OAuth2User oauth2User) { + public UserData registerNewUser(AuthUser authUser) { var user = new UserData(); - user.setProvider("github"); - user.setAuthId(oauth2User.getName()); - user.setLoginName(oauth2User.getAttribute("login")); - user.setFullName(oauth2User.getAttribute("name")); - user.setEmail(oauth2User.getAttribute("email")); - user.setProviderUrl(oauth2User.getAttribute("html_url")); - user.setAvatarUrl(oauth2User.getAttribute("avatar_url")); + user.setProvider(authUser.getProviderId()); + user.setAuthId(authUser.getAuthId()); + user.setLoginName(authUser.getLoginName()); + user.setFullName(authUser.getFullName()); + user.setEmail(authUser.getEmail()); + user.setProviderUrl(authUser.getProviderUrl()); + user.setAvatarUrl(authUser.getAvatarUrl()); entityManager.persist(user); return user; } @Transactional - public UserData updateExistingUser(UserData user, OAuth2User oauth2User) { - if ("github".equals(user.getProvider())) { + public UserData updateExistingUser(UserData user, AuthUser authUser) { + if (authUser.getProviderId().equals(user.getProvider())) { var updated = false; - String loginName = oauth2User.getAttribute("login"); + String loginName = authUser.getLoginName(); if (loginName != null && !loginName.equals(user.getLoginName())) { user.setLoginName(loginName); updated = true; } - String fullName = oauth2User.getAttribute("name"); + String fullName = authUser.getFullName(); if (fullName != null && !fullName.equals(user.getFullName())) { user.setFullName(fullName); updated = true; } - String email = oauth2User.getAttribute("email"); + String email = authUser.getEmail(); if (email != null && !email.equals(user.getEmail())) { user.setEmail(email); updated = true; } - String providerUrl = oauth2User.getAttribute("html_url"); + String providerUrl = authUser.getProviderUrl(); if (providerUrl != null && !providerUrl.equals(user.getProviderUrl())) { user.setProviderUrl(providerUrl); updated = true; } - String avatarUrl = oauth2User.getAttribute("avatar_url"); + String avatarUrl = authUser.getAvatarUrl(); if (avatarUrl != null && !avatarUrl.equals(user.getAvatarUrl())) { user.setAvatarUrl(avatarUrl); updated = true; @@ -298,4 +304,4 @@ public ResultJson deleteAccessToken(UserData user, long id) { token.setActive(false); return ResultJson.success("Deleted access token for user " + user.getLoginName() + "."); } -} \ No newline at end of file +} diff --git a/server/src/main/java/org/eclipse/openvsx/security/AuthUser.java b/server/src/main/java/org/eclipse/openvsx/security/AuthUser.java new file mode 100644 index 000000000..5a1026434 --- /dev/null +++ b/server/src/main/java/org/eclipse/openvsx/security/AuthUser.java @@ -0,0 +1,48 @@ +/******************************************************************************** + * Copyright (c) 2023 Ericsson 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; + +/** + * Encapsulate information about freshly authenticated users. + * + * Different OAuth2 providers may return the same information with different + * attribute keys. This interface allows bridging arbitrary providers. + */ +public interface AuthUser { + /** + * @return Non-human readable unique identifier. + */ + String getAuthId(); + /** + * @return The user's avatar URL. Some services require post-processing to get the actual value for it + * (the value returned is a template and you need to remplace variables). + */ + String getAvatarUrl(); + /** + * @return The user's email. + */ + String getEmail(); + /** + * @return The user's full name (first and last names). + */ + String getFullName(); + /** + * @return The login name for the user. Human-readable unique name. AKA username. + */ + String getLoginName(); + /** + * @return The authentication provider unique name, e.g. `github`, `eclipse`, etc. + */ + String getProviderId(); + /** + * @return The authentication provider URL. + */ + String getProviderUrl(); +} 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 1a69ee538..ea723d476 100644 --- a/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java +++ b/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java @@ -9,13 +9,15 @@ ********************************************************************************/ package org.eclipse.openvsx.security; -import static org.eclipse.openvsx.security.CodedAuthException.*; +import static org.eclipse.openvsx.security.CodedAuthException.ECLIPSE_MISMATCH_GITHUB_ID; +import static org.eclipse.openvsx.security.CodedAuthException.ECLIPSE_MISSING_GITHUB_ID; +import static org.eclipse.openvsx.security.CodedAuthException.INVALID_GITHUB_USER; +import static org.eclipse.openvsx.security.CodedAuthException.NEED_MAIN_LOGIN; +import static org.eclipse.openvsx.security.CodedAuthException.UNSUPPORTED_REGISTRATION; import java.util.Collection; import java.util.Collections; -import jakarta.persistence.EntityManager; - import org.apache.commons.lang3.StringUtils; import org.eclipse.openvsx.UserService; import org.eclipse.openvsx.eclipse.EclipseService; @@ -39,6 +41,8 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Service; +import jakarta.persistence.EntityManager; + @Service public class OAuth2UserServices { @@ -47,7 +51,7 @@ public class OAuth2UserServices { @Autowired TokenService tokens; - + @Autowired RepositoryService repositories; @@ -112,16 +116,17 @@ public IdPrincipal loadUser(OAuth2UserRequest userRequest) { } private IdPrincipal loadGitHubUser(OAuth2UserRequest userRequest) { - var authUser = delegate.loadUser(userRequest); - String loginName = authUser.getAttribute("login"); + var authUser = new GithubAuthUser(delegate.loadUser(userRequest)); + String loginName = authUser.getLoginName(); if (StringUtils.isEmpty(loginName)) throw new CodedAuthException("Invalid login: missing 'login' field.", INVALID_GITHUB_USER); var userData = repositories.findUserByLoginName("github", loginName); - if (userData == null) + if (userData == null) { userData = users.registerNewUser(authUser); - else + } else { users.updateExistingUser(userData, authUser); - return new IdPrincipal(userData.getId(), authUser.getName(), getAuthorities(userData)); + } + return new IdPrincipal(userData.getId(), authUser.getAuthId(), getAuthorities(userData)); } private IdPrincipal loadEclipseUser(OAuth2UserRequest userRequest) { @@ -169,4 +174,30 @@ private Collection getAuthorities(UserData userData) { } } -} \ No newline at end of file + static class GithubAuthUser implements AuthUser { + + final String authId; + final String avatarUrl; + final String email; + final String fullName; + final String loginName; + final String providerUrl; + + @Override public String getAuthId() { return authId; } + @Override public String getAvatarUrl() { return avatarUrl; } + @Override public String getEmail() { return email; } + @Override public String getFullName() { return fullName; } + @Override public String getLoginName() { return loginName; } + @Override public String getProviderId() { return "github"; } + @Override public String getProviderUrl() { return providerUrl; } + + public GithubAuthUser(OAuth2User oauth2User) { + authId = oauth2User.getName(); + avatarUrl = oauth2User.getAttribute("avatar_url"); + email = oauth2User.getAttribute("email"); + fullName = oauth2User.getAttribute("name"); + loginName = oauth2User.getAttribute("login"); + providerUrl = oauth2User.getAttribute("html_url"); + } + } +} From 0f8c930e6842e977bf1a97abc343a76507464377 Mon Sep 17 00:00:00 2001 From: Paul Marechal Date: Sun, 26 Nov 2023 14:50:25 -0500 Subject: [PATCH 2/7] fix tests --- .../openvsx/security/DefaultAuthUser.java | 68 +++++++++++++++++++ .../openvsx/security/OAuth2UserServices.java | 31 +-------- .../openvsx/cache/CacheServiceTest.java | 36 ++++++---- 3 files changed, 94 insertions(+), 41 deletions(-) create mode 100644 server/src/main/java/org/eclipse/openvsx/security/DefaultAuthUser.java diff --git a/server/src/main/java/org/eclipse/openvsx/security/DefaultAuthUser.java b/server/src/main/java/org/eclipse/openvsx/security/DefaultAuthUser.java new file mode 100644 index 000000000..50b617553 --- /dev/null +++ b/server/src/main/java/org/eclipse/openvsx/security/DefaultAuthUser.java @@ -0,0 +1,68 @@ +/******************************************************************************** + * Copyright (c) 2023 Ericsson 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.core.user.OAuth2User; + +public class DefaultAuthUser implements AuthUser { + + final String authId; + final String avatarUrl; + final String email; + final String fullName; + final String loginName; + final String providerId; + final String providerUrl; + + public DefaultAuthUser(String providerId, OAuth2User oauth2User) { + authId = oauth2User.getName(); + avatarUrl = oauth2User.getAttribute("avatar_url"); + email = oauth2User.getAttribute("email"); + fullName = oauth2User.getAttribute("name"); + loginName = oauth2User.getAttribute("login"); + this.providerId = providerId; + providerUrl = oauth2User.getAttribute("html_url"); + } + + @Override + public String getAuthId() { + return authId; + } + + @Override + public String getAvatarUrl() { + return avatarUrl; + } + + @Override + public String getEmail() { + return email; + } + + @Override + public String getFullName() { + return fullName; + } + + @Override + public String getLoginName() { + return loginName; + } + + @Override + public String getProviderId() { + return providerId; + } + + @Override + public String getProviderUrl() { + return providerUrl; + } +} 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 ea723d476..efd5cab4f 100644 --- a/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java +++ b/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java @@ -116,11 +116,11 @@ public IdPrincipal loadUser(OAuth2UserRequest userRequest) { } private IdPrincipal loadGitHubUser(OAuth2UserRequest userRequest) { - var authUser = new GithubAuthUser(delegate.loadUser(userRequest)); + var authUser = new DefaultAuthUser("github", delegate.loadUser(userRequest)); String loginName = authUser.getLoginName(); if (StringUtils.isEmpty(loginName)) throw new CodedAuthException("Invalid login: missing 'login' field.", INVALID_GITHUB_USER); - var userData = repositories.findUserByLoginName("github", loginName); + var userData = repositories.findUserByLoginName(authUser.getProviderId(), loginName); if (userData == null) { userData = users.registerNewUser(authUser); } else { @@ -173,31 +173,4 @@ private Collection getAuthorities(UserData userData) { return Collections.emptyList(); } } - - static class GithubAuthUser implements AuthUser { - - final String authId; - final String avatarUrl; - final String email; - final String fullName; - final String loginName; - final String providerUrl; - - @Override public String getAuthId() { return authId; } - @Override public String getAvatarUrl() { return avatarUrl; } - @Override public String getEmail() { return email; } - @Override public String getFullName() { return fullName; } - @Override public String getLoginName() { return loginName; } - @Override public String getProviderId() { return "github"; } - @Override public String getProviderUrl() { return providerUrl; } - - public GithubAuthUser(OAuth2User oauth2User) { - authId = oauth2User.getName(); - avatarUrl = oauth2User.getAttribute("avatar_url"); - email = oauth2User.getAttribute("email"); - fullName = oauth2User.getAttribute("name"); - loginName = oauth2User.getAttribute("login"); - providerUrl = oauth2User.getAttribute("html_url"); - } - } } diff --git a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java index 272c1c01f..4010e5fe1 100644 --- a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java @@ -9,14 +9,35 @@ * ****************************************************************************** */ package org.eclipse.openvsx.cache; +import static org.eclipse.openvsx.cache.CacheService.CACHE_EXTENSION_JSON; +import static org.eclipse.openvsx.entities.FileResource.DOWNLOAD; +import static org.eclipse.openvsx.entities.FileResource.STORAGE_DB; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + import org.eclipse.openvsx.ExtensionService; import org.eclipse.openvsx.LocalRegistryService; import org.eclipse.openvsx.UserService; import org.eclipse.openvsx.admin.AdminService; -import org.eclipse.openvsx.entities.*; +import org.eclipse.openvsx.entities.Extension; +import org.eclipse.openvsx.entities.ExtensionReview; +import org.eclipse.openvsx.entities.ExtensionVersion; +import org.eclipse.openvsx.entities.FileResource; +import org.eclipse.openvsx.entities.Namespace; +import org.eclipse.openvsx.entities.PersonalAccessToken; +import org.eclipse.openvsx.entities.UserData; import org.eclipse.openvsx.json.ExtensionJson; import org.eclipse.openvsx.json.ReviewJson; import org.eclipse.openvsx.repositories.RepositoryService; +import org.eclipse.openvsx.security.DefaultAuthUser; import org.eclipse.openvsx.security.IdPrincipal; import org.eclipse.openvsx.util.TimeUtil; import org.junit.jupiter.api.Test; @@ -34,16 +55,6 @@ import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -import static org.eclipse.openvsx.cache.CacheService.CACHE_EXTENSION_JSON; -import static org.eclipse.openvsx.entities.FileResource.DOWNLOAD; -import static org.eclipse.openvsx.entities.FileResource.STORAGE_DB; -import static org.junit.jupiter.api.Assertions.*; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") @@ -113,7 +124,8 @@ public void testUpdateExistingUser() { var user = extVersion.getPublishedWith().getUser(); var oauthUser = new DefaultOAuth2User(authorities, attributes, "name"); - users.updateExistingUser(user, oauthUser); + var authUser = new DefaultAuthUser(authority, oauthUser); + users.updateExistingUser(user, authUser); assertNull(cache.getCache(CACHE_EXTENSION_JSON).get(cacheKey, ExtensionJson.class)); var json = registry.getExtension(namespace.getName(), extension.getName(), extVersion.getTargetPlatform(), extVersion.getVersion()); From 64b943fd32a3b54eaf69122f8fb38af010f4b682 Mon Sep 17 00:00:00 2001 From: Paul Marechal Date: Mon, 4 Mar 2024 11:48:25 -0500 Subject: [PATCH 3/7] add AuthUserFactory remove dependency on OAuth2User from AuthUser by introducing an AuthUserFactory service. This should allow extenders to more easily customize attribute access when creating AuthUser instances. --- .../openvsx/security/AuthUserFactory.java | 20 ++++++++++++++++ .../openvsx/security/DefaultAuthUser.java | 24 ++++++++++++------- .../openvsx/security/OAuth2UserServices.java | 5 +++- .../openvsx/cache/CacheServiceTest.java | 5 ++-- 4 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 server/src/main/java/org/eclipse/openvsx/security/AuthUserFactory.java diff --git a/server/src/main/java/org/eclipse/openvsx/security/AuthUserFactory.java b/server/src/main/java/org/eclipse/openvsx/security/AuthUserFactory.java new file mode 100644 index 000000000..2ea73db53 --- /dev/null +++ b/server/src/main/java/org/eclipse/openvsx/security/AuthUserFactory.java @@ -0,0 +1,20 @@ +package org.eclipse.openvsx.security; + +import org.springframework.security.oauth2.core.user.OAuth2User; +import org.springframework.stereotype.Service; + +@Service +public class AuthUserFactory { + + public AuthUser createAuthUser(String providerId, OAuth2User oauth2User) { + return new DefaultAuthUser( + oauth2User.getName(), + oauth2User.getAttribute("avatar_url"), + oauth2User.getAttribute("email"), + oauth2User.getAttribute("name"), + oauth2User.getAttribute("login"), + providerId, + oauth2User.getAttribute("html_url") + ); + } +} diff --git a/server/src/main/java/org/eclipse/openvsx/security/DefaultAuthUser.java b/server/src/main/java/org/eclipse/openvsx/security/DefaultAuthUser.java index 50b617553..27d85d2cd 100644 --- a/server/src/main/java/org/eclipse/openvsx/security/DefaultAuthUser.java +++ b/server/src/main/java/org/eclipse/openvsx/security/DefaultAuthUser.java @@ -9,8 +9,6 @@ ********************************************************************************/ package org.eclipse.openvsx.security; -import org.springframework.security.oauth2.core.user.OAuth2User; - public class DefaultAuthUser implements AuthUser { final String authId; @@ -21,14 +19,22 @@ public class DefaultAuthUser implements AuthUser { final String providerId; final String providerUrl; - public DefaultAuthUser(String providerId, OAuth2User oauth2User) { - authId = oauth2User.getName(); - avatarUrl = oauth2User.getAttribute("avatar_url"); - email = oauth2User.getAttribute("email"); - fullName = oauth2User.getAttribute("name"); - loginName = oauth2User.getAttribute("login"); + public DefaultAuthUser( + final String authId, + final String avatarUrl, + final String email, + final String fullName, + final String loginName, + final String providerId, + final String providerUrl + ) { + this.authId = authId; + this.avatarUrl = avatarUrl; + this.email = email; + this.fullName = fullName; + this.loginName = loginName; this.providerId = providerId; - providerUrl = oauth2User.getAttribute("html_url"); + this.providerUrl = providerUrl; } @Override 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 efd5cab4f..6b5e05dad 100644 --- a/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java +++ b/server/src/main/java/org/eclipse/openvsx/security/OAuth2UserServices.java @@ -61,6 +61,9 @@ public class OAuth2UserServices { @Autowired EclipseService eclipse; + @Autowired + AuthUserFactory authUserFactory; + private final DefaultOAuth2UserService delegate = new DefaultOAuth2UserService(); private final OAuth2UserService oauth2; private final OAuth2UserService oidc; @@ -116,7 +119,7 @@ public IdPrincipal loadUser(OAuth2UserRequest userRequest) { } private IdPrincipal loadGitHubUser(OAuth2UserRequest userRequest) { - var authUser = new DefaultAuthUser("github", delegate.loadUser(userRequest)); + var authUser = authUserFactory.createAuthUser("github", delegate.loadUser(userRequest)); String loginName = authUser.getLoginName(); if (StringUtils.isEmpty(loginName)) throw new CodedAuthException("Invalid login: missing 'login' field.", INVALID_GITHUB_USER); diff --git a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java index 4010e5fe1..86592202a 100644 --- a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java @@ -37,7 +37,7 @@ import org.eclipse.openvsx.json.ExtensionJson; import org.eclipse.openvsx.json.ReviewJson; import org.eclipse.openvsx.repositories.RepositoryService; -import org.eclipse.openvsx.security.DefaultAuthUser; +import org.eclipse.openvsx.security.AuthUserFactory; import org.eclipse.openvsx.security.IdPrincipal; import org.eclipse.openvsx.util.TimeUtil; import org.junit.jupiter.api.Test; @@ -124,7 +124,8 @@ public void testUpdateExistingUser() { var user = extVersion.getPublishedWith().getUser(); var oauthUser = new DefaultOAuth2User(authorities, attributes, "name"); - var authUser = new DefaultAuthUser(authority, oauthUser); + var authUserFactory = new AuthUserFactory(); + var authUser = authUserFactory.createAuthUser(authority, oauthUser); users.updateExistingUser(user, authUser); assertNull(cache.getCache(CACHE_EXTENSION_JSON).get(cacheKey, ExtensionJson.class)); From 3c02760c1197675e0b00612db7924a1416fac7a2 Mon Sep 17 00:00:00 2001 From: Paul Marechal Date: Mon, 4 Mar 2024 15:03:48 -0500 Subject: [PATCH 4/7] update tests --- .../test/java/org/eclipse/openvsx/cache/CacheServiceTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java index 86592202a..9896187aa 100644 --- a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java @@ -81,6 +81,9 @@ public class CacheServiceTest { @Autowired RepositoryService repositories; + @Autowired + AuthUserFactory authUserFactory; + @Test @Transactional public void testGetExtension() { @@ -124,7 +127,6 @@ public void testUpdateExistingUser() { var user = extVersion.getPublishedWith().getUser(); var oauthUser = new DefaultOAuth2User(authorities, attributes, "name"); - var authUserFactory = new AuthUserFactory(); var authUser = authUserFactory.createAuthUser(authority, oauthUser); users.updateExistingUser(user, authUser); assertNull(cache.getCache(CACHE_EXTENSION_JSON).get(cacheKey, ExtensionJson.class)); From 9ce6fe861ee52eafe2237ff425bcffcabfac0f98 Mon Sep 17 00:00:00 2001 From: Paul Marechal Date: Mon, 4 Mar 2024 17:12:49 -0500 Subject: [PATCH 5/7] add test bean for AuthUserFactory --- .../org/eclipse/openvsx/RegistryAPITest.java | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java index 33d2553e7..075e6c6e0 100644 --- a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java @@ -27,6 +27,7 @@ import org.eclipse.openvsx.search.ExtensionSearch; import org.eclipse.openvsx.search.ISearchService; import org.eclipse.openvsx.search.SearchUtilService; +import org.eclipse.openvsx.security.AuthUserFactory; import org.eclipse.openvsx.security.OAuth2UserServices; import org.eclipse.openvsx.security.SecurityConfig; import org.eclipse.openvsx.security.TokenService; @@ -118,7 +119,7 @@ public void testPublicNamespace() throws Exception { var namespace = mockNamespace(); Mockito.when(repositories.countMemberships(namespace, NamespaceMembership.ROLE_OWNER)) .thenReturn(0L); - + mockMvc.perform(get("/api/{namespace}", "foobar")) .andExpect(status().isOk()) .andExpect(content().json(namespaceJson(n -> { @@ -1207,7 +1208,7 @@ public void testCreateNamespace() throws Exception { .andExpect(redirectedUrl("http://localhost/api/foobar")) .andExpect(content().json(successJson("Created namespace foobar"))); } - + @Test public void testCreateNamespaceNoName() throws Exception { mockAccessToken(); @@ -1217,7 +1218,7 @@ public void testCreateNamespaceNoName() throws Exception { .andExpect(status().isOk()) .andExpect(content().json(errorJson("Missing required property 'name'."))); } - + @Test public void testCreateNamespaceInvalidName() throws Exception { mockAccessToken(); @@ -1227,7 +1228,7 @@ public void testCreateNamespaceInvalidName() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Invalid namespace name: foo.bar"))); } - + @Test public void testCreateNamespaceInactiveToken() throws Exception { var token = mockAccessToken(); @@ -1238,7 +1239,7 @@ public void testCreateNamespaceInactiveToken() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Invalid access token."))); } - + @Test public void testCreateExistingNamespace() throws Exception { mockAccessToken(); @@ -1246,7 +1247,7 @@ public void testCreateExistingNamespace() throws Exception { namespace.setName("foobar"); Mockito.when(repositories.findNamespace("foobar")) .thenReturn(namespace); - + mockMvc.perform(post("/api/-/namespace/create?token={token}", "my_token") .contentType(MediaType.APPLICATION_JSON) .content(namespaceJson(n -> { n.name = "foobar"; }))) @@ -1296,7 +1297,7 @@ public void testVerifyTokenNoPermission() throws Exception { mockMvc.perform(get("/api/{namespace}/verify-pat?token={token}", "foobar", "my_token")) .andExpect(status().isBadRequest()); } - + @Test public void testPublishOrphan() throws Exception { mockForPublish("orphan"); @@ -1307,7 +1308,7 @@ public void testPublishOrphan() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Insufficient access rights for publisher: foo"))); } - + @Test public void testPublishRequireLicenseNone() throws Exception { var previousRequireLicense = extensions.requireLicense; @@ -1324,7 +1325,7 @@ public void testPublishRequireLicenseNone() throws Exception { extensions.requireLicense = previousRequireLicense; } } - + @Test public void testPublishRequireLicenseOk() throws Exception { var previousRequireLicense = extensions.requireLicense; @@ -1350,7 +1351,7 @@ public void testPublishRequireLicenseOk() throws Exception { extensions.requireLicense = previousRequireLicense; } } - + @Test public void testPublishInactiveToken() throws Exception { mockForPublish("invalid"); @@ -1361,7 +1362,7 @@ public void testPublishInactiveToken() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Invalid access token."))); } - + @Test public void testPublishUnknownNamespace() throws Exception { mockAccessToken(); @@ -1373,7 +1374,7 @@ public void testPublishUnknownNamespace() throws Exception { .andExpect(content().json(errorJson("Unknown publisher: foo" + "\nUse the 'create-namespace' command to create a namespace corresponding to your publisher name."))); } - + @Test public void testPublishVerifiedOwner() throws Exception { mockForPublish("owner"); @@ -1535,7 +1536,7 @@ public void testPublishInvalidVersion() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("The version string 'latest' is reserved."))); } - + @Test public void testPostReview() throws Exception { var user = mockUserData(); @@ -1558,7 +1559,7 @@ public void testPostReview() throws Exception { .andExpect(status().isCreated()) .andExpect(content().json(successJson("Added review for foo.bar"))); } - + @Test public void testPostReviewNotLoggedIn() throws Exception { mockMvc.perform(post("/api/{namespace}/{extension}/review", "foo", "bar") @@ -1568,7 +1569,7 @@ public void testPostReviewNotLoggedIn() throws Exception { })).with(csrf().asHeader())) .andExpect(status().isForbidden()); } - + @Test public void testPostReviewInvalidRating() throws Exception { mockMvc.perform(post("/api/{namespace}/{extension}/review", "foo", "bar") @@ -1581,7 +1582,7 @@ public void testPostReviewInvalidRating() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("The rating must be an integer number between 0 and 5."))); } - + @Test public void testPostReviewUnknownExtension() throws Exception { mockUserData(); @@ -1595,7 +1596,7 @@ public void testPostReviewUnknownExtension() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Extension not found: foo.bar"))); } - + @Test public void testPostExistingReview() throws Exception { var user = mockUserData(); @@ -1620,7 +1621,7 @@ public void testPostExistingReview() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("You must not submit more than one review for an extension."))); } - + @Test public void testDeleteReview() throws Exception { var user = mockUserData(); @@ -1643,13 +1644,13 @@ public void testDeleteReview() throws Exception { .andExpect(status().isOk()) .andExpect(content().json(successJson("Deleted review for foo.bar"))); } - + @Test public void testDeleteReviewNotLoggedIn() throws Exception { mockMvc.perform(post("/api/{namespace}/{extension}/review/delete", "foo", "bar").with(csrf())) .andExpect(status().isForbidden()); } - + @Test public void testDeleteReviewUnknownExtension() throws Exception { mockUserData(); @@ -1659,7 +1660,7 @@ public void testDeleteReviewUnknownExtension() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Extension not found: foo.bar"))); } - + @Test public void testDeleteNonExistingReview() throws Exception { var user = mockUserData(); @@ -2088,7 +2089,7 @@ private PersonalAccessToken mockAccessToken() { .thenReturn(token); return token; } - + private void mockForPublish(String mode) { var token = mockAccessToken(); if (mode.equals("invalid")) { @@ -2262,10 +2263,11 @@ private byte[] createExtensionPackage(String name, String version, String licens archive.finish(); return bytes.toByteArray(); } - + @TestConfiguration @Import(SecurityConfig.class) static class TestConfig { + @Bean TransactionTemplate transactionTemplate() { return new MockTransactionTemplate(); @@ -2318,5 +2320,10 @@ LatestExtensionVersionCacheKeyGenerator latestExtensionVersionCacheKeyGenerator( PublishExtensionVersionHandler publishExtensionVersionHandler() { return new PublishExtensionVersionHandler(); } + + @Bean + AuthUserFactory authUserFactory() { + return new AuthUserFactory(); + } } -} \ No newline at end of file +} From 7480927e4448c5a8be735dd09add36225741bb72 Mon Sep 17 00:00:00 2001 From: Paul Marechal Date: Tue, 5 Mar 2024 11:06:27 -0500 Subject: [PATCH 6/7] move AuthUserFactory bean around --- .../org/eclipse/openvsx/RegistryAPITest.java | 55 ++++++++----------- .../openvsx/cache/CacheServiceTest.java | 11 ++++ 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java index 075e6c6e0..33d2553e7 100644 --- a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java @@ -27,7 +27,6 @@ import org.eclipse.openvsx.search.ExtensionSearch; import org.eclipse.openvsx.search.ISearchService; import org.eclipse.openvsx.search.SearchUtilService; -import org.eclipse.openvsx.security.AuthUserFactory; import org.eclipse.openvsx.security.OAuth2UserServices; import org.eclipse.openvsx.security.SecurityConfig; import org.eclipse.openvsx.security.TokenService; @@ -119,7 +118,7 @@ public void testPublicNamespace() throws Exception { var namespace = mockNamespace(); Mockito.when(repositories.countMemberships(namespace, NamespaceMembership.ROLE_OWNER)) .thenReturn(0L); - + mockMvc.perform(get("/api/{namespace}", "foobar")) .andExpect(status().isOk()) .andExpect(content().json(namespaceJson(n -> { @@ -1208,7 +1207,7 @@ public void testCreateNamespace() throws Exception { .andExpect(redirectedUrl("http://localhost/api/foobar")) .andExpect(content().json(successJson("Created namespace foobar"))); } - + @Test public void testCreateNamespaceNoName() throws Exception { mockAccessToken(); @@ -1218,7 +1217,7 @@ public void testCreateNamespaceNoName() throws Exception { .andExpect(status().isOk()) .andExpect(content().json(errorJson("Missing required property 'name'."))); } - + @Test public void testCreateNamespaceInvalidName() throws Exception { mockAccessToken(); @@ -1228,7 +1227,7 @@ public void testCreateNamespaceInvalidName() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Invalid namespace name: foo.bar"))); } - + @Test public void testCreateNamespaceInactiveToken() throws Exception { var token = mockAccessToken(); @@ -1239,7 +1238,7 @@ public void testCreateNamespaceInactiveToken() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Invalid access token."))); } - + @Test public void testCreateExistingNamespace() throws Exception { mockAccessToken(); @@ -1247,7 +1246,7 @@ public void testCreateExistingNamespace() throws Exception { namespace.setName("foobar"); Mockito.when(repositories.findNamespace("foobar")) .thenReturn(namespace); - + mockMvc.perform(post("/api/-/namespace/create?token={token}", "my_token") .contentType(MediaType.APPLICATION_JSON) .content(namespaceJson(n -> { n.name = "foobar"; }))) @@ -1297,7 +1296,7 @@ public void testVerifyTokenNoPermission() throws Exception { mockMvc.perform(get("/api/{namespace}/verify-pat?token={token}", "foobar", "my_token")) .andExpect(status().isBadRequest()); } - + @Test public void testPublishOrphan() throws Exception { mockForPublish("orphan"); @@ -1308,7 +1307,7 @@ public void testPublishOrphan() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Insufficient access rights for publisher: foo"))); } - + @Test public void testPublishRequireLicenseNone() throws Exception { var previousRequireLicense = extensions.requireLicense; @@ -1325,7 +1324,7 @@ public void testPublishRequireLicenseNone() throws Exception { extensions.requireLicense = previousRequireLicense; } } - + @Test public void testPublishRequireLicenseOk() throws Exception { var previousRequireLicense = extensions.requireLicense; @@ -1351,7 +1350,7 @@ public void testPublishRequireLicenseOk() throws Exception { extensions.requireLicense = previousRequireLicense; } } - + @Test public void testPublishInactiveToken() throws Exception { mockForPublish("invalid"); @@ -1362,7 +1361,7 @@ public void testPublishInactiveToken() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Invalid access token."))); } - + @Test public void testPublishUnknownNamespace() throws Exception { mockAccessToken(); @@ -1374,7 +1373,7 @@ public void testPublishUnknownNamespace() throws Exception { .andExpect(content().json(errorJson("Unknown publisher: foo" + "\nUse the 'create-namespace' command to create a namespace corresponding to your publisher name."))); } - + @Test public void testPublishVerifiedOwner() throws Exception { mockForPublish("owner"); @@ -1536,7 +1535,7 @@ public void testPublishInvalidVersion() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("The version string 'latest' is reserved."))); } - + @Test public void testPostReview() throws Exception { var user = mockUserData(); @@ -1559,7 +1558,7 @@ public void testPostReview() throws Exception { .andExpect(status().isCreated()) .andExpect(content().json(successJson("Added review for foo.bar"))); } - + @Test public void testPostReviewNotLoggedIn() throws Exception { mockMvc.perform(post("/api/{namespace}/{extension}/review", "foo", "bar") @@ -1569,7 +1568,7 @@ public void testPostReviewNotLoggedIn() throws Exception { })).with(csrf().asHeader())) .andExpect(status().isForbidden()); } - + @Test public void testPostReviewInvalidRating() throws Exception { mockMvc.perform(post("/api/{namespace}/{extension}/review", "foo", "bar") @@ -1582,7 +1581,7 @@ public void testPostReviewInvalidRating() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("The rating must be an integer number between 0 and 5."))); } - + @Test public void testPostReviewUnknownExtension() throws Exception { mockUserData(); @@ -1596,7 +1595,7 @@ public void testPostReviewUnknownExtension() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Extension not found: foo.bar"))); } - + @Test public void testPostExistingReview() throws Exception { var user = mockUserData(); @@ -1621,7 +1620,7 @@ public void testPostExistingReview() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("You must not submit more than one review for an extension."))); } - + @Test public void testDeleteReview() throws Exception { var user = mockUserData(); @@ -1644,13 +1643,13 @@ public void testDeleteReview() throws Exception { .andExpect(status().isOk()) .andExpect(content().json(successJson("Deleted review for foo.bar"))); } - + @Test public void testDeleteReviewNotLoggedIn() throws Exception { mockMvc.perform(post("/api/{namespace}/{extension}/review/delete", "foo", "bar").with(csrf())) .andExpect(status().isForbidden()); } - + @Test public void testDeleteReviewUnknownExtension() throws Exception { mockUserData(); @@ -1660,7 +1659,7 @@ public void testDeleteReviewUnknownExtension() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Extension not found: foo.bar"))); } - + @Test public void testDeleteNonExistingReview() throws Exception { var user = mockUserData(); @@ -2089,7 +2088,7 @@ private PersonalAccessToken mockAccessToken() { .thenReturn(token); return token; } - + private void mockForPublish(String mode) { var token = mockAccessToken(); if (mode.equals("invalid")) { @@ -2263,11 +2262,10 @@ private byte[] createExtensionPackage(String name, String version, String licens archive.finish(); return bytes.toByteArray(); } - + @TestConfiguration @Import(SecurityConfig.class) static class TestConfig { - @Bean TransactionTemplate transactionTemplate() { return new MockTransactionTemplate(); @@ -2320,10 +2318,5 @@ LatestExtensionVersionCacheKeyGenerator latestExtensionVersionCacheKeyGenerator( PublishExtensionVersionHandler publishExtensionVersionHandler() { return new PublishExtensionVersionHandler(); } - - @Bean - AuthUserFactory authUserFactory() { - return new AuthUserFactory(); - } } -} +} \ No newline at end of file diff --git a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java index 9896187aa..4866df3f3 100644 --- a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java @@ -43,7 +43,9 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; import org.springframework.cache.CacheManager; +import org.springframework.context.annotation.Bean; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.GrantedAuthority; @@ -431,4 +433,13 @@ private ExtensionVersion insertExtensionVersion(String version) { return extVersion; } + + @TestConfiguration + static class CacheServiceTestConfiguration { + + @Bean + AuthUserFactory authUserFactory() { + return new AuthUserFactory(); + } + } } From 8f65be8b7120fb0dca518447adf4d49df7113ca8 Mon Sep 17 00:00:00 2001 From: Paul Marechal Date: Wed, 6 Mar 2024 11:37:13 -0500 Subject: [PATCH 7/7] finally fix test suite --- .../org/eclipse/openvsx/RegistryAPITest.java | 54 +++++++------- .../java/org/eclipse/openvsx/UserAPITest.java | 7 +- .../openvsx/adapter/VSCodeAPITest.java | 7 +- .../eclipse/openvsx/admin/AdminAPITest.java | 71 ++++++++++++++----- .../openvsx/cache/CacheServiceTest.java | 9 --- 5 files changed, 94 insertions(+), 54 deletions(-) diff --git a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java index 33d2553e7..5f79fd876 100644 --- a/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/RegistryAPITest.java @@ -27,6 +27,7 @@ import org.eclipse.openvsx.search.ExtensionSearch; import org.eclipse.openvsx.search.ISearchService; import org.eclipse.openvsx.search.SearchUtilService; +import org.eclipse.openvsx.security.AuthUserFactory; import org.eclipse.openvsx.security.OAuth2UserServices; import org.eclipse.openvsx.security.SecurityConfig; import org.eclipse.openvsx.security.TokenService; @@ -118,7 +119,7 @@ public void testPublicNamespace() throws Exception { var namespace = mockNamespace(); Mockito.when(repositories.countMemberships(namespace, NamespaceMembership.ROLE_OWNER)) .thenReturn(0L); - + mockMvc.perform(get("/api/{namespace}", "foobar")) .andExpect(status().isOk()) .andExpect(content().json(namespaceJson(n -> { @@ -1207,7 +1208,7 @@ public void testCreateNamespace() throws Exception { .andExpect(redirectedUrl("http://localhost/api/foobar")) .andExpect(content().json(successJson("Created namespace foobar"))); } - + @Test public void testCreateNamespaceNoName() throws Exception { mockAccessToken(); @@ -1217,7 +1218,7 @@ public void testCreateNamespaceNoName() throws Exception { .andExpect(status().isOk()) .andExpect(content().json(errorJson("Missing required property 'name'."))); } - + @Test public void testCreateNamespaceInvalidName() throws Exception { mockAccessToken(); @@ -1227,7 +1228,7 @@ public void testCreateNamespaceInvalidName() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Invalid namespace name: foo.bar"))); } - + @Test public void testCreateNamespaceInactiveToken() throws Exception { var token = mockAccessToken(); @@ -1238,7 +1239,7 @@ public void testCreateNamespaceInactiveToken() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Invalid access token."))); } - + @Test public void testCreateExistingNamespace() throws Exception { mockAccessToken(); @@ -1246,7 +1247,7 @@ public void testCreateExistingNamespace() throws Exception { namespace.setName("foobar"); Mockito.when(repositories.findNamespace("foobar")) .thenReturn(namespace); - + mockMvc.perform(post("/api/-/namespace/create?token={token}", "my_token") .contentType(MediaType.APPLICATION_JSON) .content(namespaceJson(n -> { n.name = "foobar"; }))) @@ -1296,7 +1297,7 @@ public void testVerifyTokenNoPermission() throws Exception { mockMvc.perform(get("/api/{namespace}/verify-pat?token={token}", "foobar", "my_token")) .andExpect(status().isBadRequest()); } - + @Test public void testPublishOrphan() throws Exception { mockForPublish("orphan"); @@ -1307,7 +1308,7 @@ public void testPublishOrphan() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Insufficient access rights for publisher: foo"))); } - + @Test public void testPublishRequireLicenseNone() throws Exception { var previousRequireLicense = extensions.requireLicense; @@ -1324,7 +1325,7 @@ public void testPublishRequireLicenseNone() throws Exception { extensions.requireLicense = previousRequireLicense; } } - + @Test public void testPublishRequireLicenseOk() throws Exception { var previousRequireLicense = extensions.requireLicense; @@ -1350,7 +1351,7 @@ public void testPublishRequireLicenseOk() throws Exception { extensions.requireLicense = previousRequireLicense; } } - + @Test public void testPublishInactiveToken() throws Exception { mockForPublish("invalid"); @@ -1361,7 +1362,7 @@ public void testPublishInactiveToken() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Invalid access token."))); } - + @Test public void testPublishUnknownNamespace() throws Exception { mockAccessToken(); @@ -1373,7 +1374,7 @@ public void testPublishUnknownNamespace() throws Exception { .andExpect(content().json(errorJson("Unknown publisher: foo" + "\nUse the 'create-namespace' command to create a namespace corresponding to your publisher name."))); } - + @Test public void testPublishVerifiedOwner() throws Exception { mockForPublish("owner"); @@ -1535,7 +1536,7 @@ public void testPublishInvalidVersion() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("The version string 'latest' is reserved."))); } - + @Test public void testPostReview() throws Exception { var user = mockUserData(); @@ -1558,7 +1559,7 @@ public void testPostReview() throws Exception { .andExpect(status().isCreated()) .andExpect(content().json(successJson("Added review for foo.bar"))); } - + @Test public void testPostReviewNotLoggedIn() throws Exception { mockMvc.perform(post("/api/{namespace}/{extension}/review", "foo", "bar") @@ -1568,7 +1569,7 @@ public void testPostReviewNotLoggedIn() throws Exception { })).with(csrf().asHeader())) .andExpect(status().isForbidden()); } - + @Test public void testPostReviewInvalidRating() throws Exception { mockMvc.perform(post("/api/{namespace}/{extension}/review", "foo", "bar") @@ -1581,7 +1582,7 @@ public void testPostReviewInvalidRating() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("The rating must be an integer number between 0 and 5."))); } - + @Test public void testPostReviewUnknownExtension() throws Exception { mockUserData(); @@ -1595,7 +1596,7 @@ public void testPostReviewUnknownExtension() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Extension not found: foo.bar"))); } - + @Test public void testPostExistingReview() throws Exception { var user = mockUserData(); @@ -1620,7 +1621,7 @@ public void testPostExistingReview() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("You must not submit more than one review for an extension."))); } - + @Test public void testDeleteReview() throws Exception { var user = mockUserData(); @@ -1643,13 +1644,13 @@ public void testDeleteReview() throws Exception { .andExpect(status().isOk()) .andExpect(content().json(successJson("Deleted review for foo.bar"))); } - + @Test public void testDeleteReviewNotLoggedIn() throws Exception { mockMvc.perform(post("/api/{namespace}/{extension}/review/delete", "foo", "bar").with(csrf())) .andExpect(status().isForbidden()); } - + @Test public void testDeleteReviewUnknownExtension() throws Exception { mockUserData(); @@ -1659,7 +1660,7 @@ public void testDeleteReviewUnknownExtension() throws Exception { .andExpect(status().isBadRequest()) .andExpect(content().json(errorJson("Extension not found: foo.bar"))); } - + @Test public void testDeleteNonExistingReview() throws Exception { var user = mockUserData(); @@ -2088,7 +2089,7 @@ private PersonalAccessToken mockAccessToken() { .thenReturn(token); return token; } - + private void mockForPublish(String mode) { var token = mockAccessToken(); if (mode.equals("invalid")) { @@ -2262,7 +2263,7 @@ private byte[] createExtensionPackage(String name, String version, String licens archive.finish(); return bytes.toByteArray(); } - + @TestConfiguration @Import(SecurityConfig.class) static class TestConfig { @@ -2318,5 +2319,10 @@ LatestExtensionVersionCacheKeyGenerator latestExtensionVersionCacheKeyGenerator( PublishExtensionVersionHandler publishExtensionVersionHandler() { return new PublishExtensionVersionHandler(); } + + @Bean + AuthUserFactory authUserFactory() { + return new AuthUserFactory(); + } } -} \ No newline at end of file +} diff --git a/server/src/test/java/org/eclipse/openvsx/UserAPITest.java b/server/src/test/java/org/eclipse/openvsx/UserAPITest.java index cd8d85ceb..42034dd45 100644 --- a/server/src/test/java/org/eclipse/openvsx/UserAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/UserAPITest.java @@ -37,6 +37,7 @@ import org.eclipse.openvsx.json.ResultJson; import org.eclipse.openvsx.json.UserJson; import org.eclipse.openvsx.repositories.RepositoryService; +import org.eclipse.openvsx.security.AuthUserFactory; import org.eclipse.openvsx.security.OAuth2UserServices; import org.eclipse.openvsx.security.SecurityConfig; import org.eclipse.openvsx.security.TokenService; @@ -575,6 +576,10 @@ VersionService versionService() { LatestExtensionVersionCacheKeyGenerator latestExtensionVersionCacheKeyGenerator() { return new LatestExtensionVersionCacheKeyGenerator(); } + + @Bean + AuthUserFactory authUserFactory() { + return new AuthUserFactory(); + } } - } \ No newline at end of file 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 a3b2f5d17..f91b589a8 100644 --- a/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/adapter/VSCodeAPITest.java @@ -42,6 +42,7 @@ import org.eclipse.openvsx.search.ExtensionSearch; import org.eclipse.openvsx.search.ISearchService; import org.eclipse.openvsx.search.SearchUtilService; +import org.eclipse.openvsx.security.AuthUserFactory; import org.eclipse.openvsx.security.OAuth2UserServices; import org.eclipse.openvsx.security.SecurityConfig; import org.eclipse.openvsx.security.TokenService; @@ -955,6 +956,10 @@ VersionService getVersionService() { LatestExtensionVersionCacheKeyGenerator latestExtensionVersionCacheKeyGenerator() { return new LatestExtensionVersionCacheKeyGenerator(); } - } + @Bean + AuthUserFactory authUserFactory() { + return new AuthUserFactory(); + } + } } 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 d0a0c8a40..7fe00bfa1 100644 --- a/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java +++ b/server/src/test/java/org/eclipse/openvsx/admin/AdminAPITest.java @@ -9,20 +9,57 @@ ********************************************************************************/ package org.eclipse.openvsx.admin; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.micrometer.core.instrument.simple.SimpleMeterRegistry; -import org.eclipse.openvsx.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyCollection; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import org.eclipse.openvsx.ExtensionService; +import org.eclipse.openvsx.ExtensionValidator; +import org.eclipse.openvsx.LocalRegistryService; +import org.eclipse.openvsx.MockTransactionTemplate; +import org.eclipse.openvsx.UpstreamRegistryService; +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.eclipse.EclipseService; -import org.eclipse.openvsx.entities.*; -import org.eclipse.openvsx.json.*; +import org.eclipse.openvsx.entities.AdminStatistics; +import org.eclipse.openvsx.entities.Extension; +import org.eclipse.openvsx.entities.ExtensionVersion; +import org.eclipse.openvsx.entities.Namespace; +import org.eclipse.openvsx.entities.NamespaceMembership; +import org.eclipse.openvsx.entities.PersonalAccessToken; +import org.eclipse.openvsx.entities.UserData; +import org.eclipse.openvsx.json.AdminStatisticsJson; +import org.eclipse.openvsx.json.ChangeNamespaceJson; +import org.eclipse.openvsx.json.ExtensionJson; +import org.eclipse.openvsx.json.NamespaceJson; +import org.eclipse.openvsx.json.NamespaceMembershipJson; +import org.eclipse.openvsx.json.NamespaceMembershipListJson; +import org.eclipse.openvsx.json.ResultJson; +import org.eclipse.openvsx.json.UserJson; +import org.eclipse.openvsx.json.UserPublishInfoJson; import org.eclipse.openvsx.publish.ExtensionVersionIntegrityService; import org.eclipse.openvsx.publish.PublishExtensionVersionHandler; import org.eclipse.openvsx.repositories.RepositoryService; import org.eclipse.openvsx.search.SearchUtilService; +import org.eclipse.openvsx.security.AuthUserFactory; import org.eclipse.openvsx.security.OAuth2UserServices; import org.eclipse.openvsx.security.SecurityConfig; import org.eclipse.openvsx.security.TokenService; @@ -51,20 +88,11 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.transaction.support.TransactionTemplate; -import jakarta.persistence.EntityManager; -import java.time.LocalDateTime; -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Collectors; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyCollection; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import jakarta.persistence.EntityManager; @WebMvcTest(AdminAPI.class) @AutoConfigureWebClient @@ -1243,5 +1271,10 @@ VersionService versionService() { LatestExtensionVersionCacheKeyGenerator latestExtensionVersionCacheKeyGenerator() { return new LatestExtensionVersionCacheKeyGenerator(); } + + @Bean + AuthUserFactory authUserFactory() { + return new AuthUserFactory(); + } } } \ No newline at end of file diff --git a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java index 4866df3f3..b51a8b9ed 100644 --- a/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java +++ b/server/src/test/java/org/eclipse/openvsx/cache/CacheServiceTest.java @@ -433,13 +433,4 @@ private ExtensionVersion insertExtensionVersion(String version) { return extVersion; } - - @TestConfiguration - static class CacheServiceTestConfiguration { - - @Bean - AuthUserFactory authUserFactory() { - return new AuthUserFactory(); - } - } }