diff --git a/scm-core/src/main/resources/sonia/scm/config/admin-account.xml b/scm-core/src/main/resources/sonia/scm/config/admin-account.xml deleted file mode 100644 index 980545d8bd..0000000000 --- a/scm-core/src/main/resources/sonia/scm/config/admin-account.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - scmadmin - SCM Administrator - scm-admin@scm-manager.org - $shiro1$SHA-512$8192$$yrNahBVDa4Gz+y5gat4msdjyvjtHlVE+N5nTl4WIDhtBFwhSIib13mKJt1sWmVqgHDWi3VwX7fkdkJ2+WToTbw== - true - xml - diff --git a/scm-core/src/main/resources/sonia/scm/config/anonymous-account.xml b/scm-core/src/main/resources/sonia/scm/config/anonymous-account.xml deleted file mode 100644 index 7a6a058e5b..0000000000 --- a/scm-core/src/main/resources/sonia/scm/config/anonymous-account.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - anonymous - SCM Anonymous - scm-anonymous@scm-manager.org - false - xml - diff --git a/scm-webapp/src/main/java/sonia/scm/boot/SetupContextListener.java b/scm-webapp/src/main/java/sonia/scm/boot/SetupContextListener.java new file mode 100644 index 0000000000..427e9f5dd7 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/boot/SetupContextListener.java @@ -0,0 +1,71 @@ +package sonia.scm.boot; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.shiro.authc.credential.PasswordService; +import sonia.scm.plugin.Extension; +import sonia.scm.security.PermissionAssigner; +import sonia.scm.security.PermissionDescriptor; +import sonia.scm.user.User; +import sonia.scm.user.UserManager; +import sonia.scm.web.security.AdministrationContext; +import sonia.scm.web.security.PrivilegedAction; + +import javax.inject.Inject; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import java.util.Collections; + +@Extension +public class SetupContextListener implements ServletContextListener { + + private final AdministrationContext administrationContext; + + @Inject + public SetupContextListener(AdministrationContext administrationContext) { + this.administrationContext = administrationContext; + } + + @Override + public void contextInitialized(ServletContextEvent sce) { + administrationContext.runAsAdmin(SetupAction.class); + } + + @Override + public void contextDestroyed(ServletContextEvent sce) {} + + @VisibleForTesting + static class SetupAction implements PrivilegedAction { + + private final UserManager userManager; + private final PasswordService passwordService; + private final PermissionAssigner permissionAssigner; + + @Inject + public SetupAction(UserManager userManager, PasswordService passwordService, PermissionAssigner permissionAssigner) { + this.userManager = userManager; + this.passwordService = passwordService; + this.permissionAssigner = permissionAssigner; + } + + @Override + public void run() { + if (isFirstStart()) { + createAdminAccount(); + } + } + + private boolean isFirstStart() { + return userManager.getAll().isEmpty(); + } + + private void createAdminAccount() { + User scmadmin = new User("scmadmin", "SCM Administrator", "scm-admin@scm-manager.org"); + String password = passwordService.encryptPassword("scmadmin"); + scmadmin.setPassword(password); + userManager.create(scmadmin); + + PermissionDescriptor descriptor = new PermissionDescriptor("*"); + permissionAssigner.setPermissionsForUser("scmadmin", Collections.singleton(descriptor)); + } + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java index 709987fb1b..3b78267631 100644 --- a/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java +++ b/scm-webapp/src/main/java/sonia/scm/user/DefaultUserManager.java @@ -71,13 +71,6 @@ import java.util.List; public class DefaultUserManager extends AbstractUserManager { - /** Field description */ - public static final String ADMIN_PATH = "/sonia/scm/config/admin-account.xml"; - - /** Field description */ - public static final String ANONYMOUS_PATH = - "/sonia/scm/config/anonymous-account.xml"; - /** Field description */ public static final String STORE_NAME = "users"; @@ -173,12 +166,6 @@ public class DefaultUserManager extends AbstractUserManager @Override public void init(SCMContextProvider context) { - - // create default account only, if no other account is available - if (userDAO.getAll().isEmpty()) - { - createDefaultAccounts(); - } } /** @@ -457,28 +444,6 @@ public class DefaultUserManager extends AbstractUserManager } } - /** - * Method description - * - */ - private void createDefaultAccounts() - { - try - { - logger.info("create default accounts"); - - JAXBContext context = JAXBContext.newInstance(User.class); - Unmarshaller unmarshaller = context.createUnmarshaller(); - - createDefaultAccount(unmarshaller, ADMIN_PATH); - createDefaultAccount(unmarshaller, ANONYMOUS_PATH); - } - catch (JAXBException ex) - { - logger.error("could not create default accounts", ex); - } - } - //~--- fields --------------------------------------------------------------- private final UserDAO userDAO; diff --git a/scm-webapp/src/test/java/sonia/scm/boot/SetupContextListenerTest.java b/scm-webapp/src/test/java/sonia/scm/boot/SetupContextListenerTest.java new file mode 100644 index 0000000000..421dca5cb7 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/boot/SetupContextListenerTest.java @@ -0,0 +1,94 @@ +package sonia.scm.boot; + +import com.google.common.collect.Lists; +import org.apache.shiro.authc.credential.PasswordService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import sonia.scm.security.PermissionAssigner; +import sonia.scm.security.PermissionDescriptor; +import sonia.scm.user.User; +import sonia.scm.user.UserManager; +import sonia.scm.user.UserTestData; +import sonia.scm.web.security.AdministrationContext; + +import java.util.Collection; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class SetupContextListenerTest { + + @Mock + private AdministrationContext administrationContext; + + @InjectMocks + private SetupContextListener setupContextListener; + + @Mock + private UserManager userManager; + + @Mock + private PasswordService passwordService; + + @Mock + private PermissionAssigner permissionAssigner; + + @InjectMocks + private SetupContextListener.SetupAction setupAction; + + @BeforeEach + void setupObjectUnderTest() { + doAnswer(ic -> { + setupAction.run(); + return null; + }).when(administrationContext).runAsAdmin(SetupContextListener.SetupAction.class); + } + + @Test + void shouldCreateAdminAccountAndAssignPermissions() { + when(passwordService.encryptPassword("scmadmin")).thenReturn("secret"); + + setupContextListener.contextInitialized(null); + + verifyAdminCreated(); + verifyAdminPermissionsAssigned(); + } + + @Test + void shouldDoNothingOnSecondStart() { + List users = Lists.newArrayList(UserTestData.createTrillian()); + when(userManager.getAll()).thenReturn(users); + + setupContextListener.contextInitialized(null); + + verify(userManager, never()).create(any(User.class)); + verify(permissionAssigner, never()).setPermissionsForUser(anyString(), any(Collection.class)); + } + + private void verifyAdminPermissionsAssigned() { + 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"); + PermissionDescriptor descriptor = permissionCaptor.getValue().iterator().next(); + assertThat(descriptor.getValue()).isEqualTo("*"); + } + + private void verifyAdminCreated() { + ArgumentCaptor userCaptor = ArgumentCaptor.forClass(User.class); + verify(userManager).create(userCaptor.capture()); + User user = userCaptor.getValue(); + assertThat(user.getName()).isEqualTo("scmadmin"); + assertThat(user.getPassword()).isEqualTo("secret"); + } + +} diff --git a/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java b/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java index ab31d751fd..fe6b41724e 100644 --- a/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java +++ b/scm-webapp/src/test/java/sonia/scm/user/DefaultUserManagerTest.java @@ -67,7 +67,7 @@ import org.junit.Rule; ) public class DefaultUserManagerTest extends UserManagerTestBase { - + @Rule public ShiroRule shiro = new ShiroRule(); @@ -97,39 +97,6 @@ public class DefaultUserManagerTest extends UserManagerTestBase when(userDAO.get("trillian")).thenReturn(trillian); } - /** - * Method description - * - */ - @Test - public void testDefaultAccountAfterFristStart() - { - List users = Lists.newArrayList(new User("tuser")); - - when(userDAO.getAll()).thenReturn(users); - - UserManager userManager = new DefaultUserManager(userDAO); - - userManager.init(contextProvider); - verify(userDAO, never()).add(any(User.class)); - } - - /** - * Method description - * - */ - @Test - @SuppressWarnings("unchecked") - public void testDefaultAccountCreation() - { - when(userDAO.getAll()).thenReturn(Collections.EMPTY_LIST); - - UserManager userManager = new DefaultUserManager(userDAO); - - userManager.init(contextProvider); - verify(userDAO, times(2)).add(any(User.class)); - } - @Test(expected = InvalidPasswordException.class) public void shouldFailChangePasswordForWrongOldPassword() { UserManager userManager = new DefaultUserManager(userDAO);