From 4afed7cb74731a1663aa714575cc3f297602ce22 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 16 Jul 2019 16:26:34 +0200 Subject: [PATCH 1/4] migrate securityV1 permissions --- .../scm/security/AssignedPermission.java | 2 +- .../security/XmlSecurityV1UpdateStep.java | 67 ++++++++++++++++++- .../scm/update/user/XmlUserV1UpdateStep.java | 19 +++--- 3 files changed, 76 insertions(+), 12 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/security/AssignedPermission.java b/scm-core/src/main/java/sonia/scm/security/AssignedPermission.java index c98d81f8ba..cc7ff87534 100644 --- a/scm-core/src/main/java/sonia/scm/security/AssignedPermission.java +++ b/scm-core/src/main/java/sonia/scm/security/AssignedPermission.java @@ -162,7 +162,7 @@ public class AssignedPermission implements PermissionObject, Serializable //J- return MoreObjects.toStringHelper(this) .add("name", name) - .add("groupPermisison", groupPermission) + .add("groupPermission", groupPermission) .add("permission", permission) .toString(); //J+ diff --git a/scm-webapp/src/main/java/sonia/scm/update/security/XmlSecurityV1UpdateStep.java b/scm-webapp/src/main/java/sonia/scm/update/security/XmlSecurityV1UpdateStep.java index f62b81b5df..fba3e6a929 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/security/XmlSecurityV1UpdateStep.java +++ b/scm-webapp/src/main/java/sonia/scm/update/security/XmlSecurityV1UpdateStep.java @@ -21,6 +21,7 @@ import javax.xml.bind.annotation.XmlRootElement; import java.io.File; import java.nio.file.Path; import java.util.Arrays; +import java.util.List; import java.util.function.Consumer; import static java.util.Optional.ofNullable; @@ -46,6 +47,44 @@ public class XmlSecurityV1UpdateStep implements UpdateStep { forAllAdmins(user -> createSecurityEntry(user, false, securityStore), group -> createSecurityEntry(group, true, securityStore)); + + mapV1Permissions(securityStore); + } + + private void mapV1Permissions(ConfigurationEntryStore securityStore) throws JAXBException { + Path v1SecurityFile = determineConfigDirectory().resolve("securityV1" + StoreConstants.FILE_EXTENSION); + + if (!v1SecurityFile.toFile().exists()) { + LOG.info("no v1 file for security found"); + return; + } + + JAXBContext jaxbContext = JAXBContext.newInstance(XmlSecurityV1UpdateStep.V1Security.class); + V1Security v1Security = (V1Security) jaxbContext.createUnmarshaller().unmarshal(v1SecurityFile.toFile()); + + v1Security.entries.forEach(assignedPermission -> { + + String newPermission = ""; + if (assignedPermission.value.permission != null && !assignedPermission.value.permission.isEmpty()) { + String[] splitPermission = assignedPermission.value.permission.split(":"); + switch(splitPermission[2]) { + case "OWNER": + newPermission = "repository:*"; + break; + case "WRITE": + newPermission = "repository:read,pull,push:*"; + break; + case "READ": + newPermission = "repository:read,pull:*"; + } + } + + securityStore.put(new AssignedPermission( + assignedPermission.value.name, + Boolean.parseBoolean(assignedPermission.value.groupPermission), + newPermission + )); + }); } private void forAllAdmins(Consumer userConsumer, Consumer groupConsumer) throws JAXBException { @@ -70,10 +109,9 @@ public class XmlSecurityV1UpdateStep implements UpdateStep { Arrays.stream(entries.split(",")).forEach(consumer); } - @Override public Version getTargetVersion() { - return parse("2.0.0"); + return parse("2.0.1"); } @Override @@ -102,4 +140,29 @@ public class XmlSecurityV1UpdateStep implements UpdateStep { @XmlElement(name = "admin-groups") private String adminGroups; } + + @XmlAccessorType(XmlAccessType.FIELD) + @XmlRootElement(name = "configuration") + private static class V1Security { + @XmlElement(name = "entry") + private List entries; + } + + @XmlAccessorType(XmlAccessType.FIELD) + private static class Entry { + @XmlElement(name = "key") + private String key; + @XmlElement(name = "value") + private Value value; + } + + @XmlAccessorType(XmlAccessType.FIELD) + private static class Value { + @XmlElement(name = "permission") + String permission; + @XmlElement(name = "name") + String name; + @XmlElement(name = "group-permission") + String groupPermission; + } } diff --git a/scm-webapp/src/main/java/sonia/scm/update/user/XmlUserV1UpdateStep.java b/scm-webapp/src/main/java/sonia/scm/update/user/XmlUserV1UpdateStep.java index b2da69fd9b..f2561ac53e 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/user/XmlUserV1UpdateStep.java +++ b/scm-webapp/src/main/java/sonia/scm/update/user/XmlUserV1UpdateStep.java @@ -57,7 +57,8 @@ public class XmlUserV1UpdateStep implements UpdateStep { @Override public void doUpdate() throws JAXBException { - Optional v1UsersFile = determineV1File(); + Optional v1UsersFile = determineV1File("users"); + determineV1File("security"); if (!v1UsersFile.isPresent()) { LOG.info("no v1 file for users found"); return; @@ -107,17 +108,17 @@ public class XmlUserV1UpdateStep implements UpdateStep { return configurationEntryStoreFactory.withType(AssignedPermission.class).withName("security").build(); } - private Optional determineV1File() { - Path existingUsersFile = resolveConfigFile("users"); - Path usersV1File = resolveConfigFile("usersV1"); - if (existingUsersFile.toFile().exists()) { + private Optional determineV1File(String filename) { + Path existingFile = resolveConfigFile(filename); + Path v1File = resolveConfigFile(filename + "V1"); + if (existingFile.toFile().exists()) { try { - Files.move(existingUsersFile, usersV1File); + Files.move(existingFile, v1File); } catch (IOException e) { - throw new UpdateException("could not move old users file to " + usersV1File.toAbsolutePath()); + throw new UpdateException("could not move old " + filename + " file to " + v1File.toAbsolutePath()); } - LOG.info("moved old users file to {}", usersV1File.toAbsolutePath()); - return of(usersV1File); + LOG.info("moved old " + filename + " file to {}", v1File.toAbsolutePath()); + return of(v1File); } return empty(); } From 0840109d56ea4f4b00f42a4d8302e0ea3c713225 Mon Sep 17 00:00:00 2001 From: Eduard Heimbuch Date: Tue, 16 Jul 2019 16:27:50 +0200 Subject: [PATCH 2/4] add securityV1Migration-Test --- .../security/XmlSecurityV1UpdateStepTest.java | 20 +++++++++++++++++++ .../sonia/scm/update/security/securityV1.xml | 19 ++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 scm-webapp/src/test/resources/sonia/scm/update/security/securityV1.xml diff --git a/scm-webapp/src/test/java/sonia/scm/update/security/XmlSecurityV1UpdateStepTest.java b/scm-webapp/src/test/java/sonia/scm/update/security/XmlSecurityV1UpdateStepTest.java index d0115e3426..76914d3bfa 100644 --- a/scm-webapp/src/test/java/sonia/scm/update/security/XmlSecurityV1UpdateStepTest.java +++ b/scm-webapp/src/test/java/sonia/scm/update/security/XmlSecurityV1UpdateStepTest.java @@ -56,6 +56,13 @@ class XmlSecurityV1UpdateStepTest { copyTestDatabaseFile(configDir, "config.xml"); } + @BeforeEach + void createSecurityV1XML(@TempDirectory.TempDir Path tempDir) throws IOException { + Path configDir = tempDir.resolve("config"); + Files.createDirectories(configDir); + copyTestDatabaseFile(configDir, "securityV1.xml"); + } + @Test void shouldCreatePermissionForUsersConfiguredAsAdmin() throws JAXBException { updateStep.doUpdate(); @@ -81,6 +88,19 @@ class XmlSecurityV1UpdateStepTest { .collect(toList()); assertThat(assignedPermission).contains("admins", "vogons"); } + + @Test + void shouldMapV1PermissionsFromSecurityV1XML() throws JAXBException { + updateStep.doUpdate(); + List assignedPermission = + assignedPermissionStore.getAll().values() + .stream() + .filter(a -> a.getPermission().getValue().contains("repository:")) + .map(AssignedPermission::getName) + .collect(toList()); + assertThat(assignedPermission).contains("scmadmin"); + assertThat(assignedPermission).contains("test"); + } } private void copyTestDatabaseFile(Path configDir, String fileName) throws IOException { diff --git a/scm-webapp/src/test/resources/sonia/scm/update/security/securityV1.xml b/scm-webapp/src/test/resources/sonia/scm/update/security/securityV1.xml new file mode 100644 index 0000000000..2f23062f42 --- /dev/null +++ b/scm-webapp/src/test/resources/sonia/scm/update/security/securityV1.xml @@ -0,0 +1,19 @@ + + + + 4lRWOA7DH1 + + false + scmadmin + repository:*:READ + + + + CfRWOAANM2 + + true + test + repository:*:OWNER + + + From a4cb2f7caa6ea31192dbf3844b626d6a072678e9 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 18 Jul 2019 12:17:20 +0200 Subject: [PATCH 3/4] fix migration with unknown permissions --- .../security/XmlSecurityV1UpdateStep.java | 50 +++++++++++-------- .../security/XmlSecurityV1UpdateStepTest.java | 20 +++++--- .../sonia/scm/update/security/securityV1.xml | 48 ++++++++++++------ 3 files changed, 75 insertions(+), 43 deletions(-) diff --git a/scm-webapp/src/main/java/sonia/scm/update/security/XmlSecurityV1UpdateStep.java b/scm-webapp/src/main/java/sonia/scm/update/security/XmlSecurityV1UpdateStep.java index fba3e6a929..8dac2b5073 100644 --- a/scm-webapp/src/main/java/sonia/scm/update/security/XmlSecurityV1UpdateStep.java +++ b/scm-webapp/src/main/java/sonia/scm/update/security/XmlSecurityV1UpdateStep.java @@ -23,6 +23,8 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static java.util.Optional.ofNullable; import static sonia.scm.version.Version.parse; @@ -30,6 +32,8 @@ import static sonia.scm.version.Version.parse; @Extension public class XmlSecurityV1UpdateStep implements UpdateStep { + private static final Pattern v1PermissionPattern = Pattern.compile("^repository:\\*:(READ|WRITE|OWNER)$"); + private static final Logger LOG = LoggerFactory.getLogger(XmlSecurityV1UpdateStep.class); private final SCMContextProvider contextProvider; @@ -63,30 +67,36 @@ public class XmlSecurityV1UpdateStep implements UpdateStep { V1Security v1Security = (V1Security) jaxbContext.createUnmarshaller().unmarshal(v1SecurityFile.toFile()); v1Security.entries.forEach(assignedPermission -> { - - String newPermission = ""; - if (assignedPermission.value.permission != null && !assignedPermission.value.permission.isEmpty()) { - String[] splitPermission = assignedPermission.value.permission.split(":"); - switch(splitPermission[2]) { - case "OWNER": - newPermission = "repository:*"; - break; - case "WRITE": - newPermission = "repository:read,pull,push:*"; - break; - case "READ": - newPermission = "repository:read,pull:*"; - } + Matcher matcher = v1PermissionPattern.matcher(assignedPermission.value.permission); + if (matcher.matches()) { + String newPermission = convertRole(matcher.group(1)); + securityStore.put(new AssignedPermission( + assignedPermission.value.name, + Boolean.parseBoolean(assignedPermission.value.groupPermission), + newPermission + )); } - - securityStore.put(new AssignedPermission( - assignedPermission.value.name, - Boolean.parseBoolean(assignedPermission.value.groupPermission), - newPermission - )); }); } + private String convertRole(String role) { + String newPermission; + switch (role) { + case "OWNER": + newPermission = "repository:*"; + break; + case "WRITE": + newPermission = "repository:read,pull,push:*"; + break; + case "READ": + newPermission = "repository:read,pull:*"; + break; + default: + newPermission = ""; + } + return newPermission; + } + private void forAllAdmins(Consumer userConsumer, Consumer groupConsumer) throws JAXBException { Path configDirectory = determineConfigDirectory(); Path existingConfigFile = configDirectory.resolve("config" + StoreConstants.FILE_EXTENSION); diff --git a/scm-webapp/src/test/java/sonia/scm/update/security/XmlSecurityV1UpdateStepTest.java b/scm-webapp/src/test/java/sonia/scm/update/security/XmlSecurityV1UpdateStepTest.java index 76914d3bfa..73c7fe6aca 100644 --- a/scm-webapp/src/test/java/sonia/scm/update/security/XmlSecurityV1UpdateStepTest.java +++ b/scm-webapp/src/test/java/sonia/scm/update/security/XmlSecurityV1UpdateStepTest.java @@ -56,13 +56,6 @@ class XmlSecurityV1UpdateStepTest { copyTestDatabaseFile(configDir, "config.xml"); } - @BeforeEach - void createSecurityV1XML(@TempDirectory.TempDir Path tempDir) throws IOException { - Path configDir = tempDir.resolve("config"); - Files.createDirectories(configDir); - copyTestDatabaseFile(configDir, "securityV1.xml"); - } - @Test void shouldCreatePermissionForUsersConfiguredAsAdmin() throws JAXBException { updateStep.doUpdate(); @@ -89,6 +82,18 @@ class XmlSecurityV1UpdateStepTest { assertThat(assignedPermission).contains("admins", "vogons"); } + } + + @Nested + class WithExistingSecurityXml { + + @BeforeEach + void createSecurityV1XML(@TempDirectory.TempDir Path tempDir) throws IOException { + Path configDir = tempDir.resolve("config"); + Files.createDirectories(configDir); + copyTestDatabaseFile(configDir, "securityV1.xml"); + } + @Test void shouldMapV1PermissionsFromSecurityV1XML() throws JAXBException { updateStep.doUpdate(); @@ -101,6 +106,7 @@ class XmlSecurityV1UpdateStepTest { assertThat(assignedPermission).contains("scmadmin"); assertThat(assignedPermission).contains("test"); } + } private void copyTestDatabaseFile(Path configDir, String fileName) throws IOException { diff --git a/scm-webapp/src/test/resources/sonia/scm/update/security/securityV1.xml b/scm-webapp/src/test/resources/sonia/scm/update/security/securityV1.xml index 2f23062f42..8de82f88d9 100644 --- a/scm-webapp/src/test/resources/sonia/scm/update/security/securityV1.xml +++ b/scm-webapp/src/test/resources/sonia/scm/update/security/securityV1.xml @@ -1,19 +1,35 @@ - - 4lRWOA7DH1 - - false - scmadmin - repository:*:READ - - - - CfRWOAANM2 - - true - test - repository:*:OWNER - - + + 4lRWOA7DH1 + + false + scmadmin + repository:*:READ + + + + CfRWOAANM2 + + true + test + repository:*:OWNER + + + + CfRWOAANM2 + + true + test + invalid:permission + + + + CfRWOAANM2 + + true + test + repository:*:STRANGE + + From f9c023330d2214cdc1c38b2100f0d1b2bf147a22 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 19 Jul 2019 05:15:27 +0000 Subject: [PATCH 4/4] Close branch bugfix/migrate_security_xml