add user profile refresh after changing effective user id

This commit is contained in:
Jörg Prante 2024-06-10 18:24:13 +02:00
parent be8f6ef4b3
commit 4d844e4347
10 changed files with 127 additions and 33 deletions

View file

@ -1,3 +1,3 @@
group = org.xbib
name = net-http
version = 4.7.1
version = 4.8.0

View file

@ -3,6 +3,8 @@ package org.xbib.net.http.server.application;
import java.nio.file.Path;
import java.time.ZoneId;
import java.util.Locale;
import org.xbib.net.http.server.domain.HttpSecurityDomain;
import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.route.HttpRouter;
import org.xbib.net.mime.MimeTypeService;
@ -15,6 +17,8 @@ public interface ApplicationBuilder {
ApplicationBuilder setSecret(String hexSecret);
ApplicationBuilder setSecurityDomain(HttpSecurityDomain securityDomain);
ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled);
ApplicationBuilder setLocale(Locale locale);

View file

@ -246,8 +246,10 @@ public class BaseApplication implements Application {
);
}
protected Collection<HttpHandler> newPersistHandlers(Codec<UserProfile> userProfileCodec, Codec<Session> sessionCodec) {
return List.of(new PersistSessionHandler(sessionCodec), new PersistUserProfileHandler(userProfileCodec));
protected Collection<HttpHandler> newPersistHandlers(Codec<UserProfile> userProfileCodec,
Codec<Session> sessionCodec) {
return List.of(new PersistSessionHandler(sessionCodec),
new PersistUserProfileHandler(userProfileCodec, builder.securityDomain));
}
@Override

View file

@ -7,6 +7,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import org.xbib.net.http.server.domain.HttpSecurityDomain;
import org.xbib.net.http.server.executor.BaseExecutor;
import org.xbib.net.http.server.executor.Executor;
import org.xbib.net.http.server.route.HttpRouter;
@ -32,6 +33,8 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
protected String secret;
protected HttpSecurityDomain securityDomain;
protected boolean sessionsEnabled;
protected Locale locale;
@ -87,6 +90,12 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
return this;
}
@Override
public ApplicationBuilder setSecurityDomain(HttpSecurityDomain securityDomain) {
this.securityDomain = securityDomain;
return this;
}
@Override
public ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled) {
this.sessionsEnabled = sessionsEnabled;

View file

@ -26,12 +26,15 @@ public class BaseUserProfile implements UserProfile {
private Collection<String> effectivePermissions;
private boolean refresh;
public BaseUserProfile() {
this.attributes = new BaseAttributes();
this.roles = new ArrayList<>();
this.effectiveRoles = new ArrayList<>();
this.permissions = new ArrayList<>();
this.effectivePermissions = new ArrayList<>();
this.refresh = false;
}
@Override
@ -134,6 +137,16 @@ public class BaseUserProfile implements UserProfile {
attributes.put(key, value);
}
@Override
public void triggerRefresh(boolean refresh) {
this.refresh = refresh;
}
@Override
public boolean isRefreshTriggered() {
return refresh;
}
@Override
public Attributes getAttributes() {
return attributes;
@ -197,24 +210,22 @@ public class BaseUserProfile implements UserProfile {
@SuppressWarnings("unchecked")
public static UserProfile fromMap(UserProfile userProfile, Map<String, Object> map) {
if (userProfile.getName() == null && map.containsKey(NAME)) {
if (map.containsKey(NAME)) {
userProfile.setName((String) map.get(NAME));
}
if (userProfile.getUserId() == null && map.containsKey(USER_ID)) {
String userId = (String) map.get(USER_ID);
userProfile.setUserId(userId);
if (map.containsKey(USER_ID)) {
userProfile.setUserId((String) map.get(USER_ID));
}
if (map.containsKey(EFFECTIVE_USER_ID)) {
String eUserId = (String) map.get(EFFECTIVE_USER_ID);
userProfile.setEffectiveUserId(eUserId);
userProfile.setEffectiveUserId((String) map.get(EFFECTIVE_USER_ID));
}
if ((userProfile.getRoles() == null || userProfile.getRoles().isEmpty()) && map.containsKey(ROLES)) {
if (map.containsKey(ROLES)) {
userProfile.setRoles((Collection<String>) map.get(ROLES));
}
if (map.containsKey(EFFECTIVE_ROLES)) {
userProfile.setEffectiveRoles((Collection<String>) map.get(EFFECTIVE_ROLES));
}
if ((userProfile.getPermissions() == null || userProfile.getPermissions().isEmpty()) && map.containsKey(PERMISSIONS)) {
if (map.containsKey(PERMISSIONS)) {
userProfile.setPermissions((Collection<String>) map.get(PERMISSIONS));
}
if (map.containsKey(EFFECTIVE_PERMISSIONS)) {

View file

@ -1,7 +1,9 @@
package org.xbib.net.http.server.auth;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -54,6 +56,20 @@ public class LoginAuthenticationHandler implements HttpHandler {
logger.log(Level.FINE, "no user profile found in context");
return;
}
if (userProfile.isRefreshTriggered()) {
try {
List<String> roles = new ArrayList<>();
userProfile.setRoles(roles);
List<String> effectiveRoles = new ArrayList<>();
userProfile.setEffectiveRoles(effectiveRoles);
loadRoles(userProfile);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
} finally {
userProfile.triggerRefresh(false);
}
return;
}
if (userProfile.getUserId() != null) {
logger.log(Level.FINE, "user id already set");
return;
@ -102,26 +118,28 @@ public class LoginAuthenticationHandler implements HttpHandler {
} else {
userProfile.setEffectiveUserId(authContext.getUsername());
}
GroupsProvider.Context groupContext = new GroupsProvider.Context(authContext.getUsername(), null);
Collection<String> groups = securityRealm.getGroupsProvider().getGroups(groupContext);
for (String group : groups) {
userProfile.addRole(group);
}
if (!userProfile.getUserId().equals(userProfile.getEffectiveUserId())) {
GroupsProvider.Context effectiveGroupContext = new GroupsProvider.Context(userProfile.getEffectiveUserId(), null);
for (String group : securityRealm.getGroupsProvider().getGroups(effectiveGroupContext)) {
userProfile.addEffectiveRole(group);
}
} else {
for (String group : groups) {
userProfile.addEffectiveRole(group);
}
}
// TODO permissions and effective permissions
logger.log(Level.FINE, "user and role setup completed for " + username);
loadRoles(userProfile);
return true;
}
logger.log(Level.FINE, "authentication failure for user " + username);
return false;
}
private void loadRoles(UserProfile userProfile) {
GroupsProvider.Context groupContext = new GroupsProvider.Context(userProfile.getUserId(), null);
Collection<String> groups = securityRealm.getGroupsProvider().getGroups(groupContext);
for (String group : groups) {
userProfile.addRole(group);
}
if (!userProfile.getUserId().equals(userProfile.getEffectiveUserId())) {
GroupsProvider.Context effectiveGroupContext = new GroupsProvider.Context(userProfile.getEffectiveUserId(), null);
for (String group : securityRealm.getGroupsProvider().getGroups(effectiveGroupContext)) {
userProfile.addEffectiveRole(group);
}
} else {
for (String group : groups) {
userProfile.addEffectiveRole(group);
}
}
}
}

View file

@ -1,13 +1,19 @@
package org.xbib.net.http.server.auth;
import org.xbib.net.GroupsProvider;
import org.xbib.net.SecurityRealm;
import org.xbib.net.UserProfile;
import org.xbib.net.http.HttpResponseStatus;
import org.xbib.net.http.server.HttpException;
import org.xbib.net.http.server.HttpHandler;
import org.xbib.net.http.server.domain.HttpSecurityDomain;
import org.xbib.net.http.server.persist.Codec;
import org.xbib.net.http.server.route.HttpRouterContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -17,16 +23,42 @@ public class PersistUserProfileHandler implements HttpHandler {
private final Codec<UserProfile> userProfileCodec;
public PersistUserProfileHandler(Codec<UserProfile> userProfileCodec) {
private final HttpSecurityDomain securityDomain;
public PersistUserProfileHandler(Codec<UserProfile> userProfileCodec,
HttpSecurityDomain securityDomain) {
this.userProfileCodec = userProfileCodec;
this.securityDomain = securityDomain;
}
@Override
public void handle(HttpRouterContext context) throws IOException {
UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile");
if (userProfile != null && userProfile.getUserId() != null) {
if (userProfile.isRefreshTriggered()) {
if (securityDomain == null) {
logger.log(Level.FINE, "unable to refresh, security domain is null");
return;
}
if (securityDomain.getRealm() == null) {
logger.log(Level.FINE, "unable to refresh, security realm is null");
return;
}
try {
// reset roles and load again
List<String> roles = new ArrayList<>();
userProfile.setRoles(roles);
List<String> effectiveRoles = new ArrayList<>();
userProfile.setEffectiveRoles(effectiveRoles);
loadRoles(userProfile, securityDomain.getRealm());
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
} finally {
userProfile.triggerRefresh(false);
}
}
try {
logger.log(Level.FINEST, "writing user profile id " + userProfile.getUserId());
logger.log(Level.FINEST, "writing user profile id = " + userProfile.getUserId());
userProfileCodec.write(userProfile.getUserId(), userProfile);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
@ -37,4 +69,23 @@ public class PersistUserProfileHandler implements HttpHandler {
(userProfile != null ? userProfile.getUserId() : null));
}
}
private void loadRoles(UserProfile userProfile, SecurityRealm securityRealm) {
String username = userProfile.getUserId();
GroupsProvider.Context groupContext = new GroupsProvider.Context(username, null);
Collection<String> groups = securityRealm.getGroupsProvider().getGroups(groupContext);
for (String group : groups) {
userProfile.addRole(group);
}
if (!userProfile.getUserId().equals(userProfile.getEffectiveUserId())) {
GroupsProvider.Context effectiveGroupContext = new GroupsProvider.Context(userProfile.getEffectiveUserId(), null);
for (String group : securityRealm.getGroupsProvider().getGroups(effectiveGroupContext)) {
userProfile.addEffectiveRole(group);
}
} else {
for (String group : groups) {
userProfile.addEffectiveRole(group);
}
}
}
}

View file

@ -2,6 +2,7 @@ package org.xbib.net.http.server.domain;
import java.util.List;
import org.xbib.net.SecurityDomain;
import org.xbib.net.UserProfile;
import org.xbib.net.http.server.HttpHandler;
public interface HttpSecurityDomain extends SecurityDomain {

View file

@ -105,12 +105,10 @@ public class IncomingContextHandler implements HttpHandler {
logger.log(Level.FINE, "new session created, id = " + session.id());
}
}
// important
context.getAttributes().put("session", session);
if (userProfile == null) {
userProfile = recoverUserProfile(context, session, payload);
}
// important
context.getAttributes().put("userprofile", userProfile);
}

View file

@ -6,7 +6,7 @@ dependencyResolutionManagement {
version('netty', '4.1.110.Final')
version('netty-tcnative', '2.0.65.Final')
version('datastructures', '5.1.0')
version('net', '4.7.0')
version('net', '4.8.0')
library('netty-codec-http2', 'io.netty', 'netty-codec-http2').versionRef('netty')
library('netty-handler', 'io.netty', 'netty-handler').versionRef('netty')
library('netty-handler-proxy', 'io.netty', 'netty-handler-proxy').versionRef('netty')