From 76305265443e63354da9cbba8ca890ddd779ee96 Mon Sep 17 00:00:00 2001 From: Marc Sladek Date: Sat, 15 Aug 2026 01:02:01 +0200 Subject: [PATCH] CELDEV-1348 Auto Login --- .../celements/auth/AuthenticationService.java | 5 +- .../com/celements/auth/MainAdminConfig.java | 64 ++++++++++++++ .../com/celements/auth/SecureRandomUtils.java | 16 ++++ .../auth/user/CelementsUserService.java | 4 +- .../EnsureConsistentUserStateListener.java | 4 +- .../celements/mandatory/MainAdminUser.java | 41 ++++----- .../web/token/TokenLDAPAuthServiceImpl.java | 35 +++++++- .../celements/auth/MainAdminConfigTest.java | 84 +++++++++++++++++++ .../celements/auth/SecureRandomUtilsTest.java | 19 +++++ .../mandatory/MainAdminUserTest.java | 58 ++++++++++--- .../token/TokenLDAPAuthServiceImplTest.java | 60 ++++++++++++- 11 files changed, 341 insertions(+), 49 deletions(-) create mode 100644 celements-user-manager/component/src/main/java/com/celements/auth/MainAdminConfig.java create mode 100644 celements-user-manager/component/src/main/java/com/celements/auth/SecureRandomUtils.java create mode 100644 celements-user-manager/component/src/test/java/com/celements/auth/MainAdminConfigTest.java create mode 100644 celements-user-manager/component/src/test/java/com/celements/auth/SecureRandomUtilsTest.java diff --git a/celements-user-manager/component/src/main/java/com/celements/auth/AuthenticationService.java b/celements-user-manager/component/src/main/java/com/celements/auth/AuthenticationService.java index 34836810..7794979e 100644 --- a/celements-user-manager/component/src/main/java/com/celements/auth/AuthenticationService.java +++ b/celements-user-manager/component/src/main/java/com/celements/auth/AuthenticationService.java @@ -8,7 +8,6 @@ import java.util.Optional; import java.util.Set; -import org.apache.commons.lang.RandomStringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xwiki.component.annotation.Component; @@ -85,7 +84,7 @@ public Map activateAccount(String activationCode) .filter(not(User::isSuspended)); LOGGER.debug("activateAccount: user = {}", user); if (user.isPresent()) { - String password = RandomStringUtils.randomAlphanumeric(24); + String password = SecureRandomUtils.randomAlphanumeric(24); enableUser(user.get(), password, true); Map userAccount = new HashMap<>(); userAccount.put("username", modelUtils.serializeRefLocal(user.get().getDocRef())); @@ -148,7 +147,7 @@ public String getUniqueValidationKey() throws QueryException { Query.XWQL).execute()); String validkey; do { - validkey = RandomStringUtils.randomAlphanumeric(24); + validkey = SecureRandomUtils.randomAlphanumeric(24); } while (existingKeys.contains(validkey)); return validkey; } diff --git a/celements-user-manager/component/src/main/java/com/celements/auth/MainAdminConfig.java b/celements-user-manager/component/src/main/java/com/celements/auth/MainAdminConfig.java new file mode 100644 index 00000000..8c2c3707 --- /dev/null +++ b/celements-user-manager/component/src/main/java/com/celements/auth/MainAdminConfig.java @@ -0,0 +1,64 @@ +package com.celements.auth; + +import static com.google.common.base.Preconditions.*; +import static com.xpn.xwiki.XWikiConstant.*; +import static java.util.function.Predicate.*; + +import java.util.Optional; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.springframework.context.annotation.Configuration; +import org.xwiki.configuration.ConfigurationSource; +import org.xwiki.model.reference.DocumentReference; + +import com.celements.model.util.ModelUtils; +import com.celements.servlet.NodeConfig.NodeIdentity; +import com.xpn.xwiki.user.api.XWikiUser; + +@Configuration +public class MainAdminConfig { + + static final String CFG_KEY_AUTOLOGIN = "main.admin.autologin"; + static final String CFG_KEY_PASSWORD = "main.admin.password"; + + public static final DocumentReference USER_DOC_REF = + new DocumentReference(MAIN_WIKI.getName(), XWIKI_SPACE, "Admin"); + + private final ConfigurationSource configSource; + private final NodeIdentity nodeIdentity; + private final ModelUtils modelUtils; + + @Inject + public MainAdminConfig( + @Named("xwikiproperties") ConfigurationSource configSource, + NodeIdentity nodeIdentity, + ModelUtils modelUtils) { + this.configSource = configSource; + this.nodeIdentity = nodeIdentity; + this.modelUtils = modelUtils; + isAutoLoginEnabled(); + } + + public DocumentReference getUserDocRef() { + return USER_DOC_REF; + } + + public XWikiUser getXWikiUser() { + return new XWikiUser(modelUtils.serializeRef(USER_DOC_REF), true); + } + + public Optional getPassword() { + return Optional.ofNullable(configSource.getProperty(CFG_KEY_PASSWORD, "")) + .map(String::trim) + .filter(not(String::isEmpty)); + } + + public boolean isAutoLoginEnabled() { + var enabled = configSource.getProperty(CFG_KEY_AUTOLOGIN, false); + checkState(!enabled || "local".equals(nodeIdentity.clusterName()), + "Admin auto-login requires CLUSTER_NAME=local"); + return enabled; + } +} diff --git a/celements-user-manager/component/src/main/java/com/celements/auth/SecureRandomUtils.java b/celements-user-manager/component/src/main/java/com/celements/auth/SecureRandomUtils.java new file mode 100644 index 00000000..14dc3ffa --- /dev/null +++ b/celements-user-manager/component/src/main/java/com/celements/auth/SecureRandomUtils.java @@ -0,0 +1,16 @@ +package com.celements.auth; + +import java.security.SecureRandom; + +import org.apache.commons.lang.RandomStringUtils; + +public final class SecureRandomUtils { + + private static final SecureRandom RANDOM = new SecureRandom(); + + private SecureRandomUtils() {} + + public static String randomAlphanumeric(int length) { + return RandomStringUtils.random(length, 0, 0, true, true, null, RANDOM); + } +} diff --git a/celements-user-manager/component/src/main/java/com/celements/auth/user/CelementsUserService.java b/celements-user-manager/component/src/main/java/com/celements/auth/user/CelementsUserService.java index e7a21178..f9b0f604 100644 --- a/celements-user-manager/component/src/main/java/com/celements/auth/user/CelementsUserService.java +++ b/celements-user-manager/component/src/main/java/com/celements/auth/user/CelementsUserService.java @@ -24,7 +24,6 @@ import javax.inject.Named; import javax.validation.constraints.NotNull; -import org.apache.commons.lang.RandomStringUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,6 +36,7 @@ import org.xwiki.query.QueryException; import org.xwiki.query.QueryManager; +import com.celements.auth.SecureRandomUtils; import com.celements.init.XWikiProvider; import com.celements.marshalling.ReferenceMarshaller; import com.celements.model.access.IModelAccessFacade; @@ -217,7 +217,7 @@ void fillInUserData(XWikiDocument userDoc, Map userData) throws DocumentAccessException { userData.putIfAbsent(XWikiUsersClass.FIELD_ACTIVE.getName(), "0"); userData.putIfAbsent(XWikiUsersClass.FIELD_PASSWORD.getName(), - RandomStringUtils.randomAlphanumeric(24)); + SecureRandomUtils.randomAlphanumeric(24)); try { BaseObject userObject = XWikiObjectEditor.on(userDoc).filter(usersClass).createFirst(); xwiki.get() diff --git a/celements-user-manager/component/src/main/java/com/celements/auth/user/listener/EnsureConsistentUserStateListener.java b/celements-user-manager/component/src/main/java/com/celements/auth/user/listener/EnsureConsistentUserStateListener.java index 35986ea7..11641a67 100644 --- a/celements-user-manager/component/src/main/java/com/celements/auth/user/listener/EnsureConsistentUserStateListener.java +++ b/celements-user-manager/component/src/main/java/com/celements/auth/user/listener/EnsureConsistentUserStateListener.java @@ -6,12 +6,12 @@ import javax.inject.Inject; import javax.validation.constraints.NotNull; -import org.apache.commons.lang.RandomStringUtils; import org.springframework.stereotype.Component; import org.xwiki.model.reference.DocumentReference; import org.xwiki.model.reference.EntityReference; import org.xwiki.observation.event.Event; +import com.celements.auth.SecureRandomUtils; import com.celements.auth.user.UserPageType; import com.celements.common.observation.listener.AbstractLocalEventListener; import com.celements.model.object.xwiki.XWikiObjectEditor; @@ -93,7 +93,7 @@ void setDefaultValuesOnNewUser(XWikiDocument userDoc) { .filter(XWikiUsersClass.CLASS_REF) .filterAbsent(XWikiUsersClass.FIELD_PASSWORD) .editField(XWikiUsersClass.FIELD_PASSWORD) - .first(RandomStringUtils.randomAlphanumeric(24)); + .first(SecureRandomUtils.randomAlphanumeric(24)); } private XWikiUser asXWikiUser(DocumentReference userDocRef) { diff --git a/celements-user-manager/component/src/main/java/com/celements/mandatory/MainAdminUser.java b/celements-user-manager/component/src/main/java/com/celements/mandatory/MainAdminUser.java index 2ad03600..d453b584 100644 --- a/celements-user-manager/component/src/main/java/com/celements/mandatory/MainAdminUser.java +++ b/celements-user-manager/component/src/main/java/com/celements/mandatory/MainAdminUser.java @@ -1,10 +1,8 @@ package com.celements.mandatory; -import static com.celements.common.lambda.LambdaExceptionUtil.*; -import static java.util.function.Predicate.*; +import static com.xpn.xwiki.XWikiConstant.*; import java.util.List; -import java.util.Optional; import javax.inject.Inject; @@ -15,20 +13,17 @@ import org.xwiki.model.reference.DocumentReference; import com.celements.auth.AuthenticationService; +import com.celements.auth.MainAdminConfig; +import com.celements.auth.SecureRandomUtils; import com.celements.auth.user.UserInstantiationException; import com.celements.auth.user.UserService; import com.celements.model.access.exception.DocumentSaveException; -import com.celements.model.reference.RefBuilder; -import com.xpn.xwiki.XWikiConstant; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; @Component("celements.mandatory.MainAdminUser") public class MainAdminUser extends AbstractMandatoryDocument { - public static final String ADMIN_DOC_NAME = "Admin"; - public static final String CFG_KEY_ADMIN_PASSWORD = "main.admin.password"; - private static final Logger LOGGER = LoggerFactory.getLogger(MainAdminUser.class); @Inject @@ -37,6 +32,9 @@ public class MainAdminUser extends AbstractMandatoryDocument { @Inject private AuthenticationService authService; + @Inject + private MainAdminConfig config; + @Override public String getName() { return "MainAdminUser"; @@ -51,12 +49,12 @@ public List dependsOnMandatoryDocuments() { @Override protected DocumentReference getDocRef() { - return getAdminUserDocRef(); + return config.getUserDocRef(); } @Override protected boolean isEnabledByDefault() { - return getAdminPassword().isPresent(); + return config.isAutoLoginEnabled() || config.getPassword().isPresent(); } @Override @@ -72,9 +70,11 @@ protected boolean checkDocuments(XWikiDocument doc) throws XWikiException { @Override protected boolean checkDocumentsMain(XWikiDocument doc) throws XWikiException { try { - var adminUser = userService.getUser(getAdminUserDocRef()); - getAdminPassword().ifPresent(rethrowConsumer(password -> authService - .enableUser(adminUser, password, false))); + var adminUser = userService.getUser(config.getUserDocRef()); + if (isEnabledByDefault()) { + var password = config.getPassword().orElseGet(() -> SecureRandomUtils.randomAlphanumeric(24)); + authService.enableUser(adminUser, password, false); + } userService.addUserToGroup(adminUser, getAdminGroupRef()); return false; // safe already handled } catch (UserInstantiationException | DocumentSaveException exc) { @@ -82,21 +82,8 @@ protected boolean checkDocumentsMain(XWikiDocument doc) throws XWikiException { } } - Optional getAdminPassword() { - String password = xwikiPropConfigSource.getProperty(CFG_KEY_ADMIN_PASSWORD, ""); - return Optional.ofNullable(password).map(String::trim).filter(not(String::isEmpty)); - } - - private DocumentReference getAdminUserDocRef() { - return new RefBuilder() - .with(modelContext.getWikiRef()) - .space(XWikiConstant.XWIKI_SPACE) - .doc(ADMIN_DOC_NAME) - .build(DocumentReference.class); - } - private ClassReference getAdminGroupRef() { - return new ClassReference(XWikiConstant.XWIKI_SPACE, "XWikiAdminGroup"); + return new ClassReference(XWIKI_SPACE, "XWikiAdminGroup"); } @Override diff --git a/celements-user-manager/component/src/main/java/com/celements/web/token/TokenLDAPAuthServiceImpl.java b/celements-user-manager/component/src/main/java/com/celements/web/token/TokenLDAPAuthServiceImpl.java index f0a791b8..99208814 100644 --- a/celements-user-manager/component/src/main/java/com/celements/web/token/TokenLDAPAuthServiceImpl.java +++ b/celements-user-manager/component/src/main/java/com/celements/web/token/TokenLDAPAuthServiceImpl.java @@ -21,21 +21,27 @@ import static com.celements.common.lambda.LambdaExceptionUtil.*; import static com.celements.logging.LogUtils.*; +import static com.celements.spring.context.SpringContextProvider.*; import static com.google.common.base.Strings.*; import static com.google.common.collect.ImmutableList.*; import static com.xpn.xwiki.user.api.XWikiRightService.*; import static java.util.Arrays.*; +import java.security.Principal; import java.util.Date; import java.util.List; import java.util.Optional; +import java.util.function.Supplier; +import org.securityfilter.realm.SimplePrincipal; import org.xwiki.model.reference.DocumentReference; +import com.celements.auth.MainAdminConfig; import com.celements.auth.user.User; import com.celements.auth.user.UserInstantiationException; import com.celements.auth.user.UserService; import com.celements.model.util.ModelUtils; +import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.xpn.xwiki.XWikiContext; import com.xpn.xwiki.XWikiException; @@ -47,6 +53,9 @@ public class TokenLDAPAuthServiceImpl extends XWikiLDAPAuthServiceImpl { + private final Supplier mainAdminCfg = Suppliers.memoize( + () -> getSpringContext().getBean(MainAdminConfig.class)); + @Override public XWikiUser checkAuth(XWikiContext context) throws XWikiException { if ((context.getResponse() != null) && !"".equals(context.getWiki().Param("celements.auth.P3P", @@ -54,12 +63,34 @@ public XWikiUser checkAuth(XWikiContext context) throws XWikiException { context.getResponse().addHeader("P3P", "CP=\"" + context.getWiki().Param("celements.auth.P3P") + "\""); } - return Optional.ofNullable(checkAuthByToken(context) - .orElseGet(rethrowSupplier(() -> super.checkAuth(context)))) + return autoLogin() + .or(rethrowSupplier(() -> checkAuthByToken(context))) + .or(rethrowSupplier(() -> Optional.ofNullable(super.checkAuth(context)))) .filter(this::isNotSuspended) .orElse(null); } + @Override + public XWikiUser checkAuth(String username, String password, String rememberme, + XWikiContext context) throws XWikiException { + return autoLogin().orElseGet(rethrowSupplier(() -> super + .checkAuth(username, password, rememberme, context))); + } + + @Override + public Principal authenticate(String username, String password, XWikiContext context) + throws XWikiException { + return autoLogin() + .map(x -> new SimplePrincipal(x.getUser())) + .orElseGet(rethrowSupplier(() -> super.authenticate(username, password, context))); + } + + private Optional autoLogin() { + return mainAdminCfg.get().isAutoLoginEnabled() + ? Optional.of(mainAdminCfg.get().getXWikiUser()) + : Optional.empty(); + } + private boolean isNotSuspended(XWikiUser xUser) { try { if (xUser.getUser().equals(SUPERADMIN_USER_FULLNAME) diff --git a/celements-user-manager/component/src/test/java/com/celements/auth/MainAdminConfigTest.java b/celements-user-manager/component/src/test/java/com/celements/auth/MainAdminConfigTest.java new file mode 100644 index 00000000..33b09b28 --- /dev/null +++ b/celements-user-manager/component/src/test/java/com/celements/auth/MainAdminConfigTest.java @@ -0,0 +1,84 @@ +package com.celements.auth; + +import static com.celements.auth.MainAdminConfig.*; +import static org.easymock.EasyMock.*; +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.BeanCreationException; + +import com.celements.common.test.AbstractComponentTest; +import com.celements.model.util.ModelUtils; +import com.celements.servlet.NodeConfig.NodeIdentity; +import com.xpn.xwiki.user.api.XWikiUser; + +public class MainAdminConfigTest extends AbstractComponentTest { + + @Before + public void prepareTest() throws Exception { + registerComponentMocks(NodeIdentity.class, ModelUtils.class); + } + + @Test + public void test_adminUserIdentity() { + expect(getMock(ModelUtils.class).serializeRef(same(USER_DOC_REF))) + .andReturn("xwiki:XWiki.Admin"); + + replayDefault(); + MainAdminConfig config = getBeanFactory().getBean(MainAdminConfig.class); + XWikiUser user = config.getXWikiUser(); + verifyDefault(); + + assertSame(USER_DOC_REF, config.getUserDocRef()); + assertEquals("xwiki:XWiki.Admin", user.getUser()); + assertTrue(user.isMain()); + } + + @Test + public void test_getPassword_trimmed() { + getConfigurationSource().setProperty(CFG_KEY_PASSWORD, " password "); + + replayDefault(); + MainAdminConfig config = getBeanFactory().getBean(MainAdminConfig.class); + verifyDefault(); + + assertEquals("password", config.getPassword().orElseThrow()); + } + + @Test + public void test_getPassword_blank() { + getConfigurationSource().setProperty(CFG_KEY_PASSWORD, " "); + + replayDefault(); + MainAdminConfig config = getBeanFactory().getBean(MainAdminConfig.class); + verifyDefault(); + + assertTrue(config.getPassword().isEmpty()); + } + + @Test + public void test_isAutoLoginEnabled_localCluster() { + getConfigurationSource().setProperty(CFG_KEY_AUTOLOGIN, true); + expect(getMock(NodeIdentity.class).clusterName()).andReturn("local").times(2); + + replayDefault(); + MainAdminConfig config = getBeanFactory().getBean(MainAdminConfig.class); + assertTrue(config.isAutoLoginEnabled()); + verifyDefault(); + } + + @Test + public void test_constructor_autoLoginRejectsNonLocalCluster() { + getConfigurationSource().setProperty(CFG_KEY_AUTOLOGIN, true); + expect(getMock(NodeIdentity.class).clusterName()).andReturn("production"); + + replayDefault(); + BeanCreationException exc = assertThrows(BeanCreationException.class, + () -> getBeanFactory().getBean(MainAdminConfig.class)); + verifyDefault(); + + assertEquals("Admin auto-login requires CLUSTER_NAME=local", + exc.getMostSpecificCause().getMessage()); + } +} diff --git a/celements-user-manager/component/src/test/java/com/celements/auth/SecureRandomUtilsTest.java b/celements-user-manager/component/src/test/java/com/celements/auth/SecureRandomUtilsTest.java new file mode 100644 index 00000000..c4eed8a8 --- /dev/null +++ b/celements-user-manager/component/src/test/java/com/celements/auth/SecureRandomUtilsTest.java @@ -0,0 +1,19 @@ +package com.celements.auth; + +import static org.junit.Assert.*; + +import org.junit.Test; + +public class SecureRandomUtilsTest { + + @Test + public void test_randomAlphanumeric() { + assertEquals("", SecureRandomUtils.randomAlphanumeric(0)); + assertTrue(SecureRandomUtils.randomAlphanumeric(24).matches("[A-Za-z0-9]{24}")); + } + + @Test(expected = IllegalArgumentException.class) + public void test_randomAlphanumeric_negativeLength() { + SecureRandomUtils.randomAlphanumeric(-1); + } +} diff --git a/celements-user-manager/component/src/test/java/com/celements/mandatory/MainAdminUserTest.java b/celements-user-manager/component/src/test/java/com/celements/mandatory/MainAdminUserTest.java index a6afa394..6a658b60 100644 --- a/celements-user-manager/component/src/test/java/com/celements/mandatory/MainAdminUserTest.java +++ b/celements-user-manager/component/src/test/java/com/celements/mandatory/MainAdminUserTest.java @@ -1,14 +1,18 @@ package com.celements.mandatory; import static com.celements.common.test.CelementsTestUtils.*; +import static com.xpn.xwiki.XWikiConstant.*; import static org.easymock.EasyMock.*; import static org.junit.Assert.*; +import java.util.Optional; + import org.junit.Before; import org.junit.Test; import org.xwiki.model.reference.ClassReference; import org.xwiki.model.reference.DocumentReference; +import com.celements.auth.MainAdminConfig; import com.celements.auth.user.User; import com.celements.auth.user.UserInstantiationException; import com.celements.auth.user.UserService; @@ -19,7 +23,6 @@ import com.celements.model.object.xwiki.XWikiObjectFetcher; import com.celements.model.reference.RefBuilder; import com.celements.web.classes.oldcore.XWikiUsersClass; -import com.xpn.xwiki.XWikiConstant; import com.xpn.xwiki.XWikiException; import com.xpn.xwiki.doc.XWikiDocument; import com.xpn.xwiki.objects.BaseObject; @@ -37,6 +40,10 @@ public class MainAdminUserTest extends AbstractComponentTest { public void prepareTest() throws Exception { registerComponentMock(IModelAccessFacade.class); registerComponentMock(UserService.class); + registerComponentMock(MainAdminConfig.class); + expect(getMock(MainAdminConfig.class).isAutoLoginEnabled()).andStubReturn(false); + expect(getMock(MainAdminConfig.class).getPassword()).andStubReturn(Optional.empty()); + expect(getMock(MainAdminConfig.class).getUserDocRef()).andStubReturn(getAdminUserRef()); mandatory = getBeanFactory().getBean("celements.mandatory.MainAdminUser", MainAdminUser.class); } @@ -62,7 +69,8 @@ public void testIsEnabled_default() throws Exception { @Test public void testIsEnabled_configured() throws Exception { getXContext().setDatabase("xwiki"); - setAdminPassword(ADMIN_PASSWORD); + expect(getMock(MainAdminConfig.class).getPassword()) + .andReturn(Optional.of(ADMIN_PASSWORD)); replayDefault(); assertTrue(mandatory.isEnabled()); @@ -70,12 +78,12 @@ public void testIsEnabled_configured() throws Exception { } @Test - public void testIsEnabled_configured_blank() throws Exception { + public void testIsEnabled_autoLogin() throws Exception { getXContext().setDatabase("xwiki"); - setAdminPassword(" "); + expect(getMock(MainAdminConfig.class).isAutoLoginEnabled()).andReturn(true); replayDefault(); - assertFalse(mandatory.isEnabled()); + assertTrue(mandatory.isEnabled()); verifyDefault(); } @@ -110,7 +118,8 @@ public void testSkip_localWiki() throws Exception { @Test public void testCheckDocumentsMain_activatesAdminAndAddsToGroup() throws Exception { getXContext().setDatabase("xwiki"); - setAdminPassword(ADMIN_PASSWORD); + expect(getMock(MainAdminConfig.class).getPassword()) + .andReturn(Optional.of(ADMIN_PASSWORD)).times(2); DocumentReference adminUserRef = getAdminUserRef(); XWikiDocument adminUserDoc = newAdminUserDoc(false); User adminUser = createDefaultMock(User.class); @@ -158,18 +167,43 @@ public void testCheckDocumentsMain_withoutPassword_addsToGroupOnly() throws Exce verifyDefault(); } - private ClassReference getAdminGroupRef() { - return new ClassReference(XWikiConstant.XWIKI_SPACE, "XWikiAdminGroup"); + @Test + public void testCheckDocumentsMain_autoLogin_activatesWithRandomPassword() throws Exception { + getXContext().setDatabase("xwiki"); + expect(getMock(MainAdminConfig.class).isAutoLoginEnabled()).andReturn(true); + DocumentReference adminUserRef = getAdminUserRef(); + XWikiDocument adminUserDoc = newAdminUserDoc(false); + adminUserDoc.getXObject(XWikiUsersClass.CLASS_REF.getDocRef( + adminUserRef.getWikiReference())).setStringValue( + XWikiUsersClass.FIELD_PASSWORD.getName(), "stored-hash"); + User adminUser = createDefaultMock(User.class); + expect(getMock(UserService.class).getUser(adminUserRef)).andReturn(adminUser); + expect(adminUser.getDocument()).andReturn(adminUserDoc); + getMock(IModelAccessFacade.class).saveDocument(adminUserDoc, "activate account"); + expectXWikiUsersClass(); + expect(getMock(UserService.class).addUserToGroup(adminUser, getAdminGroupRef())) + .andReturn(true); + + replayDefault(); + assertFalse(mandatory.checkDocumentsMain(new XWikiDocument(adminUserRef))); + verifyDefault(); + + assertEquals(1, getAdminUserObj(adminUserDoc).getIntValue( + XWikiUsersClass.FIELD_ACTIVE.getName())); + String passwordHash = getAdminUserObj(adminUserDoc).getStringValue( + XWikiUsersClass.FIELD_PASSWORD.getName()); + assertFalse(passwordHash.isEmpty()); + assertNotEquals("stored-hash", passwordHash); } - private void setAdminPassword(String password) { - getConfigurationSource().setProperty(MainAdminUser.CFG_KEY_ADMIN_PASSWORD, password); + private ClassReference getAdminGroupRef() { + return new ClassReference(XWIKI_SPACE, "XWikiAdminGroup"); } private DocumentReference getAdminUserRef() { return new RefBuilder().wiki("xwiki") - .space(XWikiConstant.XWIKI_SPACE) - .doc(MainAdminUser.ADMIN_DOC_NAME) + .space(XWIKI_SPACE) + .doc("Admin") .build(DocumentReference.class); } diff --git a/celements-user-manager/component/src/test/java/com/celements/web/token/TokenLDAPAuthServiceImplTest.java b/celements-user-manager/component/src/test/java/com/celements/web/token/TokenLDAPAuthServiceImplTest.java index ce02824e..bb22a501 100644 --- a/celements-user-manager/component/src/test/java/com/celements/web/token/TokenLDAPAuthServiceImplTest.java +++ b/celements-user-manager/component/src/test/java/com/celements/web/token/TokenLDAPAuthServiceImplTest.java @@ -24,6 +24,7 @@ import static org.easymock.EasyMock.*; import static org.junit.Assert.*; +import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -34,6 +35,7 @@ import org.junit.Test; import org.xwiki.model.reference.DocumentReference; +import com.celements.auth.MainAdminConfig; import com.celements.common.test.AbstractComponentTest; import com.celements.model.access.IModelAccessFacade; import com.celements.model.classes.ClassDefinition; @@ -48,12 +50,19 @@ public class TokenLDAPAuthServiceImplTest extends AbstractComponentTest { + private static final String ADMIN_USER_FULL_NAME = "xwiki:XWiki.Admin"; + private TokenLDAPAuthServiceImpl tokenAuthImpl; private XWikiStoreInterface store; + private MainAdminConfig mainAdminConfig; @Before public void prepare() throws Exception { - registerComponentMocks(IModelAccessFacade.class); + registerComponentMocks(IModelAccessFacade.class, MainAdminConfig.class); + mainAdminConfig = getMock(MainAdminConfig.class); + expect(mainAdminConfig.isAutoLoginEnabled()).andStubReturn(false); + expect(mainAdminConfig.getXWikiUser()) + .andStubReturn(new XWikiUser(ADMIN_USER_FULL_NAME, true)); tokenAuthImpl = new TokenLDAPAuthServiceImpl(); store = getStoreMock(); expect(getMock(XWiki.class).isVirtualMode()).andReturn(true).anyTimes(); @@ -168,6 +177,55 @@ public void test_checkAuthXWikiContext_noRequest() throws Exception { verifyDefault(); } + @Test + public void test_checkAuthXWikiContext_autoLoginAdmin() throws Exception { + expect(mainAdminConfig.isAutoLoginEnabled()).andReturn(true); + expectUserDocument(ADMIN_USER_FULL_NAME); + + replayDefault(); + XWikiUser user = tokenAuthImpl.checkAuth(getXContext()); + verifyDefault(); + + assertEquals(ADMIN_USER_FULL_NAME, user.getUser()); + assertTrue(user.isMain()); + } + + @Test + public void test_checkAuthWithCredentials_autoLoginAdmin_withoutCredentials() throws Exception { + expect(mainAdminConfig.isAutoLoginEnabled()).andReturn(true); + + replayDefault(); + XWikiUser user = tokenAuthImpl.checkAuth(null, null, null, getXContext()); + verifyDefault(); + + assertEquals(ADMIN_USER_FULL_NAME, user.getUser()); + assertTrue(user.isMain()); + } + + @Test + public void test_authenticate_autoLoginAdmin_withoutCredentials() throws Exception { + expect(mainAdminConfig.isAutoLoginEnabled()).andReturn(true); + + replayDefault(); + Principal principal = tokenAuthImpl.authenticate(null, null, getXContext()); + verifyDefault(); + + assertEquals(ADMIN_USER_FULL_NAME, principal.getName()); + } + + private void expectUserDocument(String fullName) throws Exception { + DocumentReference userDocRef = getModelUtils().resolveRef(fullName, DocumentReference.class); + BaseObject userObj = new BaseObject(); + userObj.setDocumentReference(userDocRef); + userObj.setXClassReference(getBeanFactory() + .getBean(XWikiUsersClass.CLASS_DEF_HINT, ClassDefinition.class).getClassReference()); + userObj.setIntValue(XWikiUsersClass.FIELD_SUSPENDED.getName(), 0); + XWikiDocument userDoc = new XWikiDocument(userDocRef); + userDoc.setNew(false); + userDoc.addXObject(userObj); + expect(getMock(IModelAccessFacade.class).getDocument(eq(userDocRef))).andReturn(userDoc); + } + @Test public void test_checkAuthXWikiContext() throws Exception { String userToken = "123456789012345678901234";