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);