diff --git a/CHANGELOG.md b/CHANGELOG.md index 97644147c9..e94d216088 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased +### Fixed +- Null Pointer Exception on anonymous migration with deleted repositories ([#1371](https://github.com/scm-manager/scm-manager/pull/1371)) + ## [2.7.0] - 2020-10-12 ### Added - Users can create API keys with limited permissions ([#1359](https://github.com/scm-manager/scm-manager/pull/1359)) diff --git a/scm-ui/ui-scripts/src/createPluginConfig.js b/scm-ui/ui-scripts/src/createPluginConfig.js index f82c6f55eb..61ab0e1cf8 100644 --- a/scm-ui/ui-scripts/src/createPluginConfig.js +++ b/scm-ui/ui-scripts/src/createPluginConfig.js @@ -29,7 +29,7 @@ const root = process.cwd(); const packageJsonPath = path.join(root, "package.json"); const packageJSON = JSON.parse(fs.readFileSync(packageJsonPath, { encoding: "UTF-8" })); -let name = packageJSON.name; +let { name } = packageJSON; const orgaIndex = name.indexOf("/"); if (orgaIndex > 0) { name = name.substring(orgaIndex + 1); @@ -62,7 +62,8 @@ module.exports = function(mode) { "classnames", "query-string", "redux", - "react-redux" + "react-redux", + /^@scm-manager\/scm-.*-plugin$/i ], module: { rules: [ @@ -90,9 +91,9 @@ module.exports = function(mode) { extensions: [".ts", ".tsx", ".js", ".jsx", ".css", ".scss", ".json"] }, output: { - path: path.join(root, "target", name + "-" + packageJSON.version, "webapp", "assets"), + path: path.join(root, "target", `${name}-${packageJSON.version}`, "webapp", "assets"), filename: "[name].bundle.js", - chunkFilename: name + ".[name].chunk.js", + chunkFilename: `${name}.[name].chunk.js`, library: name, libraryTarget: "amd" } diff --git a/scm-ui/ui-webapp/src/containers/loadBundle.ts b/scm-ui/ui-webapp/src/containers/loadBundle.ts index 8096e70ec4..fcb82d8f01 100644 --- a/scm-ui/ui-webapp/src/containers/loadBundle.ts +++ b/scm-ui/ui-webapp/src/containers/loadBundle.ts @@ -74,6 +74,29 @@ SystemJS.config({ } }); +// We have to patch the resolve methods of SystemJS +// in order to resolve the correct bundle url for plugins + +const resolveModuleUrl = (key: string) => { + if (key.startsWith("@scm-manager/scm-") && key.endsWith("-plugin")) { + const pluginName = key.replace("@scm-manager/", ""); + return urls.withContextPath(`/assets/${pluginName}.bundle.js`); + } + return key; +}; + +const defaultResolve = SystemJS.resolve; +SystemJS.resolve = function(key, parentName) { + const module = resolveModuleUrl(key); + return defaultResolve.apply(this, [module, parentName]); +}; + +const defaultResolveSync = SystemJS.resolveSync; +SystemJS.resolveSync = function(key, parentName) { + const module = resolveModuleUrl(key); + return defaultResolveSync.apply(this, [module, parentName]); +}; + const expose = (name: string, cmp: any, defaultCmp?: any) => { let mod = cmp; if (defaultCmp) { diff --git a/scm-webapp/src/main/java/sonia/scm/update/repository/PublicFlagUpdateStep.java b/scm-webapp/src/main/java/sonia/scm/update/repository/PublicFlagUpdateStep.java index 310ce25c5c..e80fca770d 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/repository/PublicFlagUpdateStep.java +++ b/scm-webapp/src/main/java/sonia/scm/update/repository/PublicFlagUpdateStep.java @@ -21,7 +21,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ - + package sonia.scm.update.repository; import org.slf4j.Logger; @@ -89,9 +89,13 @@ public class PublicFlagUpdateStep implements UpdateStep { .filter(V1Repository::isPublic) .forEach(v1Repository -> { Repository v2Repository = repositoryDAO.get(v1Repository.getId()); - LOG.info(String.format("Add RepositoryRole 'READ' to _anonymous user for repository: %s - %s/%s", v2Repository.getId(), v2Repository.getNamespace(), v2Repository.getName())); - v2Repository.addPermission(new RepositoryPermission(v2AnonymousUser.getId(), "READ", false)); - repositoryDAO.modify(v2Repository); + if (v2Repository != null) { + LOG.info("Add RepositoryRole 'READ' to _anonymous user for repository: {} - {}/{}", v2Repository.getId(), v2Repository.getNamespace(), v2Repository.getName()); + v2Repository.addPermission(new RepositoryPermission(v2AnonymousUser.getId(), "READ", false)); + repositoryDAO.modify(v2Repository); + } else { + LOG.info("Repository no longer found for id {}; could not set permission for former anonymous mode", v1Repository.getId()); + } }); } diff --git a/scm-webapp/src/test/java/sonia/scm/update/repository/PublicFlagUpdateStepTest.java b/scm-webapp/src/test/java/sonia/scm/update/repository/PublicFlagUpdateStepTest.java index 54c5460c6e..23eda6d38e 100644 --- a/scm-webapp/src/test/java/sonia/scm/update/repository/PublicFlagUpdateStepTest.java +++ b/scm-webapp/src/test/java/sonia/scm/update/repository/PublicFlagUpdateStepTest.java @@ -25,6 +25,7 @@ package sonia.scm.update.repository; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; @@ -77,63 +78,81 @@ class PublicFlagUpdateStepTest { //prepare backup xml V1RepositoryFileSystem.createV1Home(tempDir); Files.move(tempDir.resolve("config").resolve("repositories.xml"), tempDir.resolve("config").resolve("repositories.xml.v1.backup")); - when(repositoryDAO.get((String) any())).thenReturn(REPOSITORY); } @Test - void shouldDeleteOldAnonymousUserIfExists() throws JAXBException { - User anonymous = new User("anonymous"); - when(userDAO.getAll()).thenReturn(Collections.singleton(anonymous)); - doReturn(anonymous).when(userDAO).get("anonymous"); - doReturn(SCMContext.ANONYMOUS).when(userDAO).get(SCMContext.USER_ANONYMOUS); - - updateStep.doUpdate(); - - verify(userDAO).delete(anonymous); - } - - @Test - void shouldNotTryToDeleteOldAnonymousUserIfNotExists() throws JAXBException { - when(userDAO.getAll()).thenReturn(Collections.emptyList()); - doReturn(SCMContext.ANONYMOUS).when(userDAO).get(SCMContext.USER_ANONYMOUS); - - updateStep.doUpdate(); - - verify(userDAO, never()).delete(any()); - } - - @Test - void shouldCreateNewAnonymousUserIfNotExists() throws JAXBException { - doReturn(SCMContext.ANONYMOUS).when(userDAO).get(SCMContext.USER_ANONYMOUS); - when(userDAO.getAll()).thenReturn(Collections.singleton(new User("trillian"))); - - updateStep.doUpdate(); - - verify(userDAO).add(SCMContext.ANONYMOUS); - } - - @Test - void shouldNotCreateNewAnonymousUserIfAlreadyExists() throws JAXBException { - doReturn(SCMContext.ANONYMOUS).when(userDAO).get(SCMContext.USER_ANONYMOUS); - when(userDAO.getAll()).thenReturn(Collections.singleton(new User("_anonymous"))); - - updateStep.doUpdate(); - - verify(userDAO, never()).add(SCMContext.ANONYMOUS); - } - - @Test - void shouldMigratePublicFlagToAnonymousRepositoryPermission() throws JAXBException { + void shouldNotFailForDeletedRepository() throws JAXBException { when(userDAO.getAll()).thenReturn(Collections.emptyList()); when(userDAO.get("_anonymous")).thenReturn(SCMContext.ANONYMOUS); updateStep.doUpdate(); - verify(repositoryDAO, times(2)).modify(repositoryCaptor.capture()); + verify(repositoryDAO, never()).modify(any()); + } - RepositoryPermission migratedRepositoryPermission = repositoryCaptor.getValue().getPermissions().iterator().next(); - assertThat(migratedRepositoryPermission.getName()).isEqualTo(SCMContext.USER_ANONYMOUS); - assertThat(migratedRepositoryPermission.getRole()).isEqualTo("READ"); - assertThat(migratedRepositoryPermission.isGroupPermission()).isFalse(); + @Nested + class WithExistingRepository { + + @BeforeEach + void mockRepository() { + when(repositoryDAO.get((String) any())).thenReturn(REPOSITORY); + } + + @Test + void shouldDeleteOldAnonymousUserIfExists() throws JAXBException { + User anonymous = new User("anonymous"); + when(userDAO.getAll()).thenReturn(Collections.singleton(anonymous)); + doReturn(anonymous).when(userDAO).get("anonymous"); + doReturn(SCMContext.ANONYMOUS).when(userDAO).get(SCMContext.USER_ANONYMOUS); + + updateStep.doUpdate(); + + verify(userDAO).delete(anonymous); + } + + @Test + void shouldNotTryToDeleteOldAnonymousUserIfNotExists() throws JAXBException { + when(userDAO.getAll()).thenReturn(Collections.emptyList()); + doReturn(SCMContext.ANONYMOUS).when(userDAO).get(SCMContext.USER_ANONYMOUS); + + updateStep.doUpdate(); + + verify(userDAO, never()).delete(any()); + } + + @Test + void shouldCreateNewAnonymousUserIfNotExists() throws JAXBException { + doReturn(SCMContext.ANONYMOUS).when(userDAO).get(SCMContext.USER_ANONYMOUS); + when(userDAO.getAll()).thenReturn(Collections.singleton(new User("trillian"))); + + updateStep.doUpdate(); + + verify(userDAO).add(SCMContext.ANONYMOUS); + } + + @Test + void shouldNotCreateNewAnonymousUserIfAlreadyExists() throws JAXBException { + doReturn(SCMContext.ANONYMOUS).when(userDAO).get(SCMContext.USER_ANONYMOUS); + when(userDAO.getAll()).thenReturn(Collections.singleton(new User("_anonymous"))); + + updateStep.doUpdate(); + + verify(userDAO, never()).add(SCMContext.ANONYMOUS); + } + + @Test + void shouldMigratePublicFlagToAnonymousRepositoryPermission() throws JAXBException { + when(userDAO.getAll()).thenReturn(Collections.emptyList()); + when(userDAO.get("_anonymous")).thenReturn(SCMContext.ANONYMOUS); + + updateStep.doUpdate(); + + verify(repositoryDAO, times(2)).modify(repositoryCaptor.capture()); + + RepositoryPermission migratedRepositoryPermission = repositoryCaptor.getValue().getPermissions().iterator().next(); + assertThat(migratedRepositoryPermission.getName()).isEqualTo(SCMContext.USER_ANONYMOUS); + assertThat(migratedRepositoryPermission.getRole()).isEqualTo("READ"); + assertThat(migratedRepositoryPermission.isGroupPermission()).isFalse(); + } } }