diff --git a/org.eclipse.scout.rt.api/src/main/java/org/eclipse/scout/rt/api/uinotification/UiNotificationResource.java b/org.eclipse.scout.rt.api/src/main/java/org/eclipse/scout/rt/api/uinotification/UiNotificationResource.java index 9188f80fe51..ce26921c40d 100644 --- a/org.eclipse.scout.rt.api/src/main/java/org/eclipse/scout/rt/api/uinotification/UiNotificationResource.java +++ b/org.eclipse.scout.rt.api/src/main/java/org/eclipse/scout/rt/api/uinotification/UiNotificationResource.java @@ -30,8 +30,8 @@ import org.eclipse.scout.rt.api.data.uinotification.UiNotificationRequest; import org.eclipse.scout.rt.api.data.uinotification.UiNotificationResponse; import org.eclipse.scout.rt.platform.BEANS; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.rest.IRestResource; -import org.eclipse.scout.rt.security.IAccessControlService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -104,6 +104,6 @@ protected void logResponse(List notifications, List t } protected String getUserId() { - return BEANS.get(IAccessControlService.class).getUserIdOfCurrentSubject(); + return User.currentUserId(); } } diff --git a/org.eclipse.scout.rt.client.test/src/main/java/org/eclipse/scout/testing/client/ClientSessionProviderWithCache.java b/org.eclipse.scout.rt.client.test/src/main/java/org/eclipse/scout/testing/client/ClientSessionProviderWithCache.java index 3bb365fe648..5486594ea75 100644 --- a/org.eclipse.scout.rt.client.test/src/main/java/org/eclipse/scout/testing/client/ClientSessionProviderWithCache.java +++ b/org.eclipse.scout.rt.client.test/src/main/java/org/eclipse/scout/testing/client/ClientSessionProviderWithCache.java @@ -149,7 +149,7 @@ protected CompositeObject newSessionCacheKey(final String sessionId, final Subje return new CompositeObject(BEANS.get(IClientSession.class).getClass(), sessionId); } else if (subject != null) { - return new CompositeObject(BEANS.get(IClientSession.class).getClass(), BEANS.get(IAccessControlService.class).getUserId(subject)); + return new CompositeObject(BEANS.get(IClientSession.class).getClass(), BEANS.get(IAccessControlService.class).getUser(subject)); } else { return null; diff --git a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/context/ClientRunContextChainTest.java b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/context/ClientRunContextChainTest.java index 385666af745..3130ceb541a 100644 --- a/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/context/ClientRunContextChainTest.java +++ b/org.eclipse.scout.rt.client.test/src/test/java/org/eclipse/scout/rt/client/context/ClientRunContextChainTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -27,13 +27,13 @@ import org.eclipse.scout.rt.platform.nls.NlsLocale; import org.eclipse.scout.rt.platform.opentelemetry.OpenTelemetryContextProcessor; import org.eclipse.scout.rt.platform.security.SubjectProcessor; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.TransactionProcessor; import org.eclipse.scout.rt.platform.util.ThreadLocalProcessor; -import org.eclipse.scout.rt.shared.session.ISession; import org.eclipse.scout.rt.shared.opentelemetry.OpenTelemetrySpanAttributeProcessor; +import org.eclipse.scout.rt.shared.session.ISession; import org.eclipse.scout.rt.shared.session.SessionId; import org.eclipse.scout.rt.shared.ui.UserAgent; -import org.eclipse.scout.rt.shared.user.UserId; import org.eclipse.scout.rt.testing.platform.runner.PlatformTestRunner; import org.junit.Test; import org.junit.runner.RunWith; @@ -84,46 +84,46 @@ protected CallableChain createCallableChain() { // overwrite to c = chainIterator.next(); assertEquals(SubjectProcessor.class, c.getClass()); - // 7. DiagnosticContextValueProcessor + // 7. ThreadLocalProcessor for User.CURRENT + c = chainIterator.next(); + assertEquals(ThreadLocalProcessor.class, c.getClass()); + assertSame(User.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); + + // 8. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("subject.principal.name", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 8. DiagnosticContextValueProcessor + // 9. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("opentelemetry.trace.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 9. DiagnosticContextValueProcessor + // 10. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("scout.correlation.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 10. ThreadLocalProcessor for NlsLocale.CURRENT + // 11. ThreadLocalProcessor for NlsLocale.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(NlsLocale.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 11. ThreadLocalProcessor for PropertyMap.CURRENT + // 12. ThreadLocalProcessor for PropertyMap.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(PropertyMap.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 12. ThreadLocalProcessor for ISession.CURRENT + // 13. ThreadLocalProcessor for ISession.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(ISession.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 13. ThreadLocalProcessor for SessionId.CURRENT + // 14. ThreadLocalProcessor for SessionId.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(SessionId.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 14. ThreadLocalProcessor for Users.CURRENT - c = chainIterator.next(); - assertEquals(ThreadLocalProcessor.class, c.getClass()); - assertSame(UserId.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 15. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/AbstractClientSession.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/AbstractClientSession.java index 0a57fab9762..5705b776743 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/AbstractClientSession.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/AbstractClientSession.java @@ -10,7 +10,6 @@ package org.eclipse.scout.rt.client; import static java.util.Collections.*; -import static org.eclipse.scout.rt.shared.ISessionVariable.SHARED_CONTEXT_USER_ID; import java.net.URI; import java.net.URISyntaxException; @@ -44,11 +43,13 @@ import org.eclipse.scout.rt.platform.job.Jobs; import org.eclipse.scout.rt.platform.nls.NlsLocale; import org.eclipse.scout.rt.platform.reflect.AbstractPropertyObserver; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.Assertions; import org.eclipse.scout.rt.platform.util.CollectionUtility; import org.eclipse.scout.rt.platform.util.TypeCastUtility; import org.eclipse.scout.rt.platform.util.event.FastListenerList; import org.eclipse.scout.rt.platform.util.event.IFastListenerList; +import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.shared.extension.AbstractExtension; import org.eclipse.scout.rt.shared.extension.IExtensibleObject; import org.eclipse.scout.rt.shared.extension.IExtension; @@ -91,6 +92,7 @@ public abstract class AbstractClientSession extends AbstractPropertyObserver imp private IDesktop m_desktop; private VirtualDesktop m_virtualDesktop; private volatile Subject m_subject; + private volatile User m_user; private final SharedVariableMap m_sharedVariableMap; private Set m_exposedSharedVariables; @@ -107,6 +109,7 @@ public AbstractClientSession(boolean autoInitConfig) { m_stateLock = new Object(); m_userAgent = UserAgent.get(); m_subject = Subject.current(); + m_user = BEANS.get(IAccessControlService.class).getUser(m_subject); m_objectExtensions = new ObjectExtensions<>(this, true); m_sharedVariableMap = new SharedVariableMap(); m_exposedSharedVariables = null; @@ -166,7 +169,7 @@ public String getId() { */ @Override public String getUserId() { - return getSharedContextVariable(SHARED_CONTEXT_USER_ID, String.class); + return m_user != null ? m_user.getUserId() : null; } @Override @@ -501,6 +504,11 @@ public void setSubject(Subject subject) { m_subject = subject; } + @Override + public User getUser() { + return m_user; + } + @Override public void setData(String key, Object value) { m_sessionData.set(key, value); diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/IClientSession.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/IClientSession.java index cddcae8dff8..58bbf37fab9 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/IClientSession.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/IClientSession.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -20,7 +20,7 @@ import org.eclipse.scout.rt.platform.job.IExecutionSemaphore; import org.eclipse.scout.rt.platform.nls.NlsLocale; import org.eclipse.scout.rt.platform.reflect.IPropertyObserver; -import org.eclipse.scout.rt.security.IAccessControlService; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.shared.session.ISession; import org.eclipse.scout.rt.shared.ui.UserAgent; @@ -98,7 +98,14 @@ public interface IClientSession extends ISession, IPropertyObserver { void setSubject(Subject subject); /** - * Authenticated userId, extracted by {@link IAccessControlService#getUserIdOfCurrentSubject()} on server + * Consumers can query for the {@link User} of a {@link IClientSession} + *

+ * The user is set when this object is created from {@link User#current()} + */ + User getUser(); + + /** + * Authenticated userId, provided by {@link User#getUserId()}} */ String getUserId(); diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/clientnotification/ClientNotificationPoller.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/clientnotification/ClientNotificationPoller.java index 8b3222dbb76..61aecb97848 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/clientnotification/ClientNotificationPoller.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/clientnotification/ClientNotificationPoller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -13,6 +13,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; +import javax.security.auth.Subject; + import jakarta.annotation.PostConstruct; import org.eclipse.scout.rt.client.context.ClientRunContexts; @@ -36,12 +38,14 @@ import org.eclipse.scout.rt.platform.job.FixedDelayScheduleBuilder; import org.eclipse.scout.rt.platform.job.IFuture; import org.eclipse.scout.rt.platform.job.Jobs; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.Assertions; import org.eclipse.scout.rt.platform.util.SleepUtil; import org.eclipse.scout.rt.platform.util.concurrent.FutureCancelledError; import org.eclipse.scout.rt.platform.util.concurrent.IRunnable; import org.eclipse.scout.rt.platform.util.concurrent.ThreadInterruptedError; import org.eclipse.scout.rt.platform.util.date.DateUtility; +import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.shared.SharedConfigProperties.NotificationSubjectProperty; import org.eclipse.scout.rt.shared.clientnotification.ClientNotificationMessage; import org.eclipse.scout.rt.shared.clientnotification.IClientNotificationService; @@ -101,8 +105,10 @@ protected void stopPoller() { } protected RunContext createRunContext() { + final Subject subject = BEANS.get(NotificationSubjectProperty.class).getValue(); return ClientRunContexts.empty() - .withSubject(BEANS.get(NotificationSubjectProperty.class).getValue()) + .withSubject(subject) + .withUser(BEANS.get(User.class).withUserId(BEANS.get(IAccessControlService.class).extractUserId(subject)).setReadOnly()) .withUserAgent(UserAgents.createDefault()) .withSession(null, false); } diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/context/ClientRunContext.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/context/ClientRunContext.java index 56da5f5ee58..d3be6fad1ee 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/context/ClientRunContext.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/context/ClientRunContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -28,18 +28,18 @@ import org.eclipse.scout.rt.platform.context.RunMonitor; import org.eclipse.scout.rt.platform.logger.DiagnosticContextValueProcessor; import org.eclipse.scout.rt.platform.logger.DiagnosticContextValueProcessor.IDiagnosticContextValueProvider; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.platform.transaction.ITransactionMember; import org.eclipse.scout.rt.platform.transaction.TransactionScope; import org.eclipse.scout.rt.platform.util.ThreadLocalProcessor; import org.eclipse.scout.rt.platform.util.ToStringBuilder; -import org.eclipse.scout.rt.shared.session.ISession; import org.eclipse.scout.rt.shared.logging.UserIdContextValueProvider; import org.eclipse.scout.rt.shared.opentelemetry.OpenTelemetrySpanAttributeProcessor; +import org.eclipse.scout.rt.shared.session.ISession; import org.eclipse.scout.rt.shared.session.ScoutSessionIdContextValueProvider; import org.eclipse.scout.rt.shared.session.SessionId; import org.eclipse.scout.rt.shared.ui.UserAgent; -import org.eclipse.scout.rt.shared.user.UserId; /** * Use this class to propagate client-side context. @@ -65,7 +65,6 @@ protected void interceptCallableChain(final CallableChain calla callableChain .add(new ThreadLocalProcessor<>(ISession.CURRENT, m_session)) .add(new ThreadLocalProcessor<>(SessionId.CURRENT, getSession() != null ? getSession().getId() : null)) - .add(new ThreadLocalProcessor<>(UserId.CURRENT, getSession() != null ? getSession().getUserId() : null)) .add(new DiagnosticContextValueProcessor(BEANS.get(UserIdContextValueProvider.class))) .add(new DiagnosticContextValueProcessor(BEANS.get(ScoutSessionIdContextValueProvider.class))) .add(new OpenTelemetrySpanAttributeProcessor()) @@ -87,6 +86,12 @@ public ClientRunContext withSubject(final Subject subject) { return this; } + @Override + public ClientRunContext withUser(final User user) { + super.withUser(user); + return this; + } + @Override public ClientRunContext withLocale(final Locale locale) { super.withLocale(locale); @@ -187,6 +192,7 @@ public ClientRunContext withSession(final IClientSession session, final boolean m_locale = (session != null ? session.getLocale() : null); m_userAgent = (session != null ? session.getUserAgent() : null); m_subject = (session != null ? session.getSubject() : null); + m_user = (session != null ? session.getUser() : null); m_desktop = (session != null ? session.getDesktopElseVirtualDesktop() : null); } return this; diff --git a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/useradmin/DefaultPasswordForm.java b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/useradmin/DefaultPasswordForm.java index 1981c89c441..21469e7e286 100644 --- a/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/useradmin/DefaultPasswordForm.java +++ b/org.eclipse.scout.rt.client/src/main/java/org/eclipse/scout/rt/client/ui/form/useradmin/DefaultPasswordForm.java @@ -29,8 +29,8 @@ import org.eclipse.scout.rt.platform.Order; import org.eclipse.scout.rt.platform.classid.ClassId; import org.eclipse.scout.rt.platform.exception.VetoException; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.text.TEXTS; -import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.shared.services.common.pwd.IPasswordManagementService; @ClassId("5bcb48f0-9b72-4f28-9c08-038cd5d9a1c4") @@ -208,7 +208,7 @@ protected void execStore() { protected void resetSessionsIfCurrentUser(IPasswordManagementService svc) { //owasp: reset session if (isCurrentUser()) { - String currentUserId = BEANS.get(IAccessControlService.class).getUserIdOfCurrentSubject(); + String currentUserId = User.currentUserId(); IClientSession currentSession = ClientSessionProvider.currentSession(); // for security reasons force stop all other of the user's sessions in case an attacker is controlling it @@ -225,7 +225,7 @@ protected void resetSessionsIfCurrentUser(IPasswordManagementService svc) { protected boolean isCurrentUser() { String userName = BEANS.get(IPasswordManagementService.class).getUsernameFor(getUserId()); - String currentUserId = BEANS.get(IAccessControlService.class).getUserIdOfCurrentSubject(); + String currentUserId = User.currentUserId(); return currentUserId.equals(userName); } } diff --git a/org.eclipse.scout.rt.platform.test/src/main/java/org/eclipse/scout/rt/testing/platform/runner/statement/SubjectStatement.java b/org.eclipse.scout.rt.platform.test/src/main/java/org/eclipse/scout/rt/testing/platform/runner/statement/SubjectStatement.java index 4165577abb5..aaf3a51af72 100644 --- a/org.eclipse.scout.rt.platform.test/src/main/java/org/eclipse/scout/rt/testing/platform/runner/statement/SubjectStatement.java +++ b/org.eclipse.scout.rt.platform.test/src/main/java/org/eclipse/scout/rt/testing/platform/runner/statement/SubjectStatement.java @@ -11,9 +11,11 @@ import javax.security.auth.Subject; +import org.eclipse.scout.rt.platform.BEANS; import org.eclipse.scout.rt.platform.context.RunContext; import org.eclipse.scout.rt.platform.context.RunContexts; import org.eclipse.scout.rt.platform.security.SimplePrincipal; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.Assertions; import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; import org.eclipse.scout.rt.testing.platform.runner.SafeStatementInvoker; @@ -28,6 +30,7 @@ public class SubjectStatement extends Statement { private final Statement m_next; + private final String m_principal; private final Subject m_subject; /** @@ -43,11 +46,13 @@ public SubjectStatement(final Statement next, final RunWithSubject annotation) { final String principal = (annotation != null ? annotation.value() : null); if (principal != null) { + m_principal = principal; m_subject = new Subject(); m_subject.getPrincipals().add(new SimplePrincipal(principal)); m_subject.setReadOnly(); } else { + m_principal = null; m_subject = null; } } @@ -56,6 +61,10 @@ protected Statement getNext() { return m_next; } + protected String getPrincipal() { + return m_principal; + } + protected Subject getSubject() { return m_subject; } @@ -73,6 +82,12 @@ public void evaluate() throws Throwable { } protected RunContext createRunContext() { - return RunContexts.copyCurrent().withSubject(m_subject); + return RunContexts.copyCurrent() + .withSubject(m_subject) + .withUser(createUser()); + } + + protected User createUser() { + return BEANS.get(User.class).withUserId(m_principal).setReadOnly(); } } diff --git a/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/context/RunContextChainTest.java b/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/context/RunContextChainTest.java index 5eaa7a5cd36..337e141d2ef 100644 --- a/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/context/RunContextChainTest.java +++ b/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/context/RunContextChainTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -19,6 +19,7 @@ import org.eclipse.scout.rt.platform.nls.NlsLocale; import org.eclipse.scout.rt.platform.opentelemetry.OpenTelemetryContextProcessor; import org.eclipse.scout.rt.platform.security.SubjectProcessor; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.TransactionProcessor; import org.eclipse.scout.rt.platform.util.ThreadLocalProcessor; import org.eclipse.scout.rt.testing.platform.runner.PlatformTestRunner; @@ -66,32 +67,37 @@ public void testCallableChain() { c = chainIterator.next(); assertEquals(SubjectProcessor.class, c.getClass()); - // 7. DiagnosticContextValueProcessor + // 7. ThreadLocalProcessor for User.CURRENT + c = chainIterator.next(); + assertEquals(ThreadLocalProcessor.class, c.getClass()); + assertSame(User.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); + + // 8. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("subject.principal.name", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 8. DiagnosticContextValueProcessor + // 9. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("opentelemetry.trace.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 9. DiagnosticContextValueProcessor + // 10. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("scout.correlation.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 10. ThreadLocalProcessor for NlsLocale.CURRENT + // 11. ThreadLocalProcessor for NlsLocale.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(NlsLocale.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 11. ThreadLocalProcessor for PropertyMap.CURRENT + // 12. ThreadLocalProcessor for PropertyMap.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(PropertyMap.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 12. TransactionProcessor + // 13. TransactionProcessor c = chainIterator.next(); assertEquals(TransactionProcessor.class, c.getClass()); assertFalse(chainIterator.hasNext()); diff --git a/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/context/RunContextTest.java b/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/context/RunContextTest.java index 7924c165a94..00c5f927148 100644 --- a/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/context/RunContextTest.java +++ b/org.eclipse.scout.rt.platform.test/src/test/java/org/eclipse/scout/rt/platform/context/RunContextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -32,6 +32,7 @@ import org.eclipse.scout.rt.platform.logger.DiagnosticContextValueProcessor.IDiagnosticContextValueProvider; import org.eclipse.scout.rt.platform.nls.NlsLocale; import org.eclipse.scout.rt.platform.security.SimplePrincipal; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.platform.transaction.ITransactionMember; import org.eclipse.scout.rt.platform.transaction.TransactionScope; @@ -56,6 +57,7 @@ public class RunContextTest { public void testEmpty() { RunContext runContext = RunContexts.empty(); assertNull(runContext.getSubject()); + assertNull(runContext.getUser()); assertNull(runContext.getLocale()); assertTrue(toSet(runContext.getPropertyMap().iterator()).isEmpty()); assertNull(runContext.getCorrelationId()); @@ -74,6 +76,7 @@ public void testEmpty() { @Test public void testCopy() { final Subject subject = newSubject("john"); + final User user = newUser("john"); final ITransaction tx = mock(ITransaction.class); final ITransactionMember txMember1 = mock(ITransactionMember.class); when(txMember1.getMemberId()).thenReturn("txMember1"); @@ -85,6 +88,7 @@ public void testCopy() { RunContext runContext = RunContexts.empty() .withProperty("key", "value") .withSubject(subject) + .withUser(user) .withLocale(Locale.CANADA_FRENCH) .withRunMonitor(monitor) .withCorrelationId("cid") @@ -98,6 +102,7 @@ public void testCopy() { // verify assertEquals(toSet(runContext.getPropertyMap().iterator()), toSet(copy.getPropertyMap().iterator())); assertSame(runContext.getSubject(), copy.getSubject()); + assertSame(runContext.getUser(), copy.getUser()); assertSame(runContext.getLocale(), copy.getLocale()); assertSame(runContext.getRunMonitor(), copy.getRunMonitor()); assertEquals("cid", runContext.getCorrelationId()); @@ -110,6 +115,7 @@ public void testCopy() { .run(() -> { assertEquals("value", PropertyMap.CURRENT.get().get("key")); assertSame(subject, Subject.current()); + assertSame(user, User.current()); assertEquals(Locale.CANADA_FRENCH, NlsLocale.CURRENT.get()); assertSame(monitor, RunMonitor.CURRENT.get()); assertEquals("cid", CorrelationId.CURRENT.get()); @@ -119,6 +125,7 @@ public void testCopy() { RunContexts.copyCurrent().run(() -> { assertEquals("value", PropertyMap.CURRENT.get().get("key")); assertSame(subject, Subject.current()); + assertSame(user, User.current()); assertEquals(Locale.CANADA_FRENCH, NlsLocale.CURRENT.get()); assertNotSame(monitor, RunMonitor.CURRENT.get()); assertEquals("cid", CorrelationId.CURRENT.get()); @@ -129,6 +136,7 @@ public void testCopy() { RunContexts.empty().run(() -> { assertNull(PropertyMap.CURRENT.get().get("key")); assertNull(Subject.current()); + assertNull(User.current()); assertNull(NlsLocale.CURRENT.get()); assertNotSame(monitor, RunMonitor.CURRENT.get()); assertNull(CorrelationId.CURRENT.get()); @@ -194,6 +202,50 @@ public void testCopySubject() { }); } + @Test + public void testCopyUser() { + RunContexts.empty().run(() -> { + assertNull(RunContexts.copyCurrent().getUser()); + assertNull(User.current()); + }); + + final User john = newUser("john"); + RunContexts.empty().withUser(john).run(() -> { + assertSame(john, RunContexts.copyCurrent().getUser()); + assertSame(User.current(), RunContexts.copyCurrent().getUser()); + + // Change User + final User anna = newUser("anna"); + RunContexts.copyCurrent().withUser(anna).run(() -> { + // Test copy via 'RunContexts.copyCurrent' + assertSame(anna, RunContexts.copyCurrent().getUser()); + RunContexts.copyCurrent().run(() -> { + assertSame(anna, RunContexts.copyCurrent().getUser()); + assertSame(anna, User.current()); + }); + + RunContext.CURRENT.get().run(() -> { + assertSame(anna, RunContexts.copyCurrent().getUser()); + assertSame(anna, User.current()); + }); + + // Test copy via direct 'RunContext.copy' + assertEquals(anna, RunContext.CURRENT.get().copy().getUser()); + Jobs.schedule(() -> { + assertSame(anna, RunContexts.copyCurrent().getUser()); + assertSame(anna, User.current()); + }, Jobs.newInput() + .withRunContext(RunContext.CURRENT.get().copy())) + .awaitDoneAndGet(); + }); + }); + + RunContexts.empty().withUser(null).run(() -> { + assertNull(RunContexts.copyCurrent().getUser()); + assertNull(User.current()); + }); + } + @Test public void testCopyCurrent_PropertyMap() { RunContexts.empty().run(() -> { @@ -815,6 +867,10 @@ private static Subject newSubject(String principal) { return subject; } + private static User newUser(String userId) { + return BEANS.get(User.class).withUserId(userId).setReadOnly(); + } + private static class RunMonitorEx extends RunMonitor { public boolean containsCancellable(ICancellable cancellable) { diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/context/RunContext.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/context/RunContext.java index 6b20b4c57de..5e0b44d6752 100644 --- a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/context/RunContext.java +++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/context/RunContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -35,6 +35,7 @@ import org.eclipse.scout.rt.platform.opentelemetry.OpenTelemetryContextProcessor; import org.eclipse.scout.rt.platform.opentelemetry.OpenTelemetryTraceIdContextValueProvider; import org.eclipse.scout.rt.platform.security.SubjectProcessor; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.platform.transaction.ITransactionMember; import org.eclipse.scout.rt.platform.transaction.TransactionProcessor; @@ -75,6 +76,7 @@ public class RunContext implements IAdaptable { protected RunMonitor m_runMonitor = BEANS.get(RunMonitor.class); protected RunMonitor m_parentRunMonitor; protected Subject m_subject; + protected User m_user; protected Locale m_locale; protected String m_correlationId; protected PropertyMap m_propertyMap = new PropertyMap(); @@ -193,6 +195,7 @@ protected CallableChain createCallableChain() { .add(new ThreadLocalProcessor<>(CorrelationId.CURRENT, m_correlationId)) .add(new ThreadLocalProcessor<>(RunMonitor.CURRENT, Assertions.assertNotNull(m_runMonitor))) .add(new SubjectProcessor<>(m_subject)) + .add(new ThreadLocalProcessor<>(User.CURRENT, m_user)) .add(new DiagnosticContextValueProcessor(BEANS.get(PrincipalContextValueProvider.class))) .add(new DiagnosticContextValueProcessor(BEANS.get(OpenTelemetryTraceIdContextValueProvider.class))) .add(new DiagnosticContextValueProcessor(BEANS.get(CorrelationIdContextValueProvider.class))) @@ -283,6 +286,22 @@ public RunContext withSubject(final Subject subject) { return this; } + /** + * @see #withUser(User) + */ + public User getUser() { + return m_user; + } + + /** + * Associates this context with the given {@link User}. + */ + public RunContext withUser(final User user) { + Assertions.assertTrue(user == null || user.isReadOnly(), "User must be read only before using it in a run context"); + m_user = user; + return this; + } + /** * @see #withLocale(Locale) */ @@ -527,6 +546,7 @@ public String toString() { protected void interceptToStringBuilder(final ToStringBuilder builder) { builder .attr("subject", getSubject()) + .attr("user", getUser()) .attr("locale", getLocale()) .attr("cid", getCorrelationId()) .attr("transactionScope", getTransactionScope()) @@ -544,6 +564,7 @@ protected void copyValues(final RunContext origin) { m_runMonitor = origin.m_runMonitor; m_parentRunMonitor = origin.m_parentRunMonitor; m_subject = origin.m_subject; + m_user = origin.m_user; m_locale = origin.m_locale; m_correlationId = origin.m_correlationId; m_propertyMap = new PropertyMap(origin.m_propertyMap); @@ -566,6 +587,7 @@ protected void fillCurrentValues() { m_runMonitor = RunMonitor.CURRENT.get(); m_subject = Subject.current(); + m_user = User.current(); m_locale = NlsLocale.CURRENT.get(); m_correlationId = CorrelationId.CURRENT.get(); m_propertyMap = new PropertyMap(PropertyMap.CURRENT.get()); diff --git a/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/User.java b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/User.java new file mode 100644 index 00000000000..d8a59ea14e6 --- /dev/null +++ b/org.eclipse.scout.rt.platform/src/main/java/org/eclipse/scout/rt/platform/security/User.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.scout.rt.platform.security; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.scout.rt.platform.Bean; +import org.eclipse.scout.rt.platform.context.RunContext; + +@Bean +public class User implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final ThreadLocal CURRENT = new ThreadLocal<>(); + + protected static final String USER_ID = "userId"; + + private Map m_data = new HashMap<>(); + private volatile boolean m_readOnly; + + /** + * {@link User} which is currently associated to the current {@link RunContext}. + */ + public static User current() { + return CURRENT.get(); + } + + /** + * {@code userId} which is currently associated to the current {@link RunContext}. + */ + public static String currentUserId() { + return current() != null ? current().getUserId() : null; + } + + public String getUserId() { + return getData(USER_ID); + } + + public User withUserId(String userId) { + setData(USER_ID, userId); + return this; + } + + protected T getData(String property) { + //noinspection unchecked + return (T) m_data.get(property); + } + + protected void setData(String property, Serializable value) { + if (value == null) { + return; + } + if (m_readOnly) { + throw new IllegalStateException("trying to set property " + property + " on a read-only user"); + } + m_data.put(property, value); + } + + public boolean isReadOnly() { + return m_readOnly; + } + + public User setReadOnly() { + m_readOnly = true; + return this; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + + User user = (User) o; + return m_data.equals(user.m_data); + } + + @Override + public int hashCode() { + return m_data.hashCode(); + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [data=" + m_data + "]"; + } +} diff --git a/org.eclipse.scout.rt.rest.jersey.server/src/main/java/org/eclipse/scout/rt/rest/jersey/server/RequestCancellationRestContainerRequestResponseFilter.java b/org.eclipse.scout.rt.rest.jersey.server/src/main/java/org/eclipse/scout/rt/rest/jersey/server/RequestCancellationRestContainerRequestResponseFilter.java index e32e56ef229..d9c48788c3b 100644 --- a/org.eclipse.scout.rt.rest.jersey.server/src/main/java/org/eclipse/scout/rt/rest/jersey/server/RequestCancellationRestContainerRequestResponseFilter.java +++ b/org.eclipse.scout.rt.rest.jersey.server/src/main/java/org/eclipse/scout/rt/rest/jersey/server/RequestCancellationRestContainerRequestResponseFilter.java @@ -18,12 +18,12 @@ import org.eclipse.scout.rt.platform.BEANS; import org.eclipse.scout.rt.platform.context.RunContext; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.ConnectionErrorDetector; import org.eclipse.scout.rt.rest.RestHttpHeaders; import org.eclipse.scout.rt.rest.cancellation.RestRequestCancellationRegistry; import org.eclipse.scout.rt.rest.container.IRestContainerRequestFilter; import org.eclipse.scout.rt.rest.container.IRestContainerResponseFilter; -import org.eclipse.scout.rt.security.IAccessControlService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,7 +68,7 @@ protected void registerRunMonitor(ContainerRequestContext request) { * Returns the user id of the given request. May be {@code null}. */ protected Object resolveUserId(ContainerRequestContext request) { - return BEANS.get(IAccessControlService.class).getUserIdOfCurrentSubject(); + return User.currentUserId(); } /** diff --git a/org.eclipse.scout.rt.rest.jersey.test/src/main/java/org/eclipse/scout/rt/rest/jersey/JerseyTestAccessControlService.java b/org.eclipse.scout.rt.rest.jersey.test/src/main/java/org/eclipse/scout/rt/rest/jersey/JerseyTestAccessControlService.java index 01ef6bbb421..6eb49aae2ab 100644 --- a/org.eclipse.scout.rt.rest.jersey.test/src/main/java/org/eclipse/scout/rt/rest/jersey/JerseyTestAccessControlService.java +++ b/org.eclipse.scout.rt.rest.jersey.test/src/main/java/org/eclipse/scout/rt/rest/jersey/JerseyTestAccessControlService.java @@ -10,28 +10,19 @@ package org.eclipse.scout.rt.rest.jersey; import org.eclipse.scout.rt.platform.BEANS; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.security.AbstractAccessControlService; import org.eclipse.scout.rt.security.AllPermissionCollection; import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.security.IPermissionCollection; /** - * Test implementation of {@link IAccessControlService} providing fixed subject. + * Test implementation of {@link IAccessControlService}. */ -public class JerseyTestAccessControlService extends AbstractAccessControlService { +public class JerseyTestAccessControlService extends AbstractAccessControlService { @Override - protected String getCurrentUserCacheKey() { - return "user.mock"; - } - - @Override - public String getUserIdOfCurrentSubject() { - return "user.mock"; - } - - @Override - protected IPermissionCollection execLoadPermissions(String cacheKey) { + protected IPermissionCollection execLoadPermissions(User user) { return BEANS.get(AllPermissionCollection.class); } } diff --git a/org.eclipse.scout.rt.rest.test/src/test/java/org/eclipse/scout/rt/rest/cancellation/RestRequestCancellationClientRequestFilterTest.java b/org.eclipse.scout.rt.rest.test/src/test/java/org/eclipse/scout/rt/rest/cancellation/RestRequestCancellationClientRequestFilterTest.java index 0d1f9500a69..3a1d4def708 100644 --- a/org.eclipse.scout.rt.rest.test/src/test/java/org/eclipse/scout/rt/rest/cancellation/RestRequestCancellationClientRequestFilterTest.java +++ b/org.eclipse.scout.rt.rest.test/src/test/java/org/eclipse/scout/rt/rest/cancellation/RestRequestCancellationClientRequestFilterTest.java @@ -21,6 +21,7 @@ import org.eclipse.scout.rt.platform.context.RunContexts; import org.eclipse.scout.rt.platform.holders.Holder; import org.eclipse.scout.rt.platform.holders.StringHolder; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.CollectionUtility; import org.eclipse.scout.rt.platform.util.concurrent.ICancellable; import org.eclipse.scout.rt.rest.RestHttpHeaders; @@ -37,8 +38,8 @@ public class RestRequestCancellationClientRequestFilterTest { public void testSubjectSameRunContext() { Subject subject = mock(Subject.class); RunContext runContext = RunContexts.empty().withSubject(subject); - - RunContext cancellationRunContext = testCancellationSubject(runContext, runContext); + + RunContext cancellationRunContext = testCancellation(runContext, runContext); assertEquals(subject, cancellationRunContext.getSubject()); } @@ -48,28 +49,61 @@ public void testSubjectFallback() { RunContext runContext = RunContexts.empty().withSubject(subject); // if context calling the cancellation does not include a Subject, the Subject of the original request should be used - RunContext cancellationRunContext = testCancellationSubject(runContext, RunContexts.empty()); + RunContext cancellationRunContext = testCancellation(runContext, RunContexts.empty()); assertEquals(subject, cancellationRunContext.getSubject()); } + @Test + public void testUserFallback() { + User user = createUserMock(); + RunContext runContext = RunContexts.empty().withUser(user); + + // if context calling the cancellation does not include a User, the User of the original request should be used + RunContext cancellationRunContext = testCancellation(runContext, RunContexts.empty()); + assertEquals(user, cancellationRunContext.getUser()); + } + @Test public void testSubjectDifferent() { Subject subject1 = mock(Subject.class); Subject subject2 = mock(Subject.class); // normally the Subject of the context calling the cancellation should be used for cancel call - RunContext cancellationRunContext = testCancellationSubject(RunContexts.empty().withSubject(subject1), RunContexts.empty().withSubject(subject2)); + RunContext cancellationRunContext = testCancellation(RunContexts.empty().withSubject(subject1), RunContexts.empty().withSubject(subject2)); assertEquals(subject2, cancellationRunContext.getSubject()); } + @Test + public void testUserDifferent() { + User user1 = createUserMock(); + User user2 = createUserMock(); + + // normally the User of the context calling the cancellation should be used for cancel call + RunContext cancellationRunContext = testCancellation(RunContexts.empty().withUser(user1), RunContexts.empty().withUser(user2)); + assertEquals(user2, cancellationRunContext.getUser()); + } + @Test public void testSubjectNone() { // if original request and context calling the cancellation does not include a Subject there is not much we can do, we try to run cancellation also without Subject (which might fail though) - RunContext cancellationRunContext = testCancellationSubject(RunContexts.empty(), RunContexts.empty()); + RunContext cancellationRunContext = testCancellation(RunContexts.empty(), RunContexts.empty()); assertNull(cancellationRunContext.getSubject()); } - protected RunContext testCancellationSubject(RunContext requestRunContext, RunContext cancellationRunContext) { + @Test + public void testUserNone() { + // if original request and context calling the cancellation does not include a User there is not much we can do, we try to run cancellation also without User (which might fail though) + RunContext cancellationRunContext = testCancellation(RunContexts.empty(), RunContexts.empty()); + assertNull(cancellationRunContext.getUser()); + } + + protected User createUserMock() { + User user = mock(User.class); + when(user.isReadOnly()).thenReturn(true); + return user; + } + + protected RunContext testCancellation(RunContext requestRunContext, RunContext cancellationRunContext) { Holder cancellationRunContextHolder = new Holder<>(); StringHolder cancellationRequestIdHolder = new StringHolder(); diff --git a/org.eclipse.scout.rt.rest/src/main/java/org/eclipse/scout/rt/rest/cancellation/CancellationResource.java b/org.eclipse.scout.rt.rest/src/main/java/org/eclipse/scout/rt/rest/cancellation/CancellationResource.java index b94afe2944b..efdfda52738 100644 --- a/org.eclipse.scout.rt.rest/src/main/java/org/eclipse/scout/rt/rest/cancellation/CancellationResource.java +++ b/org.eclipse.scout.rt.rest/src/main/java/org/eclipse/scout/rt/rest/cancellation/CancellationResource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -9,21 +9,19 @@ */ package org.eclipse.scout.rt.rest.cancellation; -import javax.security.auth.Subject; - import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; import jakarta.ws.rs.PathParam; import org.eclipse.scout.rt.api.data.ApiExposed; import org.eclipse.scout.rt.platform.BEANS; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.rest.IRestResource; -import org.eclipse.scout.rt.security.IAccessControlService; /** * REST resource providing cancellation support. *

- * The {@link IAccessControlService#getUserId(Subject)} method is used to identify users, subclasses may overwrite the {@link #resolveCurrentUserId()} method to provide a different identifier. + * The {@link User#currentUserId()} method is used to identify users, subclasses may overwrite the {@link #resolveCurrentUserId()} method to provide a different identifier. * * @see RestRequestCancellationRegistry * @see RestRequestCancellationClientRequestFilter @@ -44,6 +42,6 @@ public void cancel(@PathParam("requestId") String requestId) { * Returns the user id of the current user. May be {@code null}. */ protected String resolveCurrentUserId() { - return BEANS.get(IAccessControlService.class).getUserIdOfCurrentSubject(); + return User.currentUserId(); } } diff --git a/org.eclipse.scout.rt.rest/src/main/java/org/eclipse/scout/rt/rest/cancellation/RestRequestCancellationClientRequestFilter.java b/org.eclipse.scout.rt.rest/src/main/java/org/eclipse/scout/rt/rest/cancellation/RestRequestCancellationClientRequestFilter.java index 0c413763a9a..08496fb0d6f 100644 --- a/org.eclipse.scout.rt.rest/src/main/java/org/eclipse/scout/rt/rest/cancellation/RestRequestCancellationClientRequestFilter.java +++ b/org.eclipse.scout.rt.rest/src/main/java/org/eclipse/scout/rt/rest/cancellation/RestRequestCancellationClientRequestFilter.java @@ -23,6 +23,7 @@ import org.eclipse.scout.rt.platform.context.RunContext; import org.eclipse.scout.rt.platform.context.RunContexts; import org.eclipse.scout.rt.platform.context.RunMonitor; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.concurrent.ICancellable; import org.eclipse.scout.rt.rest.RestHttpHeaders; import org.eclipse.scout.rt.rest.client.RestClientProperties; @@ -61,6 +62,7 @@ public static class ScoutRestRequestCancellable implements ICancellable { private final String m_requestId; private final Subject m_subject; + private final User m_user; private final ClientRequestContext m_clientRequestContext; private final AtomicBoolean m_cancelled; private final Consumer m_requestCanceller; @@ -68,6 +70,7 @@ public static class ScoutRestRequestCancellable implements ICancellable { public ScoutRestRequestCancellable(String requestId, ClientRequestContext requestContext, Consumer requestCanceller) { m_requestId = requestId; m_subject = Subject.current(); + m_user = User.current(); m_clientRequestContext = requestContext; m_requestCanceller = requestCanceller; m_cancelled = new AtomicBoolean(); @@ -92,6 +95,10 @@ public boolean cancel(boolean interruptIfRunning) { LOG.trace("No subject set: Using subject {} for cancellation", m_subject); runContext.withSubject(m_subject); } + if (m_user != null && runContext.getUser() == null) { + LOG.trace("No user set: Using user {} for cancellation", m_user); + runContext.withUser(m_user); + } runContext .withRunMonitor(BEANS.get(RunMonitor.class)) // execute with a new RunMonitor .run(() -> m_requestCanceller.accept(m_requestId)); diff --git a/org.eclipse.scout.rt.security.test/src/test/java/org/eclipse/scout/rt/security/AccessControlServiceTest.java b/org.eclipse.scout.rt.security.test/src/test/java/org/eclipse/scout/rt/security/AccessControlServiceTest.java index 8d1cabc94f0..46dcfeef20c 100644 --- a/org.eclipse.scout.rt.security.test/src/test/java/org/eclipse/scout/rt/security/AccessControlServiceTest.java +++ b/org.eclipse.scout.rt.security.test/src/test/java/org/eclipse/scout/rt/security/AccessControlServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -28,6 +28,7 @@ import org.eclipse.scout.rt.platform.exception.ProcessingException; import org.eclipse.scout.rt.platform.internal.BeanInstanceUtil; import org.eclipse.scout.rt.platform.security.SimplePrincipal; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.testing.platform.BeanTestingHelper; import org.eclipse.scout.rt.testing.platform.runner.PlatformTestRunner; import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; @@ -42,7 +43,7 @@ @RunWith(PlatformTestRunner.class) @RunWithSubject("john") public class AccessControlServiceTest { - private static final String TEST_USER = "john"; + private static final User TEST_USER = BEANS.get(User.class).withUserId("john").setReadOnly(); private P_SharedAccessControlService m_accessControlService; private List> m_registerServices; @@ -64,29 +65,29 @@ public void tearDown() { } @Test - public void testGetUserId() { - assertNull(m_accessControlService.getUserId(null)); + public void testGetUser() { + assertNull(m_accessControlService.getUser(null)); Subject s = new Subject(); - assertNull(m_accessControlService.getUserId(s)); + assertNull(m_accessControlService.getUser(s).getUserId()); s.getPrincipals().add(new DummyPrincipal()); - assertEquals("dummy", m_accessControlService.getUserId(s)); + assertEquals("dummy", m_accessControlService.getUser(s).getUserId()); // first principal wins s.getPrincipals().add(new SimplePrincipal("simple")); - assertEquals("dummy", m_accessControlService.getUserId(s)); + assertEquals("dummy", m_accessControlService.getUser(s).getUserId()); s = new Subject(); s.getPrincipals().add(new SimplePrincipal("simple")); s.getPrincipals().add(new SimplePrincipal("other")); s.getPrincipals().add(new DummyPrincipal()); - assertEquals("simple", m_accessControlService.getUserId(s)); + assertEquals("simple", m_accessControlService.getUser(s).getUserId()); } @Test public void testGetUserIdOfCurrentSubjectNoSubject() { - assertNull(Subject.doAs(null, (PrivilegedAction) () -> m_accessControlService.getUserIdOfCurrentSubject())); + assertNull(Subject.doAs(null, (PrivilegedAction) () -> m_accessControlService.extractUserId(Subject.current()))); } @Test @@ -94,7 +95,7 @@ public void testGetUserIdOfCurrentSubject() { Subject subject = new Subject(); subject.getPrincipals().add(new SimplePrincipal("username")); - assertEquals("username", Subject.doAs(subject, (PrivilegedAction) () -> m_accessControlService.getUserIdOfCurrentSubject())); + assertEquals("username", Subject.doAs(subject, (PrivilegedAction) () -> m_accessControlService.extractUserId(Subject.current()))); } /** @@ -142,15 +143,10 @@ public void testClearCacheOfUserIds() throws ProcessingException { } @IgnoreBean - private static class P_SharedAccessControlService extends AbstractAccessControlService { + private static class P_SharedAccessControlService extends AbstractAccessControlService { @Override - protected String getCurrentUserCacheKey() { - return getUserIdOfCurrentSubject(); - } - - @Override - protected IPermissionCollection execLoadPermissions(String userId) { + protected IPermissionCollection execLoadPermissions(User user) { DefaultPermissionCollection permissions = BEANS.get(DefaultPermissionCollection.class); permissions.add(new SomePermission1(), PermissionLevel.ALL); permissions.setReadOnly(); @@ -158,7 +154,7 @@ protected IPermissionCollection execLoadPermissions(String userId) { } @Override - protected ICacheBuilder createCacheBuilder() { + protected ICacheBuilder createCacheBuilder() { return super.createCacheBuilder() .withCacheId(ACCESS_CONTROL_SERVICE_CACHE_ID + ".for.test") .withReplaceIfExists(true); diff --git a/org.eclipse.scout.rt.security/src/main/java/org/eclipse/scout/rt/security/AbstractAccessControlService.java b/org.eclipse.scout.rt.security/src/main/java/org/eclipse/scout/rt/security/AbstractAccessControlService.java index 1ded37f1989..5f4ef9cd80f 100644 --- a/org.eclipse.scout.rt.security/src/main/java/org/eclipse/scout/rt/security/AbstractAccessControlService.java +++ b/org.eclipse.scout.rt.security/src/main/java/org/eclipse/scout/rt/security/AbstractAccessControlService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -9,6 +9,8 @@ */ package org.eclipse.scout.rt.security; +import static org.eclipse.scout.rt.platform.util.Assertions.assertTrue; + import java.security.PermissionCollection; import java.security.Principal; import java.util.Collection; @@ -32,7 +34,7 @@ import org.eclipse.scout.rt.platform.cache.ICacheInvalidationListener; import org.eclipse.scout.rt.platform.cache.ICacheValueResolver; import org.eclipse.scout.rt.platform.cache.KeyCacheEntryFilter; -import org.eclipse.scout.rt.platform.context.RunContext; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.event.FastListenerList; import org.eclipse.scout.rt.platform.util.event.IFastListenerList; import org.slf4j.Logger; @@ -40,27 +42,25 @@ /** * Common logic for an {@link IAccessControlService} implementation. An Implementation has to override - * {@link #getCurrentUserCacheKey()} and {@link #execLoadPermissions(Object)}. For example use as generic key type - * String and simply return as cache key the current userId in {@link #getCurrentUserCacheKey()}. + * {@link #execLoadPermissions(User)}. The method {{@link #getUser(Subject)}} creates the {@link User} object for the + * given {@link Subject} that is used to load the permissions and is also used as cache key. *

- * Note that the method {@link #execLoadPermissions(Object)} must not have a valid implementation in the client, + * Note that the method {@link #execLoadPermissions(User)} must not have a valid implementation in the client, * as a client will always get the value from the server. Therefore, consider two implementations like * 'CustomAccessControlService' and 'CustomServerAccessControlService'. *

* This class caches permission collections. As default, the cache is transactional and with a time to live duration of * one hour. To change any of these properties override {@link #createCacheBuilder()}. * - * @param - * the type of keys maintained the cache * @since 4.3.0 (Mars-M5) */ -public abstract class AbstractAccessControlService implements IAccessControlService { +public abstract class AbstractAccessControlService implements IAccessControlService { private static final Logger LOG = LoggerFactory.getLogger(AbstractAccessControlService.class); public static final String ACCESS_CONTROL_SERVICE_CACHE_ID = AbstractAccessControlService.class.getName(); private volatile Pattern[] m_userIdSearchPatterns; - private volatile ICache m_cache; - private volatile IFastListenerList> m_invalidationListeners; + private volatile ICache m_cache; + private volatile IFastListenerList> m_invalidationListeners; public AbstractAccessControlService() { m_userIdSearchPatterns = new Pattern[]{ @@ -114,9 +114,9 @@ protected void setUserIdSearchPatterns(String... patterns) { * * @return {@link ICacheBuilder} for the internal cache */ - protected ICacheBuilder createCacheBuilder() { + protected ICacheBuilder createCacheBuilder() { @SuppressWarnings("unchecked") - ICacheBuilder cacheBuilder = BEANS.get(ICacheBuilder.class); + ICacheBuilder cacheBuilder = BEANS.get(ICacheBuilder.class); return cacheBuilder.withCacheId(ACCESS_CONTROL_SERVICE_CACHE_ID).withValueResolver(createCacheValueResolver()) .withShared(true) .withClusterEnabled(true) @@ -126,81 +126,70 @@ protected ICacheBuilder createCacheBuilder() { .withTimeToLive(1L, TimeUnit.HOURS, false); } - protected static class InvalidationListenerWrapper extends AbstractCacheWrapper { + protected static class InvalidationListenerWrapper extends AbstractCacheWrapper { - public InvalidationListenerWrapper(ICache delegate) { + public InvalidationListenerWrapper(ICache delegate) { super(delegate); } @Override - public void invalidate(ICacheEntryFilter filter, boolean propagate) { + public void invalidate(ICacheEntryFilter filter, boolean propagate) { super.invalidate(filter, propagate); BEANS.get(IAccessControlService.class).getInvalidationListeners().forEach(l -> l.invalidated(filter, propagate)); } } @Override - public void addInvalidationListener(ICacheInvalidationListener listener) { + public void addInvalidationListener(ICacheInvalidationListener listener) { if (listener != null) { m_invalidationListeners.add(listener); } } @Override - public void removeInvalidationListener(ICacheInvalidationListener listener) { + public void removeInvalidationListener(ICacheInvalidationListener listener) { if (listener != null) { m_invalidationListeners.remove(listener); } } @Override - public List> getInvalidationListeners() { + public List> getInvalidationListeners() { return m_invalidationListeners.list(); } - protected ICacheValueResolver createCacheValueResolver() { - return this::execLoadPermissions; + protected ICacheValueResolver createCacheValueResolver() { + return user -> { + assertTrue(user.isReadOnly(), "User must be read only before accessing the permission cache"); + return execLoadPermissions(user); + }; } - protected ICache getCache() { + protected ICache getCache() { return m_cache; } - /** - * Implement this method to get the cache key of the current user. Extract it from the current session or any other - * property in the current {@link RunContext}. - * - * @return cache key of the current user or null if the current context has no user assigned to it. - */ - protected abstract K getCurrentUserCacheKey(); - /** * Implement this method to load a {@link PermissionCollection} for a given cache key. This method must be valid * only in the server. Client does never call this method but loads its value directly from the server cache. * * @return new PermissionCollection for the given cache key */ - protected abstract IPermissionCollection execLoadPermissions(K cacheKey); - - @Override - public String getUserIdOfCurrentSubject() { - return getUserId(Subject.current()); - } + protected abstract IPermissionCollection execLoadPermissions(User user); @Override - public String getUserIdForCacheKey(Object cacheKey) { - if (cacheKey instanceof String) { - // default implementation where the userId itself is the cache key - return (String) cacheKey; + public User getUser(Subject subject) { + if (subject == null) { + return null; } - - // if another cache-key is used, subclasses must implement their own mapping - LOG.error("By default only userId cacheKeys are supported. Overwrite this method for custom cacheKeys."); - return null; + User user = BEANS.get(User.class) + .withUserId(extractUserId(subject)); + initUser(user, subject); + return user.setReadOnly(); } @Override - public String getUserId(Subject subject) { + public String extractUserId(Subject subject) { if (subject == null) { return null; } @@ -216,13 +205,20 @@ public String getUserId(Subject subject) { return null; } + /** + * Load additional data using given {@code subject} to {@link User}. + */ + protected void initUser(User user, Subject subject) { + // NOP + } + @Override public IPermissionCollection getPermissions() { - K currentUserCacheKey = getCurrentUserCacheKey(); - IPermissionCollection permissions = getCache().get(currentUserCacheKey); - LOG.trace("getPermissions() called for {}, returned {}", currentUserCacheKey, permissions); + User user = User.current(); + IPermissionCollection permissions = getCache().get(user); + LOG.trace("getPermissions() called for {}, returned {}", user, permissions); if (permissions == null) { - LOG.error("getPermissions() called for {}, returned {}", currentUserCacheKey, permissions); + LOG.error("getPermissions() called for {}, returned {}", user, permissions); } return permissions == null ? BEANS.get(NonePermissionCollection.class) : permissions; } @@ -234,14 +230,14 @@ public void clearCache() { @Override public void clearCacheOfCurrentUser() { - clearCache(Collections.singleton(getCurrentUserCacheKey())); + clearCache(Collections.singleton(User.current())); } - protected void clearCache(Collection cacheKeys) { + protected void clearCache(Collection cacheKeys) { if (cacheKeys == null) { return; } - KeyCacheEntryFilter filter = new KeyCacheEntryFilter<>(cacheKeys); + KeyCacheEntryFilter filter = new KeyCacheEntryFilter<>(cacheKeys); if (filter.getKeys().isEmpty()) { return; } diff --git a/org.eclipse.scout.rt.security/src/main/java/org/eclipse/scout/rt/security/IAccessControlService.java b/org.eclipse.scout.rt.security/src/main/java/org/eclipse/scout/rt/security/IAccessControlService.java index a6381402393..a0243f52537 100644 --- a/org.eclipse.scout.rt.security/src/main/java/org/eclipse/scout/rt/security/IAccessControlService.java +++ b/org.eclipse.scout.rt.security/src/main/java/org/eclipse/scout/rt/security/IAccessControlService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -16,6 +16,8 @@ import org.eclipse.scout.rt.platform.cache.ICache; import org.eclipse.scout.rt.platform.cache.ICacheEntryFilter; import org.eclipse.scout.rt.platform.cache.ICacheInvalidationListener; +import org.eclipse.scout.rt.platform.context.RunContext; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.service.IService; /** @@ -24,14 +26,14 @@ public interface IAccessControlService extends IService { /** - * @return current UserId extracted from current {@link Subject} + * @return {@link User} object for the provided {@link Subject} */ - String getUserIdOfCurrentSubject(); + User getUser(Subject subject); /** - * @return current UserId extracted from the provided {@link Subject} + * Caution Use carefully only in cases where there is no {@link RunContext} providing {@link User} in context. */ - String getUserId(Subject subject); + String extractUserId(Subject subject); /** * Returns the {@link IPermissionCollection} for the current user. @@ -62,22 +64,15 @@ public interface IAccessControlService extends IService { * The listener to add. The {@link ICacheEntryFilter} given to the listener is the filter passed to * {@link ICache#invalidate(ICacheEntryFilter, boolean)}. */ - void addInvalidationListener(ICacheInvalidationListener listener); + void addInvalidationListener(ICacheInvalidationListener listener); /** * Removes the given listener. */ - void removeInvalidationListener(ICacheInvalidationListener listener); + void removeInvalidationListener(ICacheInvalidationListener listener); /** * @return All registered invalidation listeners. */ - List> getInvalidationListeners(); - - /** - * @param cacheKey - * A cacheKey used by the internal cache of this service. - * @return The userId (username) of the given cacheKey. - */ - String getUserIdForCacheKey(Object cacheKey); + List> getInvalidationListeners(); } diff --git a/org.eclipse.scout.rt.server.commons.test/src/test/java/org/eclipse/scout/rt/server/commons/context/HttpRunContextProducerTest.java b/org.eclipse.scout.rt.server.commons.test/src/test/java/org/eclipse/scout/rt/server/commons/context/HttpRunContextProducerTest.java index 158631acb55..9923756ea72 100644 --- a/org.eclipse.scout.rt.server.commons.test/src/test/java/org/eclipse/scout/rt/server/commons/context/HttpRunContextProducerTest.java +++ b/org.eclipse.scout.rt.server.commons.test/src/test/java/org/eclipse/scout/rt/server/commons/context/HttpRunContextProducerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -24,6 +24,7 @@ import org.eclipse.scout.rt.platform.context.CorrelationId; import org.eclipse.scout.rt.platform.context.RunContext; import org.eclipse.scout.rt.platform.security.SimplePrincipalProducer; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.TransactionScope; import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.server.commons.authentication.ServletFilterHelper; @@ -48,7 +49,7 @@ public class HttpRunContextProducerTest { @Before public void before() { - when(m_mockAccessControlService.getUserIdOfCurrentSubject()).thenReturn(TEST_SUBJECT_NAME); + when(m_mockAccessControlService.getUser(TEST_SUBJECT)).thenReturn(BEANS.get(User.class).withUserId(TEST_SUBJECT_NAME).setReadOnly()); } @Test @@ -59,6 +60,7 @@ public void testCreateRunContextWithCid() { RunContext context = producer.produce(req, resp); assertEquals(TEST_SUBJECT.getPrincipals(), context.getSubject().getPrincipals()); + assertEquals(TEST_SUBJECT_NAME, context.getUser().getUserId()); assertEquals(TEST_CID, context.getCorrelationId()); assertSame(req, context.getThreadLocal(IHttpServletRoundtrip.CURRENT_HTTP_SERVLET_REQUEST)); assertSame(resp, context.getThreadLocal(IHttpServletRoundtrip.CURRENT_HTTP_SERVLET_RESPONSE)); diff --git a/org.eclipse.scout.rt.server.commons/src/main/java/org/eclipse/scout/rt/server/commons/context/HttpRunContextProducer.java b/org.eclipse.scout.rt.server.commons/src/main/java/org/eclipse/scout/rt/server/commons/context/HttpRunContextProducer.java index c8f98c4bb85..a50a2aa9dee 100644 --- a/org.eclipse.scout.rt.server.commons/src/main/java/org/eclipse/scout/rt/server/commons/context/HttpRunContextProducer.java +++ b/org.eclipse.scout.rt.server.commons/src/main/java/org/eclipse/scout/rt/server/commons/context/HttpRunContextProducer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -24,7 +24,6 @@ import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.server.commons.servlet.IHttpServletRoundtrip; import org.eclipse.scout.rt.server.commons.servlet.logging.ServletDiagnosticsProviderFactory; -import org.eclipse.scout.rt.shared.user.UserId; /** * Creates a {@link RunContext} based on a {@link HttpServletRequest} and the current JAAS context. @@ -64,7 +63,7 @@ public RunContext produce(HttpServletRequest req, HttpServletResponse resp, RunC return contextToFill .withSubject(Subject.current()) - .withThreadLocal(UserId.CURRENT, BEANS.get(IAccessControlService.class).getUserId(Subject.current())) + .withUser(BEANS.get(IAccessControlService.class).getUser(Subject.current())) .withCorrelationId(currentCorrelationId(req)) .withThreadLocal(IHttpServletRoundtrip.CURRENT_HTTP_SERVLET_REQUEST, req) .withThreadLocal(IHttpServletRoundtrip.CURRENT_HTTP_SERVLET_RESPONSE, resp) diff --git a/org.eclipse.scout.rt.server.jaxws.test/src/test/java/org/eclipse/scout/rt/server/jaxws/consumer/InvocationContextTest.java b/org.eclipse.scout.rt.server.jaxws.test/src/test/java/org/eclipse/scout/rt/server/jaxws/consumer/InvocationContextTest.java index 94bf4230530..8363e0f028d 100644 --- a/org.eclipse.scout.rt.server.jaxws.test/src/test/java/org/eclipse/scout/rt/server/jaxws/consumer/InvocationContextTest.java +++ b/org.eclipse.scout.rt.server.jaxws.test/src/test/java/org/eclipse/scout/rt/server/jaxws/consumer/InvocationContextTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -29,12 +29,12 @@ import org.eclipse.scout.rt.platform.holders.Holder; import org.eclipse.scout.rt.platform.job.IBlockingCondition; import org.eclipse.scout.rt.platform.job.Jobs; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.platform.transaction.TransactionScope; import org.eclipse.scout.rt.server.context.ServerRunContexts; import org.eclipse.scout.rt.server.jaxws.MessageContexts; import org.eclipse.scout.rt.server.jaxws.implementor.JaxWsImplementorSpecifics; -import org.eclipse.scout.rt.shared.user.UserId; import org.eclipse.scout.rt.testing.platform.BeanTestingHelper; import org.eclipse.scout.rt.testing.platform.runner.JUnitExceptionHandler; import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; @@ -94,7 +94,7 @@ public void testWithSuccess() { invocationContext.whenRollback(m_rollbackListener); invocationContext.whenInvoke((proxy, method, args) -> { invocationTransaction.setValue(ITransaction.CURRENT.get()); - invocationUserId.setValue(UserId.CURRENT.get()); + invocationUserId.setValue(User.currentUserId()); return method.invoke(proxy, args); }); @@ -104,7 +104,7 @@ public void testWithSuccess() { }); assertSame(currentTransaction.getValue(), invocationTransaction.getValue()); - assertSame(UserId.CURRENT.get(), invocationUserId.getValue()); + assertSame(User.currentUserId(), invocationUserId.getValue()); assertEquals(TESTING_CORRELATION_ID, m_port.getRequestContext().get(MessageContexts.PROP_CORRELATION_ID)); verify(m_port).webMethod(); @@ -140,7 +140,7 @@ public void testWithException() { invocationContext.whenRollback(m_rollbackListener); invocationContext.whenInvoke((proxy, method, args) -> { invocationTransaction.setValue(ITransaction.CURRENT.get()); - invocationUserId.setValue(UserId.CURRENT.get()); + invocationUserId.setValue(User.currentUserId()); return method.invoke(proxy, args); }); @@ -162,7 +162,7 @@ public void testWithException() { } assertSame(currentTransaction.getValue(), invocationTransaction.getValue()); - assertSame(UserId.CURRENT.get(), invocationUserId.getValue()); + assertSame(User.currentUserId(), invocationUserId.getValue()); assertEquals(TESTING_CORRELATION_ID, m_port.getRequestContext().get(MessageContexts.PROP_CORRELATION_ID)); verify(m_port).webMethod(); diff --git a/org.eclipse.scout.rt.server.jaxws/src/main/java/org/eclipse/scout/rt/server/jaxws/context/ServerRunContextProducer.java b/org.eclipse.scout.rt.server.jaxws/src/main/java/org/eclipse/scout/rt/server/jaxws/context/ServerRunContextProducer.java index 77b9fc01d1b..548212cbbd5 100644 --- a/org.eclipse.scout.rt.server.jaxws/src/main/java/org/eclipse/scout/rt/server/jaxws/context/ServerRunContextProducer.java +++ b/org.eclipse.scout.rt.server.jaxws/src/main/java/org/eclipse/scout/rt/server/jaxws/context/ServerRunContextProducer.java @@ -18,7 +18,6 @@ import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.server.context.ServerRunContext; import org.eclipse.scout.rt.server.context.ServerRunContexts; -import org.eclipse.scout.rt.shared.user.UserId; /** * Producer for {@link RunContext} objects. @@ -37,7 +36,7 @@ public class ServerRunContextProducer extends RunContextProducer { public ServerRunContext produce(final Subject subject) { return ServerRunContexts.copyCurrent(true) .withSubject(subject) - .withThreadLocal(UserId.CURRENT, BEANS.get(IAccessControlService.class).getUserId(subject)) + .withUser(BEANS.get(IAccessControlService.class).getUser(subject)) .withTransactionScope(TransactionScope.REQUIRES_NEW); } } diff --git a/org.eclipse.scout.rt.server.jdbc/src/main/java/org/eclipse/scout/rt/server/jdbc/internal/exec/StatementProcessor.java b/org.eclipse.scout.rt.server.jdbc/src/main/java/org/eclipse/scout/rt/server/jdbc/internal/exec/StatementProcessor.java index 458cb61593a..b6a3d7f0ac8 100644 --- a/org.eclipse.scout.rt.server.jdbc/src/main/java/org/eclipse/scout/rt/server/jdbc/internal/exec/StatementProcessor.java +++ b/org.eclipse.scout.rt.server.jdbc/src/main/java/org/eclipse/scout/rt/server/jdbc/internal/exec/StatementProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -38,6 +38,7 @@ import org.eclipse.scout.rt.platform.holders.NVPair; import org.eclipse.scout.rt.platform.holders.TableBeanHolderFilter; import org.eclipse.scout.rt.platform.reflect.FastPropertyDescriptor; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.platform.transaction.ITransactionMember; import org.eclipse.scout.rt.platform.util.BeanUtility; @@ -62,7 +63,6 @@ import org.eclipse.scout.rt.server.jdbc.parsers.token.ValueInputToken; import org.eclipse.scout.rt.server.jdbc.parsers.token.ValueOutputToken; import org.eclipse.scout.rt.server.jdbc.style.ISqlStyle; -import org.eclipse.scout.rt.shared.user.UserId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -116,7 +116,7 @@ public StatementProcessor(ISqlService callerService, String stm, Object[] bindBa m_maxRowCount = maxRowCount; m_maxFetchMemorySize = maxFetchMemorySize; // add userId to binds if available - final String userId = UserId.CURRENT.get(); + final String userId = User.currentUserId(); if (userId != null) { NVPair userIdBindBase = new NVPair(USER_ID_BIND_NAME, userId); if (bindBases == null) { diff --git a/org.eclipse.scout.rt.server.session.test/src/main/java/org/eclipse/scout/rt/testing/server/session/runner/statement/ServerSessionRunContextStatement.java b/org.eclipse.scout.rt.server.session.test/src/main/java/org/eclipse/scout/rt/testing/server/session/runner/statement/ServerSessionRunContextStatement.java index 1287d1bf796..882e561a1fb 100644 --- a/org.eclipse.scout.rt.server.session.test/src/main/java/org/eclipse/scout/rt/testing/server/session/runner/statement/ServerSessionRunContextStatement.java +++ b/org.eclipse.scout.rt.server.session.test/src/main/java/org/eclipse/scout/rt/testing/server/session/runner/statement/ServerSessionRunContextStatement.java @@ -22,7 +22,6 @@ import org.eclipse.scout.rt.shared.session.ISession; import org.eclipse.scout.rt.shared.ui.UserAgent; import org.eclipse.scout.rt.shared.ui.UserAgents; -import org.eclipse.scout.rt.shared.user.UserId; import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; import org.eclipse.scout.rt.testing.platform.runner.SafeStatementInvoker; import org.eclipse.scout.rt.testing.server.session.runner.RunWithServerSession; @@ -88,7 +87,7 @@ protected void evaluateWithServerRunContext() throws Throwable { ServerSessionRunContexts.copyCurrent() .withSession(serverSession) .withSubject(currentSubject) // set the test subject explicitly in case it is different to the session subject - .withThreadLocal(UserId.CURRENT, BEANS.get(IAccessControlService.class).getUserId(currentSubject)) + .withUser(BEANS.get(IAccessControlService.class).getUser(currentSubject)) .withUserAgent(userAgent) .run(invoker); invoker.throwOnError(); diff --git a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/ServerSessionProviderWithCacheTest.java b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/ServerSessionProviderWithCacheTest.java index c3e626253ee..958f001c4c5 100644 --- a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/ServerSessionProviderWithCacheTest.java +++ b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/ServerSessionProviderWithCacheTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -27,10 +27,12 @@ import org.eclipse.scout.rt.platform.BeanMetaData; import org.eclipse.scout.rt.platform.IBean; import org.eclipse.scout.rt.platform.security.SimplePrincipal; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.Assertions; import org.eclipse.scout.rt.platform.util.CompositeObject; import org.eclipse.scout.rt.platform.util.SleepUtil; import org.eclipse.scout.rt.platform.util.collection.ConcurrentExpiringMap; +import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.server.context.ServerRunContexts; import org.eclipse.scout.rt.testing.platform.BeanTestingHelper; import org.eclipse.scout.rt.testing.platform.runner.PlatformTestRunner; @@ -91,9 +93,10 @@ public void after() { public void testNewSessionCacheKey() { ServerSessionProviderWithCache sessionProvider = createAndRegisterDefaultSessionProviderWithCache(); Subject subject = createSubject("anna"); + User user = BEANS.get(IAccessControlService.class).getUser(subject); assertNull(sessionProvider.newSessionCacheKey(null, null)); assertEquals(new CompositeObject("sessionId"), sessionProvider.newSessionCacheKey("sessionId", null)); - assertEquals(new CompositeObject("anna"), sessionProvider.newSessionCacheKey(null, subject)); + assertEquals(new CompositeObject(user), sessionProvider.newSessionCacheKey(null, subject)); assertEquals(new CompositeObject("sessionId"), sessionProvider.newSessionCacheKey("sessionId", subject)); } diff --git a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextChainTest.java b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextChainTest.java index 9b70ed6ba46..1132b949cf1 100644 --- a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextChainTest.java +++ b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextChainTest.java @@ -24,6 +24,7 @@ import org.eclipse.scout.rt.platform.nls.NlsLocale; import org.eclipse.scout.rt.platform.opentelemetry.OpenTelemetryContextProcessor; import org.eclipse.scout.rt.platform.security.SubjectProcessor; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.TransactionProcessor; import org.eclipse.scout.rt.platform.util.ThreadLocalProcessor; import org.eclipse.scout.rt.server.clientnotification.ClientNotificationCollector; @@ -80,66 +81,71 @@ protected CallableChain createCallableChain() { // overwrite to c = chainIterator.next(); assertEquals(SubjectProcessor.class, c.getClass()); - // 7. DiagnosticContextValueProcessor + // 7. ThreadLocalProcessor for User.CURRENT + c = chainIterator.next(); + assertEquals(ThreadLocalProcessor.class, c.getClass()); + assertSame(User.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); + + // 8. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("subject.principal.name", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 8. DiagnosticContextValueProcessor + // 9. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("opentelemetry.trace.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 9. DiagnosticContextValueProcessor + // 10. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("scout.correlation.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 10. ThreadLocalProcessor for NlsLocale.CURRENT + // 11. ThreadLocalProcessor for NlsLocale.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(NlsLocale.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 11. ThreadLocalProcessor for PropertyMap.CURRENT + // 12. ThreadLocalProcessor for PropertyMap.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(PropertyMap.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 12. ThreadLocalProcessor for ISession.CURRENT + // 13. ThreadLocalProcessor for ISession.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(ISession.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 13. DiagnosticContextValueProcessor + // 14. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("scout.user.name", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 14. DiagnosticContextValueProcessor + // 15. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("scout.session.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 15. OpenTelemetrySpanAttributeProcessor + // 16. OpenTelemetrySpanAttributeProcessor c = chainIterator.next(); assertEquals(OpenTelemetrySpanAttributeProcessor.class, c.getClass()); - // 16. ThreadLocalProcessor for ISession.CURRENT + // 17. ThreadLocalProcessor for ISession.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(UserAgent.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 17. ThreadLocalProcessor for ClientNodeId.CURRENT + // 18. ThreadLocalProcessor for ClientNodeId.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(IClientNodeId.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 18. ThreadLocalProcessor for TransactionalClientNotificationCollector.CURRENT + // 19. ThreadLocalProcessor for TransactionalClientNotificationCollector.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(ClientNotificationCollector.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 19. TransactionProcessor + // 20. TransactionProcessor c = chainIterator.next(); assertEquals(TransactionProcessor.class, c.getClass()); assertFalse(chainIterator.hasNext()); diff --git a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextProducerTest.java b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextProducerTest.java index 4820e877fd3..52c574563e4 100644 --- a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextProducerTest.java +++ b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextProducerTest.java @@ -17,10 +17,10 @@ import org.eclipse.scout.rt.platform.context.RunContext; import org.eclipse.scout.rt.platform.job.Jobs; import org.eclipse.scout.rt.platform.security.SimplePrincipal; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.concurrent.IRunnable; import org.eclipse.scout.rt.server.context.ServerRunContext; import org.eclipse.scout.rt.server.session.IServerSession; -import org.eclipse.scout.rt.shared.user.UserId; import org.eclipse.scout.rt.testing.platform.runner.PlatformTestRunner; import org.junit.Test; import org.junit.runner.RunWith; @@ -34,7 +34,7 @@ public void testProduceWithNoSession() { assertNull((((ServerSessionRunContext) RunContext.CURRENT.get()).getSession())); // ensure no previous session ServerSessionRunContextProducer producer = BEANS.get(ServerSessionRunContextProducer.class); ServerRunContext context = producer.produce(getSubjectForPrincipalName(username)); - assertEquals(username, context.call(UserId.CURRENT::get)); + assertEquals(username, context.call(User::currentUserId)); } @Test @@ -48,7 +48,7 @@ public void testProduceWithSessionDifferentUsername() { ServerSessionRunContext context = producer.produce(getSubjectForPrincipalName(secondUsername)); IServerSession session = context.getSession(); assertNotEquals(previousSession, session); - assertEquals(secondUsername, context.call(UserId.CURRENT::get)); + assertEquals(secondUsername, context.call(User::currentUserId)); }); } @@ -63,7 +63,7 @@ public void testProduceWithSessionSameUsername() { ServerSessionRunContext context = producer.produce(getSubjectForPrincipalName(secondUsername)); IServerSession session = context.getSession(); assertEquals(previousSession, session); - assertEquals(secondUsername, context.call(UserId.CURRENT::get)); + assertEquals(secondUsername, context.call(User::currentUserId)); }); } @@ -71,7 +71,7 @@ protected void runInRunContext(String principalName, IRunnable runnable) { assertNull((((ServerSessionRunContext) RunContext.CURRENT.get()).getSession())); // ensure no previous session ServerSessionRunContextProducer producer = BEANS.get(ServerSessionRunContextProducer.class); ServerSessionRunContext context = producer.produce(getSubjectForPrincipalName(principalName)); - assertEquals(principalName, context.call(UserId.CURRENT::get)); + assertEquals(principalName, context.call(User::currentUserId)); Jobs.schedule(runnable, Jobs.newInput().withRunContext(context)).awaitDoneAndGet(); } diff --git a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerDifferentSessionTest.java b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerDifferentSessionTest.java index ec89e27821e..c45978a68cf 100644 --- a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerDifferentSessionTest.java +++ b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerDifferentSessionTest.java @@ -15,14 +15,14 @@ import java.util.Set; import org.eclipse.scout.rt.platform.IgnoreBean; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.server.session.AbstractServerSession; import org.eclipse.scout.rt.server.session.IServerSession; import org.eclipse.scout.rt.server.session.ServerSessionProvider; -import org.eclipse.scout.rt.testing.server.session.runner.ServerTestRunnerDifferentSessionTest.JUnitServerSession1; import org.eclipse.scout.rt.shared.session.ISession; -import org.eclipse.scout.rt.shared.user.UserId; import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; +import org.eclipse.scout.rt.testing.server.session.runner.ServerTestRunnerDifferentSessionTest.JUnitServerSession1; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -41,7 +41,7 @@ public static void beforeClass() { s_serverSessions = new HashSet<>(); ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession1); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); s_transactions = new HashSet<>(); @@ -54,7 +54,7 @@ public static void beforeClass() { public void test1() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession1); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); @@ -66,7 +66,7 @@ public void test1() { public void test2() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession1); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); @@ -79,7 +79,7 @@ public void test2() { public void test3() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession2); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); @@ -91,7 +91,7 @@ public void test3() { public static void afterClass() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession1); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); diff --git a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerDifferentSubjectTest.java b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerDifferentSubjectTest.java index b27be93fcc1..ce7ef790d3c 100644 --- a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerDifferentSubjectTest.java +++ b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerDifferentSubjectTest.java @@ -15,13 +15,13 @@ import java.util.Set; import org.eclipse.scout.rt.platform.IgnoreBean; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.server.session.AbstractServerSession; import org.eclipse.scout.rt.server.session.IServerSession; -import org.eclipse.scout.rt.testing.server.session.runner.ServerTestRunnerDifferentSubjectTest.JUnitServerSession; import org.eclipse.scout.rt.shared.session.ISession; -import org.eclipse.scout.rt.shared.user.UserId; import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; +import org.eclipse.scout.rt.testing.server.session.runner.ServerTestRunnerDifferentSubjectTest.JUnitServerSession; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -40,7 +40,7 @@ public static void beforeClass() { s_serverSessions = new HashSet<>(); ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); s_transactions = new HashSet<>(); @@ -53,7 +53,7 @@ public static void beforeClass() { public void test1() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); @@ -66,7 +66,7 @@ public void test1() { public void test2() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession); - assertEquals("john", UserId.CURRENT.get()); + assertEquals("john", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); @@ -78,7 +78,7 @@ public void test2() { public static void afterClass() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); diff --git a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerSameSessionTest.java b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerSameSessionTest.java index 666742f9c3f..f5233072128 100644 --- a/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerSameSessionTest.java +++ b/org.eclipse.scout.rt.server.session.test/src/test/java/org/eclipse/scout/rt/testing/server/session/runner/ServerTestRunnerSameSessionTest.java @@ -15,13 +15,13 @@ import java.util.Set; import org.eclipse.scout.rt.platform.IgnoreBean; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.server.session.AbstractServerSession; import org.eclipse.scout.rt.server.session.IServerSession; -import org.eclipse.scout.rt.testing.server.session.runner.ServerTestRunnerSameSessionTest.JUnitServerSession; import org.eclipse.scout.rt.shared.session.ISession; -import org.eclipse.scout.rt.shared.user.UserId; import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; +import org.eclipse.scout.rt.testing.server.session.runner.ServerTestRunnerSameSessionTest.JUnitServerSession; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -40,7 +40,7 @@ public static void beforeClass() { s_serverSessions = new HashSet<>(); ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); s_transactions = new HashSet<>(); @@ -53,7 +53,7 @@ public static void beforeClass() { public void test1() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); @@ -65,7 +65,7 @@ public void test1() { public void test2() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); @@ -77,7 +77,7 @@ public void test2() { public static void afterClass() { ISession serverSession = IServerSession.CURRENT.get(); assertTrue(serverSession instanceof JUnitServerSession); - assertEquals("anna", UserId.CURRENT.get()); + assertEquals("anna", User.currentUserId()); s_serverSessions.add(serverSession); ITransaction transaction = ITransaction.CURRENT.get(); diff --git a/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/AbstractServerSession.java b/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/AbstractServerSession.java index df9df28d2d0..d36ba325cb6 100644 --- a/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/AbstractServerSession.java +++ b/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/AbstractServerSession.java @@ -21,6 +21,7 @@ import org.eclipse.scout.rt.platform.context.RunContext; import org.eclipse.scout.rt.platform.job.IFuture; import org.eclipse.scout.rt.platform.job.Jobs; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.event.FastListenerList; import org.eclipse.scout.rt.platform.util.event.IFastListenerList; import org.eclipse.scout.rt.rest.cancellation.RestRequestCancellationRegistry; @@ -31,14 +32,13 @@ import org.eclipse.scout.rt.shared.extension.IExtensibleObject; import org.eclipse.scout.rt.shared.extension.IExtension; import org.eclipse.scout.rt.shared.extension.ObjectExtensions; -import org.eclipse.scout.rt.shared.session.job.filter.future.SessionFutureFilter; import org.eclipse.scout.rt.shared.services.common.security.ILogoutService; import org.eclipse.scout.rt.shared.session.IGlobalSessionListener; import org.eclipse.scout.rt.shared.session.ISessionListener; import org.eclipse.scout.rt.shared.session.SessionData; import org.eclipse.scout.rt.shared.session.SessionEvent; import org.eclipse.scout.rt.shared.session.SessionMetricsHelper; -import org.eclipse.scout.rt.shared.user.UserId; +import org.eclipse.scout.rt.shared.session.job.filter.future.SessionFutureFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -137,7 +137,7 @@ public final void start(String sessionId) { m_active = true; fireSessionChangedEvent(new SessionEvent(this, SessionEvent.TYPE_STARTED)); - LOG.info("Server session started [session={}, user={}]", this, UserId.CURRENT.get()); + LOG.info("Server session started [session={}, user={}]", this, User.currentUserId()); } /** @@ -223,7 +223,7 @@ protected void inactivateSession() { m_stopping = false; fireSessionChangedEvent(new SessionEvent(this, SessionEvent.TYPE_STOPPED)); m_sessionMetrics.sessionDestroyed(SESSION_TYPE); - LOG.info("Server session stopped [session={}, user={}]", this, UserId.CURRENT.get()); + LOG.info("Server session stopped [session={}, user={}]", this, User.currentUserId()); } } diff --git a/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/ServerSessionProviderWithCache.java b/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/ServerSessionProviderWithCache.java index c43edf59517..557cbc7d368 100644 --- a/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/ServerSessionProviderWithCache.java +++ b/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/ServerSessionProviderWithCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -9,7 +9,7 @@ */ package org.eclipse.scout.rt.server.session; -import static org.eclipse.scout.rt.platform.util.Assertions.assertNotNull; +import static org.eclipse.scout.rt.platform.util.Assertions.*; import java.util.concurrent.TimeUnit; @@ -18,6 +18,7 @@ import org.eclipse.scout.rt.platform.BEANS; import org.eclipse.scout.rt.platform.config.AbstractPositiveLongConfigProperty; import org.eclipse.scout.rt.platform.config.CONFIG; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.CompositeObject; import org.eclipse.scout.rt.platform.util.collection.ConcurrentExpiringMap; import org.eclipse.scout.rt.security.IAccessControlService; @@ -152,7 +153,9 @@ protected CompositeObject newSessionCacheKey(final String sessionId, final Subje return new CompositeObject(sessionId); } if (subject != null) { - return new CompositeObject(BEANS.get(IAccessControlService.class).getUserId(subject)); + User user = BEANS.get(IAccessControlService.class).getUser(subject); + assertTrue(user.isReadOnly(), "User must be read only before using it as cache key"); + return new CompositeObject(user); } return null; } diff --git a/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContext.java b/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContext.java index dfe712ee20d..7e6686ef9b2 100644 --- a/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContext.java +++ b/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -21,6 +21,7 @@ import org.eclipse.scout.rt.platform.context.RunContext; import org.eclipse.scout.rt.platform.context.RunMonitor; import org.eclipse.scout.rt.platform.logger.DiagnosticContextValueProcessor.IDiagnosticContextValueProvider; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.platform.transaction.ITransactionMember; import org.eclipse.scout.rt.platform.transaction.TransactionScope; @@ -77,6 +78,12 @@ public ServerSessionRunContext withSubject(final Subject subject) { return this; } + @Override + public ServerSessionRunContext withUser(final User user) { + super.withUser(user); + return this; + } + @Override public ServerSessionRunContext withLocale(final Locale locale) { super.withLocale(locale); diff --git a/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextProducer.java b/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextProducer.java index f4c80dca8b9..69092feffcf 100644 --- a/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextProducer.java +++ b/org.eclipse.scout.rt.server.session/src/main/java/org/eclipse/scout/rt/server/session/context/ServerSessionRunContextProducer.java @@ -18,7 +18,6 @@ import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.server.session.IServerSession; import org.eclipse.scout.rt.server.session.ServerSessionProviderWithCache; -import org.eclipse.scout.rt.shared.user.UserId; /** * Producer for {@link ServerSessionRunContext} based on {@link Subject}. @@ -29,7 +28,7 @@ public class ServerSessionRunContextProducer { public ServerSessionRunContext produce(Subject subject) { final ServerSessionRunContext serverRunContext = ServerSessionRunContexts.copyCurrent(true) .withSubject(subject) - .withThreadLocal(UserId.CURRENT, BEANS.get(IAccessControlService.class).getUserId(subject)) + .withUser(BEANS.get(IAccessControlService.class).getUser(subject)) .withTransactionScope(TransactionScope.REQUIRES_NEW); // ensure that the session belongs to the specified subject diff --git a/org.eclipse.scout.rt.server.test/src/main/java/org/eclipse/scout/rt/testing/server/runner/statement/ServerSubjectStatement.java b/org.eclipse.scout.rt.server.test/src/main/java/org/eclipse/scout/rt/testing/server/runner/statement/ServerSubjectStatement.java index 2b2bfccce7d..0269c652dcd 100644 --- a/org.eclipse.scout.rt.server.test/src/main/java/org/eclipse/scout/rt/testing/server/runner/statement/ServerSubjectStatement.java +++ b/org.eclipse.scout.rt.server.test/src/main/java/org/eclipse/scout/rt/testing/server/runner/statement/ServerSubjectStatement.java @@ -13,11 +13,11 @@ import org.eclipse.scout.rt.platform.BEANS; import org.eclipse.scout.rt.platform.context.RunContext; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.server.context.ServerRunContext; import org.eclipse.scout.rt.shared.session.SessionId; import org.eclipse.scout.rt.shared.ui.UserAgents; -import org.eclipse.scout.rt.shared.user.UserId; import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; import org.eclipse.scout.rt.testing.platform.runner.statement.SubjectStatement; import org.junit.runners.model.Statement; @@ -25,7 +25,7 @@ /** * Statement to execute the following statements under a particular user given by a {@link Subject}. *

- * Additionally sets the {@link UserId} for the given subject, defines a + * Additionally sets the {@link User} for the given subject, defines a * random {@link SessionId} and sets the default user agent. * * @see RunWithSubject @@ -38,15 +38,17 @@ public ServerSubjectStatement(final Statement next, final RunWithSubject annotat @Override protected RunContext createRunContext() { - String userId = BEANS.get(IAccessControlService.class).getUserId(getSubject()); - ServerRunContext context = ((ServerRunContext) super.createRunContext()) - .withThreadLocal(UserId.CURRENT, userId) .withThreadLocal(SessionId.CURRENT, SessionId.randomSessionId()) // set random session id so that server tests trying to access session id can run without a client session .withUserAgent(UserAgents.createDefault()); return initRunContext(context); } + @Override + protected User createUser() { + return BEANS.get(IAccessControlService.class).getUser(getSubject()); + } + protected ServerRunContext initRunContext(ServerRunContext runContext) { return runContext; } diff --git a/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextChainTest.java b/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextChainTest.java index a1662826c56..89f7a6fab88 100644 --- a/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextChainTest.java +++ b/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextChainTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -24,6 +24,7 @@ import org.eclipse.scout.rt.platform.nls.NlsLocale; import org.eclipse.scout.rt.platform.opentelemetry.OpenTelemetryContextProcessor; import org.eclipse.scout.rt.platform.security.SubjectProcessor; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.TransactionProcessor; import org.eclipse.scout.rt.platform.util.ThreadLocalProcessor; import org.eclipse.scout.rt.server.clientnotification.ClientNotificationCollector; @@ -79,61 +80,66 @@ protected CallableChain createCallableChain() { // overwrite to c = chainIterator.next(); assertEquals(SubjectProcessor.class, c.getClass()); - // 7. DiagnosticContextValueProcessor + // 7. ThreadLocalProcessor for User.CURRENT + c = chainIterator.next(); + assertEquals(ThreadLocalProcessor.class, c.getClass()); + assertSame(User.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); + + // 8. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("subject.principal.name", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 8. DiagnosticContextValueProcessor + // 9. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("opentelemetry.trace.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 9. DiagnosticContextValueProcessor + // 10. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("scout.correlation.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 10. ThreadLocalProcessor for NlsLocale.CURRENT + // 11. ThreadLocalProcessor for NlsLocale.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(NlsLocale.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 11. ThreadLocalProcessor for PropertyMap.CURRENT + // 12. ThreadLocalProcessor for PropertyMap.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(PropertyMap.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 12. DiagnosticContextValueProcessor + // 13. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("scout.user.name", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 13. DiagnosticContextValueProcessor + // 14. DiagnosticContextValueProcessor c = chainIterator.next(); assertEquals(DiagnosticContextValueProcessor.class, c.getClass()); assertEquals("scout.session.id", ((DiagnosticContextValueProcessor) c).getMdcKey()); - // 14. OpenTelemetrySpanAttributeProcessor + // 15. OpenTelemetrySpanAttributeProcessor c = chainIterator.next(); assertEquals(OpenTelemetrySpanAttributeProcessor.class, c.getClass()); - // 15. ThreadLocalProcessor for ISession.CURRENT + // 16. ThreadLocalProcessor for ISession.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(UserAgent.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 16. ThreadLocalProcessor for ClientNodeId.CURRENT + // 17. ThreadLocalProcessor for ClientNodeId.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(IClientNodeId.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 17. ThreadLocalProcessor for TransactionalClientNotificationCollector.CURRENT + // 18. ThreadLocalProcessor for TransactionalClientNotificationCollector.CURRENT c = chainIterator.next(); assertEquals(ThreadLocalProcessor.class, c.getClass()); assertSame(ClientNotificationCollector.CURRENT, ((ThreadLocalProcessor) c).getThreadLocal()); - // 18. TransactionProcessor + // 19. TransactionProcessor c = chainIterator.next(); assertEquals(TransactionProcessor.class, c.getClass()); assertFalse(chainIterator.hasNext()); diff --git a/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextUserIdTest.java b/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextUserTest.java similarity index 75% rename from org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextUserIdTest.java rename to org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextUserTest.java index 034574efb86..89db79266d5 100644 --- a/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextUserIdTest.java +++ b/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/context/ServerRunContextUserTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2023 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -11,7 +11,8 @@ import static org.eclipse.scout.rt.platform.util.Assertions.assertEquals; -import org.eclipse.scout.rt.shared.user.UserId; +import org.eclipse.scout.rt.platform.BEANS; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.testing.platform.runner.RunWithSubject; import org.eclipse.scout.rt.testing.server.runner.ServerTestRunner; import org.junit.Test; @@ -19,7 +20,7 @@ @RunWith(ServerTestRunner.class) @RunWithSubject("john") -public class ServerRunContextUserIdTest { +public class ServerRunContextUserTest { @Test public void testBasic() { @@ -36,7 +37,7 @@ public void testNested() { public void testChangingUser() { String otherUserId = "anna"; ServerRunContexts.copyCurrent() - .withThreadLocal(UserId.CURRENT, otherUserId) + .withUser(BEANS.get(User.class).withUserId(otherUserId).setReadOnly()) .run(() -> assertUserId(otherUserId)); } @@ -44,12 +45,12 @@ public void testChangingUser() { public void testChangingUserNested() { String otherUserId = "anna"; ServerRunContexts.copyCurrent() - .withThreadLocal(UserId.CURRENT, otherUserId) + .withUser(BEANS.get(User.class).withUserId(otherUserId).setReadOnly()) .run(() -> ServerRunContexts.copyCurrent() .run(() -> assertUserId(otherUserId))); } protected void assertUserId(String expectedUserId) { - assertEquals(expectedUserId, UserId.CURRENT.get()); + assertEquals(expectedUserId, User.currentUserId()); } } diff --git a/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/services/common/security/AccessControlServiceTest.java b/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/services/common/security/AccessControlServiceTest.java index f8b1abe840f..5c464cc0a6e 100644 --- a/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/services/common/security/AccessControlServiceTest.java +++ b/org.eclipse.scout.rt.server.test/src/test/java/org/eclipse/scout/rt/server/services/common/security/AccessControlServiceTest.java @@ -9,9 +9,12 @@ */ package org.eclipse.scout.rt.server.services.common.security; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.*; import java.util.List; +import java.util.regex.Pattern; + +import javax.security.auth.Subject; import org.eclipse.scout.rt.dataobject.id.NodeId; import org.eclipse.scout.rt.platform.BEANS; @@ -22,6 +25,8 @@ import org.eclipse.scout.rt.platform.cache.InvalidateCacheNotification; import org.eclipse.scout.rt.platform.exception.ProcessingException; import org.eclipse.scout.rt.platform.internal.BeanInstanceUtil; +import org.eclipse.scout.rt.platform.security.SimplePrincipal; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.security.AbstractAccessControlService; import org.eclipse.scout.rt.security.DefaultPermissionCollection; @@ -71,6 +76,41 @@ public void after() { BeanTestingHelper.get().unregisterBeans(m_registerServices); } + @Test + public void testGetUser_null() { + assertNull(m_accessControlService.getUser(null)); + } + + @Test + public void testGetUser_empty() { + User user = m_accessControlService.getUser(new Subject()); + assertNull(user.getUserId()); + } + + @Test + public void testGetUser_subject() { + Subject subject = new Subject(); + subject.getPrincipals().add(new SimplePrincipal("john")); + User user = m_accessControlService.getUser(subject); + assertEquals("john", user.getUserId()); + assertTrue(user.isReadOnly()); + } + + @Test + public void testGetUser_subjectUserIdPattern() { + Subject subject = new Subject(); + subject.getPrincipals().add(new SimplePrincipal("john@foo.bar")); + try { + m_accessControlService.setUserIdSearchPatterns(Pattern.compile("(.*)@foo.bar")); + User user = m_accessControlService.getUser(subject); + assertEquals("john", user.getUserId()); + assertTrue(user.isReadOnly()); + } + finally { + m_accessControlService.setUserIdSearchPatterns((Pattern[]) null); + } + } + /** * Tests that a client notification of {@link InvalidateCacheNotification} is sent when the cache is cleared: * {@link IAccessControlService#clearCache()} @@ -115,15 +155,10 @@ private List getNotifications() { * An access control service with {@link TestPermission1} for testing */ @IgnoreBean - private static class TestAccessControlService extends AbstractAccessControlService { - - @Override - protected String getCurrentUserCacheKey() { - return getUserIdOfCurrentSubject(); - } + private static class TestAccessControlService extends AbstractAccessControlService { @Override - protected IPermissionCollection execLoadPermissions(String userId) { + protected IPermissionCollection execLoadPermissions(User user) { DefaultPermissionCollection permissions = BEANS.get(DefaultPermissionCollection.class); permissions.add(new TestPermission1(), PermissionLevel.ALL); permissions.add(new RemoteServiceAccessPermission("*.shared.*", "*"), PermissionLevel.ALL); @@ -132,11 +167,16 @@ protected IPermissionCollection execLoadPermissions(String userId) { } @Override - protected ICacheBuilder createCacheBuilder() { + protected ICacheBuilder createCacheBuilder() { return super.createCacheBuilder() .withCacheId(ACCESS_CONTROL_SERVICE_CACHE_ID + ".for.test") .withReplaceIfExists(true); } + + @Override + protected void setUserIdSearchPatterns(Pattern... patterns) { + super.setUserIdSearchPatterns(patterns); + } } @IgnoreBean diff --git a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/context/ServerRunContext.java b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/context/ServerRunContext.java index 0b20c788110..d2ff7b3ac59 100644 --- a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/context/ServerRunContext.java +++ b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/context/ServerRunContext.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -23,6 +23,7 @@ import org.eclipse.scout.rt.platform.context.RunMonitor; import org.eclipse.scout.rt.platform.logger.DiagnosticContextValueProcessor; import org.eclipse.scout.rt.platform.logger.DiagnosticContextValueProcessor.IDiagnosticContextValueProvider; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.platform.transaction.ITransactionMember; import org.eclipse.scout.rt.platform.transaction.TransactionScope; @@ -80,6 +81,12 @@ public ServerRunContext withSubject(final Subject subject) { return this; } + @Override + public ServerRunContext withUser(final User user) { + super.withUser(user); + return this; + } + @Override public ServerRunContext withLocale(final Locale locale) { super.withLocale(locale); diff --git a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/bookmark/FileSystemBookmarkStorageService.java b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/bookmark/FileSystemBookmarkStorageService.java index 1990835dbf8..bdce58aebe9 100644 --- a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/bookmark/FileSystemBookmarkStorageService.java +++ b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/bookmark/FileSystemBookmarkStorageService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -19,6 +19,7 @@ import org.eclipse.scout.rt.platform.BEANS; import org.eclipse.scout.rt.platform.exception.ProcessingException; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.serialization.SerializationUtility; import org.eclipse.scout.rt.security.ACCESS; import org.eclipse.scout.rt.shared.security.PublishUserBookmarkPermission; @@ -26,7 +27,6 @@ import org.eclipse.scout.rt.shared.services.common.bookmark.BookmarkFolder; import org.eclipse.scout.rt.shared.services.common.file.IRemoteFileService; import org.eclipse.scout.rt.shared.services.common.file.RemoteFile; -import org.eclipse.scout.rt.shared.user.UserId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,7 +65,7 @@ public void publishBookmarkData(BookmarkFolder publishFolder, Map, ClusterNodeStatusInfo> m_messageStatusMap = new ConcurrentHashMap<>(); private final Subject m_subject; - private final String m_userId; + private final LazyValue m_user = new LazyValue<>(this::createUser); private volatile ISubscription m_subscription; private final Object m_subscriptionLock = new Object(); @@ -66,17 +67,20 @@ public class ClusterSynchronizationService implements IClusterSynchronizationSer public ClusterSynchronizationService() { m_subject = CONFIG.getPropertyValue(ClusterSyncUserProperty.class); - m_userId = BEANS.get(IAccessControlService.class).getUserId(m_subject); } - public String getUserId() { - return m_userId; + public User getUser() { + return m_user.get(); } public Subject getSubject() { return m_subject; } + protected User createUser() { + return BEANS.get(IAccessControlService.class).getUser(getSubject()); + } + @Override public IClusterNodeStatusInfo getStatusInfo() { return m_statusInfo.getStatus(); @@ -190,7 +194,7 @@ private void publishInternal(List messages) { @Override public IClusterNotificationProperties getNotificationProperties() { - String userId = StringUtility.emptyIfNull(UserId.CURRENT.get()); + String userId = StringUtility.emptyIfNull(User.currentUserId()); return new ClusterNotificationProperties(m_nodeId, userId); } @@ -220,8 +224,8 @@ public void onMessage(IMessage message) { protected ServerRunContext createRunContext() { return ServerRunContexts.empty() - .withSubject(m_subject) - .withThreadLocal(UserId.CURRENT, m_userId); + .withSubject(getSubject()) + .withUser(getUser()); } /** diff --git a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/code/CodeTypeInvalidationNotificationListener.java b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/code/CodeTypeInvalidationNotificationListener.java index 03b9367903c..27bf629c6e2 100644 --- a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/code/CodeTypeInvalidationNotificationListener.java +++ b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/code/CodeTypeInvalidationNotificationListener.java @@ -30,11 +30,11 @@ import org.eclipse.scout.rt.platform.CreateImmediately; import org.eclipse.scout.rt.platform.cache.ICacheEntryFilter; import org.eclipse.scout.rt.platform.cache.ICacheInvalidationListener; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.AbstractTransactionMember; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.platform.transaction.ITransactionMember; import org.eclipse.scout.rt.platform.transaction.TransactionScope; -import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.server.context.ServerRunContexts; import org.eclipse.scout.rt.shared.services.common.code.ApiExposedCodeTypeDoProvider; import org.eclipse.scout.rt.shared.services.common.code.CodeService; @@ -160,7 +160,7 @@ public void notifyExposedCodeTypeUpdate(List codeTypes) { } protected String getUserId() { - return BEANS.get(IAccessControlService.class).getUserIdOfCurrentSubject(); + return User.currentUserId(); } } } diff --git a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/security/PermissionsInvalidationNotificationListener.java b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/security/PermissionsInvalidationNotificationListener.java index c6a35209173..724add2523e 100644 --- a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/security/PermissionsInvalidationNotificationListener.java +++ b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/services/common/security/PermissionsInvalidationNotificationListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -25,12 +25,12 @@ import org.eclipse.scout.rt.platform.cache.ICacheEntryFilter; import org.eclipse.scout.rt.platform.cache.ICacheInvalidationListener; import org.eclipse.scout.rt.platform.cache.KeyCacheEntryFilter; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.transaction.AbstractTransactionMember; import org.eclipse.scout.rt.platform.transaction.ITransaction; import org.eclipse.scout.rt.platform.transaction.ITransactionMember; import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.security.IPermissionCollection; -import org.eclipse.scout.rt.server.context.ServerRunContexts; /** * Listens for permission cache invalidation and notifies the UI to update its cache. @@ -38,7 +38,7 @@ @ApplicationScoped @CreateImmediately @SuppressWarnings("unchecked") -public class PermissionsInvalidationNotificationListener implements ICacheInvalidationListener { +public class PermissionsInvalidationNotificationListener implements ICacheInvalidationListener { @PostConstruct protected void init() { @@ -51,7 +51,7 @@ protected void destroy() { } @Override - public void invalidated(ICacheEntryFilter filter, boolean propagate) { + public void invalidated(ICacheEntryFilter filter, boolean propagate) { if (filter == null) { return; // nothing has been invalidated } @@ -62,7 +62,7 @@ public void invalidated(ICacheEntryFilter filter, transaction.registerMemberIfAbsentAndNotCancelled(PermissionsUiNotificationTransactionMember.TRANSACTION_MEMBER_ID, id -> createTransactionMember(filter)); } - protected PermissionsUiNotificationTransactionMember createTransactionMember(ICacheEntryFilter filter) { + protected PermissionsUiNotificationTransactionMember createTransactionMember(ICacheEntryFilter filter) { return new PermissionsUiNotificationTransactionMember(filter); } @@ -75,9 +75,9 @@ public static class PermissionsUiNotificationTransactionMember extends AbstractT public static final String TOPIC = "permissionsUpdate"; public static final String TRANSACTION_MEMBER_ID = "permissionsUiNotification.transactionMemberId"; - private final ICacheEntryFilter m_filter; + private final ICacheEntryFilter m_filter; - public PermissionsUiNotificationTransactionMember(ICacheEntryFilter filter) { + public PermissionsUiNotificationTransactionMember(ICacheEntryFilter filter) { super(TRANSACTION_MEMBER_ID); m_filter = filter; } @@ -87,22 +87,27 @@ public boolean needsCommit() { return true; } + protected ICacheEntryFilter getFilter() { + return m_filter; + } + @Override public void commitPhase2() { UiNotificationRegistry uiNotificationRegistry = BEANS.get(UiNotificationRegistry.class); long reloadDelayWindow = uiNotificationRegistry.computeNotificationHandlerDelayWindow(TOPIC); PermissionUpdateMessageDo updateDo = BEANS.get(PermissionUpdateMessageDo.class).withReloadDelayWindow(reloadDelayWindow); + sendUiNotification(uiNotificationRegistry, updateDo); + } + + protected void sendUiNotification(UiNotificationRegistry uiNotificationRegistry, PermissionUpdateMessageDo updateDo) { if (m_filter instanceof KeyCacheEntryFilter) { - IAccessControlService accessControlService = BEANS.get(IAccessControlService.class); // only Permissions of specific users are invalidated: only inform the affected clients - Set cacheKeys = ((KeyCacheEntryFilter) m_filter).getKeys(); - - // create new run context to ensure new transaction is available in case getUserIdForCacheKey requires one to map the cacheKey to the userId - ServerRunContexts.copyCurrent() - .run(() -> cacheKeys.stream() - .map(accessControlService::getUserIdForCacheKey) - .filter(Objects::nonNull) - .forEach(userId -> uiNotificationRegistry.put(TOPIC, userId, updateDo, noTransaction().withPublishOverCluster(false)))); + Set cacheKeys = ((KeyCacheEntryFilter) m_filter).getKeys(); + + cacheKeys.stream() + .map(User::getUserId) + .filter(Objects::nonNull) + .forEach(userId -> uiNotificationRegistry.put(TOPIC, userId, updateDo, noTransaction().withPublishOverCluster(false))); } else { // update for all clients diff --git a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/servicetunnel/ServiceOperationInvoker.java b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/servicetunnel/ServiceOperationInvoker.java index 83007f807d5..6916e7a1954 100644 --- a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/servicetunnel/ServiceOperationInvoker.java +++ b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/servicetunnel/ServiceOperationInvoker.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -22,6 +22,7 @@ import org.eclipse.scout.rt.platform.exception.PlatformException; import org.eclipse.scout.rt.platform.exception.ProcessingException; import org.eclipse.scout.rt.platform.exception.VetoException; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.serialization.SerializationUtility; import org.eclipse.scout.rt.platform.service.IService; import org.eclipse.scout.rt.platform.text.TEXTS; @@ -33,7 +34,6 @@ import org.eclipse.scout.rt.shared.servicetunnel.RemoteServiceWithoutAuthorization; import org.eclipse.scout.rt.shared.servicetunnel.ServiceTunnelRequest; import org.eclipse.scout.rt.shared.servicetunnel.ServiceTunnelResponse; -import org.eclipse.scout.rt.shared.user.UserId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,7 +78,7 @@ public ServiceTunnelResponse invoke(final RunContext runContext, final ServiceTu protected ServiceTunnelResponse invokeInternal(ServiceTunnelRequest serviceReq) throws ClassNotFoundException { if (LOG.isDebugEnabled()) { - String userId = StringUtility.emptyIfNull(UserId.CURRENT.get()); + String userId = StringUtility.emptyIfNull(User.currentUserId()); LOG.debug("started {}.{} by {} at {}", serviceReq.getServiceInterfaceClassName(), serviceReq.getOperation(), userId, new Date()); } ServiceTunnelResponse serviceRes = null; diff --git a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/session/UserIdSharedVariableContributor.java b/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/session/UserIdSharedVariableContributor.java deleted file mode 100644 index afd354767b0..00000000000 --- a/org.eclipse.scout.rt.server/src/main/java/org/eclipse/scout/rt/server/session/UserIdSharedVariableContributor.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.scout.rt.server.session; - -import static org.eclipse.scout.rt.shared.ISessionVariable.SHARED_CONTEXT_USER_ID; - -import java.util.Map; - -import org.eclipse.scout.rt.shared.user.UserId; - -public class UserIdSharedVariableContributor implements IInitialSharedVariableContributor { - - @Override - public void contribute(Map variables) { - variables.put(SHARED_CONTEXT_USER_ID, UserId.CURRENT.get()); - } -} diff --git a/org.eclipse.scout.rt.shared.test/src/main/java/org/eclipse/scout/rt/testing/shared/AllAccessControlService.java b/org.eclipse.scout.rt.shared.test/src/main/java/org/eclipse/scout/rt/testing/shared/AllAccessControlService.java index 25bf481ab29..2b761e1b4ce 100644 --- a/org.eclipse.scout.rt.shared.test/src/main/java/org/eclipse/scout/rt/testing/shared/AllAccessControlService.java +++ b/org.eclipse.scout.rt.shared.test/src/main/java/org/eclipse/scout/rt/testing/shared/AllAccessControlService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -13,22 +13,17 @@ import org.eclipse.scout.rt.platform.BEANS; import org.eclipse.scout.rt.platform.Order; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.security.AbstractAccessControlService; import org.eclipse.scout.rt.security.AllPermissionCollection; import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.security.IPermissionCollection; -import org.eclipse.scout.rt.shared.user.UserId; /** * {@link IAccessControlService} service for testing using {@link AllPermission} */ @Order(4500) -public class AllAccessControlService extends AbstractAccessControlService { - - @Override - protected String getCurrentUserCacheKey() { - return UserId.CURRENT.get(); - } +public class AllAccessControlService extends AbstractAccessControlService { @Override public IPermissionCollection getPermissions() { @@ -36,7 +31,7 @@ public IPermissionCollection getPermissions() { } @Override - protected IPermissionCollection execLoadPermissions(String userId) { + protected IPermissionCollection execLoadPermissions(User user) { throw new UnsupportedOperationException(); } } diff --git a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/ISessionVariable.java b/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/ISessionVariable.java index a1eb536e83e..197fb92be2b 100644 --- a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/ISessionVariable.java +++ b/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/ISessionVariable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -11,5 +11,4 @@ public interface ISessionVariable { - String SHARED_CONTEXT_USER_ID = "userId"; } diff --git a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/authentication/DefaultAuthTokenSigner.java b/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/authentication/DefaultAuthTokenSigner.java index c05d0db1141..56398051e89 100644 --- a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/authentication/DefaultAuthTokenSigner.java +++ b/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/authentication/DefaultAuthTokenSigner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -21,6 +21,7 @@ import org.eclipse.scout.rt.platform.security.JwtPrincipal; import org.eclipse.scout.rt.platform.security.SamlPrincipal; import org.eclipse.scout.rt.platform.security.SecurityUtility; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.platform.util.CollectionUtility; import org.eclipse.scout.rt.platform.util.StringUtility; import org.eclipse.scout.rt.security.IAccessControlService; @@ -44,7 +45,9 @@ protected byte[] getPrivateKey() { } protected String getDefaultUserId() { - return BEANS.get(IAccessControlService.class).getUserIdOfCurrentSubject(); + // user thread local is set after client session has been created. + // for requests before client session has been created, load user id directly from backend instead. + return User.currentUserId() != null ? User.currentUserId() : BEANS.get(IAccessControlService.class).getUser(Subject.current()).getUserId(); } /** diff --git a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/logging/UserIdContextValueProvider.java b/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/logging/UserIdContextValueProvider.java index e9cea95b98c..ef9629543b6 100644 --- a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/logging/UserIdContextValueProvider.java +++ b/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/logging/UserIdContextValueProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -12,11 +12,11 @@ import org.eclipse.scout.rt.platform.ApplicationScoped; import org.eclipse.scout.rt.platform.logger.DiagnosticContextValueProcessor; import org.eclipse.scout.rt.platform.logger.DiagnosticContextValueProcessor.IDiagnosticContextValueProvider; -import org.eclipse.scout.rt.shared.user.UserId; +import org.eclipse.scout.rt.platform.security.User; import org.slf4j.MDC; /** - * This class provides the {@link UserId#CURRENT} to be set into the diagnostic context map for + * This class provides the {@link User#getUserId()} to be set into the diagnostic context map for * logging purpose. * * @see #KEY @@ -35,6 +35,6 @@ public String key() { @Override public String value() { - return UserId.CURRENT.get(); + return User.currentUserId(); } } diff --git a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/opentelemetry/OpenTelemetrySpanAttributeProcessor.java b/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/opentelemetry/OpenTelemetrySpanAttributeProcessor.java index 013bc3a1387..220733109ab 100644 --- a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/opentelemetry/OpenTelemetrySpanAttributeProcessor.java +++ b/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/opentelemetry/OpenTelemetrySpanAttributeProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG + * Copyright (c) 2010, 2026 BSI Business Systems Integration AG * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -15,7 +15,7 @@ import org.eclipse.scout.rt.platform.config.CONFIG; import org.eclipse.scout.rt.platform.opentelemetry.OpenTelemetryProperties.OpenTelemetrySpanAttributeProcessorEnabledProperty; import org.eclipse.scout.rt.platform.opentelemetry.OpenTelemetryProperties.OpenTelemetryTracingEnabledProperty; -import org.eclipse.scout.rt.shared.user.UserId; +import org.eclipse.scout.rt.platform.security.User; import io.opentelemetry.api.trace.Span; @@ -52,6 +52,6 @@ protected String getUserIdKey() { } protected String getUserIdValue() { - return UserId.CURRENT.get(); + return User.currentUserId(); } } diff --git a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/user/UserId.java b/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/user/UserId.java deleted file mode 100644 index daa53783f0c..00000000000 --- a/org.eclipse.scout.rt.shared/src/main/java/org/eclipse/scout/rt/shared/user/UserId.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2010, 2025 BSI Business Systems Integration AG - * - * This program and the accompanying materials are made - * available under the terms of the Eclipse Public License 2.0 - * which is available at https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.scout.rt.shared.user; - -import javax.security.auth.Subject; - -import org.eclipse.scout.rt.security.IAccessControlService; - -public final class UserId { - - private UserId() { - } - - /** - * The {@code userId} which is currently associated with the current thread. The {@code userId} is extracted from current - * {@link Subject} using {@link IAccessControlService#getUserIdOfCurrentSubject()} and usually corresponds to the username the of logged-in user. - */ - public static final ThreadLocal CURRENT = new ThreadLocal<>(); -} diff --git a/scout-hellojs-app/src/main/resources/archetype-resources/__rootArtifactId__.core/src/main/java/core/AccessControlService.java b/scout-hellojs-app/src/main/resources/archetype-resources/__rootArtifactId__.core/src/main/java/core/AccessControlService.java index 9ffbf568b5c..ff5440f5038 100644 --- a/scout-hellojs-app/src/main/resources/archetype-resources/__rootArtifactId__.core/src/main/java/core/AccessControlService.java +++ b/scout-hellojs-app/src/main/resources/archetype-resources/__rootArtifactId__.core/src/main/java/core/AccessControlService.java @@ -3,19 +3,14 @@ #set( $symbol_escape = '\' ) package ${package}.core; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.security.AbstractAccessControlService; import org.eclipse.scout.rt.security.IPermissionCollection; -import org.eclipse.scout.rt.shared.user.UserId; -public class AccessControlService extends AbstractAccessControlService { +public class AccessControlService extends AbstractAccessControlService { @Override - protected String getCurrentUserCacheKey() { - return UserId.CURRENT.get(); - } - - @Override - protected IPermissionCollection execLoadPermissions(String userId) { + protected IPermissionCollection execLoadPermissions(User user) { return null; } } diff --git a/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.client/src/main/java/client/Desktop.java b/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.client/src/main/java/client/Desktop.java index 1110e098927..cbfba02d8f4 100644 --- a/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.client/src/main/java/client/Desktop.java +++ b/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.client/src/main/java/client/Desktop.java @@ -101,7 +101,7 @@ protected String getConfiguredIconId() { @Override protected String getConfiguredText() { - String userId = BEANS.get(IAccessControlService.class).getUserIdOfCurrentSubject(); + String userId = User.currentUserId(); return StringUtility.uppercaseFirst(userId); } diff --git a/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.server/src/main/java/server/helloworld/HelloWorldService.java b/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.server/src/main/java/server/helloworld/HelloWorldService.java index 2fbb21c4815..a8703fb3522 100644 --- a/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.server/src/main/java/server/helloworld/HelloWorldService.java +++ b/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.server/src/main/java/server/helloworld/HelloWorldService.java @@ -3,7 +3,7 @@ #set( $symbol_escape = '\' ) package ${package}.server.helloworld; -import org.eclipse.scout.rt.shared.user.UserId; +import org.eclipse.scout.rt.platform.security.User; import ${package}.shared.helloworld.HelloWorldFormData; import ${package}.shared.helloworld.IHelloWorldService; @@ -16,7 +16,7 @@ public class HelloWorldService implements IHelloWorldService { @Override public HelloWorldFormData load(HelloWorldFormData input) { StringBuilder msg = new StringBuilder(); - msg.append("Hello ").append(UserId.CURRENT.get()).append('!'); + msg.append("Hello ").append(User.currentUserId()).append('!'); input.getMessage().setValue(msg.toString()); return input; } diff --git a/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.shared/src/main/java/shared/security/AccessControlService.java b/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.shared/src/main/java/shared/security/AccessControlService.java index fdc4b431dc7..62ff808d78f 100644 --- a/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.shared/src/main/java/shared/security/AccessControlService.java +++ b/scout-helloworld-app/src/main/resources/archetype-resources/__rootArtifactId__.shared/src/main/java/shared/security/AccessControlService.java @@ -3,29 +3,23 @@ #set( $symbol_escape = '\' ) package ${package}.shared.security; +import org.eclipse.scout.rt.platform.security.User; import org.eclipse.scout.rt.security.AbstractAccessControlService; import org.eclipse.scout.rt.security.IAccessControlService; import org.eclipse.scout.rt.security.IPermissionCollection; -import org.eclipse.scout.rt.shared.user.UserId; /** - * {@link IAccessControlService} service that uses {@link UserId#CURRENT} as internal cache key required by - * {@link AbstractAccessControlService} implementation. + * Default {@link IAccessControlService} implementation. *

* Replace this service at server side to load permission collection. It is not required to implement - * {@link #execLoadPermissions(String)} at client side. + * {@link #execLoadPermissions(User)} at client side. * * @author ${userName} */ -public class AccessControlService extends AbstractAccessControlService { +public class AccessControlService extends AbstractAccessControlService { @Override - protected String getCurrentUserCacheKey() { - return UserId.CURRENT.get(); - } - - @Override - protected IPermissionCollection execLoadPermissions(String userId) { + protected IPermissionCollection execLoadPermissions(User user) { return null; } }