diff --git a/gradle.properties b/gradle.properties index 3b57d67..f211c21 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ group = org.xbib name = net-http -version = 4.7.1 +version = 4.8.0 diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/application/ApplicationBuilder.java b/net-http-server/src/main/java/org/xbib/net/http/server/application/ApplicationBuilder.java index 0bd327f..51aa9f6 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/application/ApplicationBuilder.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/application/ApplicationBuilder.java @@ -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); diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplication.java b/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplication.java index 1561ad3..b8d0c95 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplication.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplication.java @@ -246,8 +246,10 @@ public class BaseApplication implements Application { ); } - protected Collection newPersistHandlers(Codec userProfileCodec, Codec sessionCodec) { - return List.of(new PersistSessionHandler(sessionCodec), new PersistUserProfileHandler(userProfileCodec)); + protected Collection newPersistHandlers(Codec userProfileCodec, + Codec sessionCodec) { + return List.of(new PersistSessionHandler(sessionCodec), + new PersistUserProfileHandler(userProfileCodec, builder.securityDomain)); } @Override diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplicationBuilder.java b/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplicationBuilder.java index 718cd5a..171e013 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplicationBuilder.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/application/BaseApplicationBuilder.java @@ -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; diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/auth/BaseUserProfile.java b/net-http-server/src/main/java/org/xbib/net/http/server/auth/BaseUserProfile.java index e13260b..5dda341 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/auth/BaseUserProfile.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/auth/BaseUserProfile.java @@ -26,12 +26,15 @@ public class BaseUserProfile implements UserProfile { private Collection 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 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) map.get(ROLES)); } if (map.containsKey(EFFECTIVE_ROLES)) { userProfile.setEffectiveRoles((Collection) map.get(EFFECTIVE_ROLES)); } - if ((userProfile.getPermissions() == null || userProfile.getPermissions().isEmpty()) && map.containsKey(PERMISSIONS)) { + if (map.containsKey(PERMISSIONS)) { userProfile.setPermissions((Collection) map.get(PERMISSIONS)); } if (map.containsKey(EFFECTIVE_PERMISSIONS)) { diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/auth/LoginAuthenticationHandler.java b/net-http-server/src/main/java/org/xbib/net/http/server/auth/LoginAuthenticationHandler.java index 6eac121..615255a 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/auth/LoginAuthenticationHandler.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/auth/LoginAuthenticationHandler.java @@ -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 roles = new ArrayList<>(); + userProfile.setRoles(roles); + List 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 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 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); + } + } + } } diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/auth/PersistUserProfileHandler.java b/net-http-server/src/main/java/org/xbib/net/http/server/auth/PersistUserProfileHandler.java index 296e3b3..77cc506 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/auth/PersistUserProfileHandler.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/auth/PersistUserProfileHandler.java @@ -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 userProfileCodec; - public PersistUserProfileHandler(Codec userProfileCodec) { + private final HttpSecurityDomain securityDomain; + + public PersistUserProfileHandler(Codec 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 roles = new ArrayList<>(); + userProfile.setRoles(roles); + List 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 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); + } + } + } } diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/domain/HttpSecurityDomain.java b/net-http-server/src/main/java/org/xbib/net/http/server/domain/HttpSecurityDomain.java index 37a1414..cf9a0f0 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/domain/HttpSecurityDomain.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/domain/HttpSecurityDomain.java @@ -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 { diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/session/IncomingContextHandler.java b/net-http-server/src/main/java/org/xbib/net/http/server/session/IncomingContextHandler.java index 0c983d1..a04b471 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/session/IncomingContextHandler.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/session/IncomingContextHandler.java @@ -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); } diff --git a/settings.gradle b/settings.gradle index 39da600..bf4ecc5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -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')