add user profile refresh after changing effective user id
This commit is contained in:
parent
be8f6ef4b3
commit
4d844e4347
10 changed files with 127 additions and 33 deletions
|
@ -1,3 +1,3 @@
|
||||||
group = org.xbib
|
group = org.xbib
|
||||||
name = net-http
|
name = net-http
|
||||||
version = 4.7.1
|
version = 4.8.0
|
||||||
|
|
|
@ -3,6 +3,8 @@ package org.xbib.net.http.server.application;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
import java.util.Locale;
|
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.executor.Executor;
|
||||||
import org.xbib.net.http.server.route.HttpRouter;
|
import org.xbib.net.http.server.route.HttpRouter;
|
||||||
import org.xbib.net.mime.MimeTypeService;
|
import org.xbib.net.mime.MimeTypeService;
|
||||||
|
@ -15,6 +17,8 @@ public interface ApplicationBuilder {
|
||||||
|
|
||||||
ApplicationBuilder setSecret(String hexSecret);
|
ApplicationBuilder setSecret(String hexSecret);
|
||||||
|
|
||||||
|
ApplicationBuilder setSecurityDomain(HttpSecurityDomain securityDomain);
|
||||||
|
|
||||||
ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled);
|
ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled);
|
||||||
|
|
||||||
ApplicationBuilder setLocale(Locale locale);
|
ApplicationBuilder setLocale(Locale locale);
|
||||||
|
|
|
@ -246,8 +246,10 @@ public class BaseApplication implements Application {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Collection<HttpHandler> newPersistHandlers(Codec<UserProfile> userProfileCodec, Codec<Session> sessionCodec) {
|
protected Collection<HttpHandler> newPersistHandlers(Codec<UserProfile> userProfileCodec,
|
||||||
return List.of(new PersistSessionHandler(sessionCodec), new PersistUserProfileHandler(userProfileCodec));
|
Codec<Session> sessionCodec) {
|
||||||
|
return List.of(new PersistSessionHandler(sessionCodec),
|
||||||
|
new PersistUserProfileHandler(userProfileCodec, builder.securityDomain));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -7,6 +7,7 @@ import java.util.Locale;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
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.BaseExecutor;
|
||||||
import org.xbib.net.http.server.executor.Executor;
|
import org.xbib.net.http.server.executor.Executor;
|
||||||
import org.xbib.net.http.server.route.HttpRouter;
|
import org.xbib.net.http.server.route.HttpRouter;
|
||||||
|
@ -32,6 +33,8 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
|
||||||
|
|
||||||
protected String secret;
|
protected String secret;
|
||||||
|
|
||||||
|
protected HttpSecurityDomain securityDomain;
|
||||||
|
|
||||||
protected boolean sessionsEnabled;
|
protected boolean sessionsEnabled;
|
||||||
|
|
||||||
protected Locale locale;
|
protected Locale locale;
|
||||||
|
@ -87,6 +90,12 @@ public class BaseApplicationBuilder implements ApplicationBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ApplicationBuilder setSecurityDomain(HttpSecurityDomain securityDomain) {
|
||||||
|
this.securityDomain = securityDomain;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled) {
|
public ApplicationBuilder setSessionsEnabled(boolean sessionsEnabled) {
|
||||||
this.sessionsEnabled = sessionsEnabled;
|
this.sessionsEnabled = sessionsEnabled;
|
||||||
|
|
|
@ -26,12 +26,15 @@ public class BaseUserProfile implements UserProfile {
|
||||||
|
|
||||||
private Collection<String> effectivePermissions;
|
private Collection<String> effectivePermissions;
|
||||||
|
|
||||||
|
private boolean refresh;
|
||||||
|
|
||||||
public BaseUserProfile() {
|
public BaseUserProfile() {
|
||||||
this.attributes = new BaseAttributes();
|
this.attributes = new BaseAttributes();
|
||||||
this.roles = new ArrayList<>();
|
this.roles = new ArrayList<>();
|
||||||
this.effectiveRoles = new ArrayList<>();
|
this.effectiveRoles = new ArrayList<>();
|
||||||
this.permissions = new ArrayList<>();
|
this.permissions = new ArrayList<>();
|
||||||
this.effectivePermissions = new ArrayList<>();
|
this.effectivePermissions = new ArrayList<>();
|
||||||
|
this.refresh = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -134,6 +137,16 @@ public class BaseUserProfile implements UserProfile {
|
||||||
attributes.put(key, value);
|
attributes.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void triggerRefresh(boolean refresh) {
|
||||||
|
this.refresh = refresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRefreshTriggered() {
|
||||||
|
return refresh;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Attributes getAttributes() {
|
public Attributes getAttributes() {
|
||||||
return attributes;
|
return attributes;
|
||||||
|
@ -197,24 +210,22 @@ public class BaseUserProfile implements UserProfile {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static UserProfile fromMap(UserProfile userProfile, Map<String, Object> map) {
|
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));
|
userProfile.setName((String) map.get(NAME));
|
||||||
}
|
}
|
||||||
if (userProfile.getUserId() == null && map.containsKey(USER_ID)) {
|
if (map.containsKey(USER_ID)) {
|
||||||
String userId = (String) map.get(USER_ID);
|
userProfile.setUserId((String) map.get(USER_ID));
|
||||||
userProfile.setUserId(userId);
|
|
||||||
}
|
}
|
||||||
if (map.containsKey(EFFECTIVE_USER_ID)) {
|
if (map.containsKey(EFFECTIVE_USER_ID)) {
|
||||||
String eUserId = (String) map.get(EFFECTIVE_USER_ID);
|
userProfile.setEffectiveUserId((String) map.get(EFFECTIVE_USER_ID));
|
||||||
userProfile.setEffectiveUserId(eUserId);
|
|
||||||
}
|
}
|
||||||
if ((userProfile.getRoles() == null || userProfile.getRoles().isEmpty()) && map.containsKey(ROLES)) {
|
if (map.containsKey(ROLES)) {
|
||||||
userProfile.setRoles((Collection<String>) map.get(ROLES));
|
userProfile.setRoles((Collection<String>) map.get(ROLES));
|
||||||
}
|
}
|
||||||
if (map.containsKey(EFFECTIVE_ROLES)) {
|
if (map.containsKey(EFFECTIVE_ROLES)) {
|
||||||
userProfile.setEffectiveRoles((Collection<String>) map.get(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));
|
userProfile.setPermissions((Collection<String>) map.get(PERMISSIONS));
|
||||||
}
|
}
|
||||||
if (map.containsKey(EFFECTIVE_PERMISSIONS)) {
|
if (map.containsKey(EFFECTIVE_PERMISSIONS)) {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package org.xbib.net.http.server.auth;
|
package org.xbib.net.http.server.auth;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
@ -54,6 +56,20 @@ public class LoginAuthenticationHandler implements HttpHandler {
|
||||||
logger.log(Level.FINE, "no user profile found in context");
|
logger.log(Level.FINE, "no user profile found in context");
|
||||||
return;
|
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) {
|
if (userProfile.getUserId() != null) {
|
||||||
logger.log(Level.FINE, "user id already set");
|
logger.log(Level.FINE, "user id already set");
|
||||||
return;
|
return;
|
||||||
|
@ -102,7 +118,15 @@ public class LoginAuthenticationHandler implements HttpHandler {
|
||||||
} else {
|
} else {
|
||||||
userProfile.setEffectiveUserId(authContext.getUsername());
|
userProfile.setEffectiveUserId(authContext.getUsername());
|
||||||
}
|
}
|
||||||
GroupsProvider.Context groupContext = new GroupsProvider.Context(authContext.getUsername(), null);
|
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);
|
Collection<String> groups = securityRealm.getGroupsProvider().getGroups(groupContext);
|
||||||
for (String group : groups) {
|
for (String group : groups) {
|
||||||
userProfile.addRole(group);
|
userProfile.addRole(group);
|
||||||
|
@ -117,11 +141,5 @@ public class LoginAuthenticationHandler implements HttpHandler {
|
||||||
userProfile.addEffectiveRole(group);
|
userProfile.addEffectiveRole(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO permissions and effective permissions
|
|
||||||
logger.log(Level.FINE, "user and role setup completed for " + username);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
logger.log(Level.FINE, "authentication failure for user " + username);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
package org.xbib.net.http.server.auth;
|
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.UserProfile;
|
||||||
import org.xbib.net.http.HttpResponseStatus;
|
import org.xbib.net.http.HttpResponseStatus;
|
||||||
import org.xbib.net.http.server.HttpException;
|
import org.xbib.net.http.server.HttpException;
|
||||||
import org.xbib.net.http.server.HttpHandler;
|
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.persist.Codec;
|
||||||
import org.xbib.net.http.server.route.HttpRouterContext;
|
import org.xbib.net.http.server.route.HttpRouterContext;
|
||||||
|
|
||||||
import java.io.IOException;
|
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.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -17,16 +23,42 @@ public class PersistUserProfileHandler implements HttpHandler {
|
||||||
|
|
||||||
private final Codec<UserProfile> userProfileCodec;
|
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.userProfileCodec = userProfileCodec;
|
||||||
|
this.securityDomain = securityDomain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(HttpRouterContext context) throws IOException {
|
public void handle(HttpRouterContext context) throws IOException {
|
||||||
UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile");
|
UserProfile userProfile = context.getAttributes().get(UserProfile.class, "userprofile");
|
||||||
if (userProfile != null && userProfile.getUserId() != null) {
|
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 {
|
try {
|
||||||
logger.log(Level.FINEST, "writing user profile id " + userProfile.getUserId());
|
// 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());
|
||||||
userProfileCodec.write(userProfile.getUserId(), userProfile);
|
userProfileCodec.write(userProfile.getUserId(), userProfile);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||||
|
@ -37,4 +69,23 @@ public class PersistUserProfileHandler implements HttpHandler {
|
||||||
(userProfile != null ? userProfile.getUserId() : null));
|
(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.xbib.net.http.server.domain;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.xbib.net.SecurityDomain;
|
import org.xbib.net.SecurityDomain;
|
||||||
|
import org.xbib.net.UserProfile;
|
||||||
import org.xbib.net.http.server.HttpHandler;
|
import org.xbib.net.http.server.HttpHandler;
|
||||||
|
|
||||||
public interface HttpSecurityDomain extends SecurityDomain {
|
public interface HttpSecurityDomain extends SecurityDomain {
|
||||||
|
|
|
@ -105,12 +105,10 @@ public class IncomingContextHandler implements HttpHandler {
|
||||||
logger.log(Level.FINE, "new session created, id = " + session.id());
|
logger.log(Level.FINE, "new session created, id = " + session.id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// important
|
|
||||||
context.getAttributes().put("session", session);
|
context.getAttributes().put("session", session);
|
||||||
if (userProfile == null) {
|
if (userProfile == null) {
|
||||||
userProfile = recoverUserProfile(context, session, payload);
|
userProfile = recoverUserProfile(context, session, payload);
|
||||||
}
|
}
|
||||||
// important
|
|
||||||
context.getAttributes().put("userprofile", userProfile);
|
context.getAttributes().put("userprofile", userProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ dependencyResolutionManagement {
|
||||||
version('netty', '4.1.110.Final')
|
version('netty', '4.1.110.Final')
|
||||||
version('netty-tcnative', '2.0.65.Final')
|
version('netty-tcnative', '2.0.65.Final')
|
||||||
version('datastructures', '5.1.0')
|
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-codec-http2', 'io.netty', 'netty-codec-http2').versionRef('netty')
|
||||||
library('netty-handler', 'io.netty', 'netty-handler').versionRef('netty')
|
library('netty-handler', 'io.netty', 'netty-handler').versionRef('netty')
|
||||||
library('netty-handler-proxy', 'io.netty', 'netty-handler-proxy').versionRef('netty')
|
library('netty-handler-proxy', 'io.netty', 'netty-handler-proxy').versionRef('netty')
|
||||||
|
|
Loading…
Reference in a new issue