From 04f7a3497c474145d716bebecb0391b41cf5c55e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Fri, 2 Jul 2021 12:22:31 +0200 Subject: [PATCH] Specify admin username at startup (#1722) In addition to the admin password, the admin user name can be (optionally) specified, too. --- docs/en/first-startup/index.md | 3 +- gradle/changelog/create_initial_user.yaml | 2 +- .../lifecycle/AdminAccountStartupAction.java | 17 +++++++---- .../AdminAccountStartupActionTest.java | 28 +++++++++++++------ 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/docs/en/first-startup/index.md b/docs/en/first-startup/index.md index a8851a472a..549cb4699a 100644 --- a/docs/en/first-startup/index.md +++ b/docs/en/first-startup/index.md @@ -32,4 +32,5 @@ The password of the administration user cannot be recovered. For automated processes, you might want to bypass the initial user creation. To do so, you can set the initial password in a system property `scm.initialPassword`. If this is present, a user `scmadmin` with this password will be created, -if it does not already exist. +if it does not already exist. To change the name of this user, you can set this with the property `scm.initialUser` +in addition. diff --git a/gradle/changelog/create_initial_user.yaml b/gradle/changelog/create_initial_user.yaml index bf39802daf..f36f62651f 100644 --- a/gradle/changelog/create_initial_user.yaml +++ b/gradle/changelog/create_initial_user.yaml @@ -1,2 +1,2 @@ - type: changed - description: Initial admin user has to be created on first startup ([#1707](https://github.com/scm-manager/scm-manager/pull/1707)) + description: Initial admin user has to be created on first startup ([#1707](https://github.com/scm-manager/scm-manager/pull/1707), [#1722](https://github.com/scm-manager/scm-manager/pull/1722)) diff --git a/scm-webapp/src/main/java/sonia/scm/lifecycle/AdminAccountStartupAction.java b/scm-webapp/src/main/java/sonia/scm/lifecycle/AdminAccountStartupAction.java index 451b89d908..8446c352b1 100644 --- a/scm-webapp/src/main/java/sonia/scm/lifecycle/AdminAccountStartupAction.java +++ b/scm-webapp/src/main/java/sonia/scm/lifecycle/AdminAccountStartupAction.java @@ -49,6 +49,7 @@ public class AdminAccountStartupAction implements InitializationStep { private static final Logger LOG = LoggerFactory.getLogger(AdminAccountStartupAction.class); private static final String INITIAL_PASSWORD_PROPERTY = "scm.initialPassword"; + private static final String INITIAL_USER_PROPERTY = "scm.initialUser"; private final PasswordService passwordService; private final UserManager userManager; @@ -77,16 +78,19 @@ public class AdminAccountStartupAction implements InitializationStep { }); } + @SuppressWarnings({"java:S2639", "java:S2629"}) // Yes, we use '.' as a regex here + // No, we do not need conditional execution for 'replaceAll' here private boolean adminUserCreatedWithGivenPassword() { String startupTokenByProperty = System.getProperty(INITIAL_PASSWORD_PROPERTY); if (startupTokenByProperty != null) { + String adminUserName = System.getProperty(INITIAL_USER_PROPERTY, "scmadmin"); context.runAsAdmin((PrivilegedStartupAction) () -> - createAdminUser("scmadmin", "SCM Administrator", "scm-admin@scm-manager.org", startupTokenByProperty)); - LOG.info("================================================="); - LOG.info("== =="); - LOG.info("== Created user 'scmadmin' with given password =="); - LOG.info("== =="); - LOG.info("================================================="); + createAdminUser(adminUserName, "SCM Administrator", "scm-admin@scm-manager.org", startupTokenByProperty)); + LOG.info("================={}========================", adminUserName.replaceAll(".", "=")); + LOG.info("== {} ==", adminUserName.replaceAll(".", " ")); + LOG.info("== Created user '{}' with given password ==", adminUserName); + LOG.info("== {} ==", adminUserName.replaceAll(".", " ")); + LOG.info("================={}========================", adminUserName.replaceAll(".", "=")); return true; } else { return false; @@ -121,6 +125,7 @@ public class AdminAccountStartupAction implements InitializationStep { }); } + @SuppressWarnings("java:S1192") // With duplication the log message is far better readable in the code private void createStartupToken() { initialToken = randomPasswordGenerator.createRandomPassword(); LOG.warn("===================================================="); diff --git a/scm-webapp/src/test/java/sonia/scm/lifecycle/AdminAccountStartupActionTest.java b/scm-webapp/src/test/java/sonia/scm/lifecycle/AdminAccountStartupActionTest.java index 567c63df20..5b8232439f 100644 --- a/scm-webapp/src/test/java/sonia/scm/lifecycle/AdminAccountStartupActionTest.java +++ b/scm-webapp/src/test/java/sonia/scm/lifecycle/AdminAccountStartupActionTest.java @@ -99,6 +99,7 @@ class AdminAccountStartupActionTest { @BeforeEach void initPasswordGenerator() { System.setProperty("scm.initialPassword", "password"); + System.clearProperty("scm.initialUser"); lenient().when(passwordService.encryptPassword("password")).thenReturn("encrypted"); } @@ -106,8 +107,19 @@ class AdminAccountStartupActionTest { void shouldCreateAdminAccountIfNoUserExistsAndAssignPermissions() { createStartupAction(); - verifyAdminCreated(); - verifyAdminPermissionsAssigned(); + verifyAdminCreated("scmadmin"); + verifyAdminPermissionsAssigned("scmadmin"); + assertThat(startupAction.done()).isTrue(); + } + + @Test + void shouldUseSpecifiedAdminUsername() { + System.setProperty("scm.initialUser", "arthur"); + + createStartupAction(); + + verifyAdminCreated("arthur"); + verifyAdminPermissionsAssigned("arthur"); assertThat(startupAction.done()).isTrue(); } @@ -118,8 +130,8 @@ class AdminAccountStartupActionTest { createStartupAction(); - verifyAdminCreated(); - verifyAdminPermissionsAssigned(); + verifyAdminCreated("scmadmin"); + verifyAdminPermissionsAssigned("scmadmin"); assertThat(startupAction.done()).isTrue(); } @@ -177,19 +189,19 @@ class AdminAccountStartupActionTest { startupAction = new AdminAccountStartupAction(passwordService, userManager, permissionAssigner, randomPasswordGenerator, context); } - private void verifyAdminPermissionsAssigned() { + private void verifyAdminPermissionsAssigned(String expectedUsername) { ArgumentCaptor usernameCaptor = ArgumentCaptor.forClass(String.class); ArgumentCaptor> permissionCaptor = ArgumentCaptor.forClass(Collection.class); verify(permissionAssigner).setPermissionsForUser(usernameCaptor.capture(), permissionCaptor.capture()); String username = usernameCaptor.getValue(); - assertThat(username).isEqualTo("scmadmin"); + assertThat(username).isEqualTo(expectedUsername); PermissionDescriptor descriptor = permissionCaptor.getValue().iterator().next(); assertThat(descriptor.getValue()).isEqualTo("*"); } - private void verifyAdminCreated() { + private void verifyAdminCreated(String expectedUsername) { User user = userCaptor.getValue(); - assertThat(user.getName()).isEqualTo("scmadmin"); + assertThat(user.getName()).isEqualTo(expectedUsername); assertThat(user.getPassword()).isEqualTo("encrypted"); } }