Thanks Youriy.
I tried both solutions but none works for our use case, I’m writing tests checking that specific constraints based on authenticated user have been applied to session.
None of the solution proposed is changing the session held by UserSessionSource, which is the one and only one that constraints are pulled from by RdbmsStore (see below)
Having created a User with some constraints defined, when using:
UserSession session = authenticationManager.authenticate(new SystemUserCredentials(login)).getSession();
AppContext.withSecurityContext(new SecurityContext(session), () -> {
[...]
});
Then the session will indeed have the constraints, this is done by UserSessionManager.createSession().
But In RdbmsStore:
protected boolean needToApplyByPredicate(LoadContext context) {
return isAuthorizationRequired() && userSessionSource.getUserSession().hasConstraints()
&& needToApplyByPredicate(context, metaClass -> security.hasConstraints(metaClass));
}
userSessionSource.getUserSession() will not yield the session created previously, but the one still held by UserSessionSource, which in our case has no constraints.
=> 1st solution outcome : there is impersonation at security level but constraints are not applied
For the second solution, it’s worse. As the test code is already authenticated (session user ‘test_admin’), nothing happens at all, see below Authentication.begin() which is called by withUser().
public UserSession begin(@Nullable String login) {
[...]
// check if a current thread session exists, that is we got here from authenticated code
SecurityContext securityContext = AppContext.getSecurityContext();
if (securityContext != null) {
UserSession userSession = userSessions.getAndRefresh(securityContext.getSessionId());
if (userSession != null) {
log.trace("Already authenticated, do nothing");
cleanupCounter.set(cleanupCounter.get() + 1);
if (log.isTraceEnabled()) {
log.trace("New cleanup counter value: {}", cleanupCounter.get());
}
return userSession;
}
}
[...]
}
=> 2nd solution outcome : no impersonation at all as the calling test code is already authenticated
BTW : I think method withUser() & begin() contracts are confusing (I saw the javadoc). As an API user when I write Authentication.withUser(“toto”) or begin(“toto”) I’m requesting that after that I’m logged as ‘toto’.
I understand that this API targets unauthenticated code like JMX calls but then, there should be an API for this kind of testing. This could be as simple as a boolean to force login in any case : withUser(String login, boolean forceLogin), begin(String login, boolean forceLogin)
As a workararound, in order to tests constraints I had to do impersonation myself like that:
// save current test session and replace it with user's one
TestUserSessionSource userSessionSource = (TestUserSessionSource) AppBeans.get(UserSessionSource.class);
UserSession savedSession = userSessionSource.getUserSession();
UserSessionManager userSessionManager = AppBeans.get(UserSessionManager.class);
final User finalUser = user;
UserSession constrainedSession = persistence.createTransaction().execute(em -> {
return userSessionManager.createSession(finalUser, Locale.FRANCE, false);
});
userSessionSource.setUserSession(constrainedSession);
` // no AfterAuthenticationEvent published to cause constraints to be applied to session
// by tenant manager, so we need do it manually
tenantManager.applyUserConstraints(constrainedSession, true);
// code here is will apply session constraints
// restore test session
userSessionSource.setUserSession(savedSession);`
PS : preview of a new post seems broken for some time, however preview when editing works