org.swisspush.gateleen.user.UserProfileHandler Maven / Gradle / Ivy
package org.swisspush.gateleen.user;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.eventbus.Message;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.DecodeException;
import io.vertx.core.json.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.swisspush.gateleen.core.http.RequestLoggerFactory;
import org.swisspush.gateleen.core.logging.LoggableResource;
import org.swisspush.gateleen.core.logging.RequestLogger;
import org.swisspush.gateleen.core.storage.ResourceStorage;
import org.swisspush.gateleen.core.util.ResponseStatusCodeLogUtil;
import org.swisspush.gateleen.core.util.RoleExtractor;
import org.swisspush.gateleen.core.util.StatusCode;
import org.swisspush.gateleen.logging.LoggingResourceManager;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author https://github.com/lbovet [Laurent Bovet]
*/
public class UserProfileHandler implements LoggableResource {
private Vertx vertx;
private ResourceStorage storage;
private String roleProfileKey = "profile";
private UserProfileConfiguration userProfileConfiguration;
private UserProfileManipulater userProfileManipulater;
private Logger log = LoggerFactory.getLogger(UserProfileHandler.class);
private Map roleProfiles = new HashMap<>();
private RoleExtractor roleExtractor;
private boolean logUserProfileChanges = false;
/**
* Constructor for the UserProfileHandler.
*
* @param vertx vertx
* @param storage storage
* @param userProfileConfiguration userProfileConfiguration
*/
public UserProfileHandler(Vertx vertx, ResourceStorage storage, UserProfileConfiguration userProfileConfiguration) {
this.vertx = vertx;
this.storage = storage;
this.userProfileConfiguration = userProfileConfiguration;
this.userProfileManipulater = new UserProfileManipulater(log);
this.roleExtractor = new RoleExtractor(userProfileConfiguration.getRolePattern());
updateRoleProfiles();
EventBus eb = vertx.eventBus();
// Receive update notifications
eb.consumer(RoleProfileHandler.UPDATE_ADDRESS, (Handler>) role -> updateRoleProfiles());
}
/**
* Constructor for the UserProfileHandler.
*
* @deprecated Use {@link UserProfileHandler#UserProfileHandler(Vertx, ResourceStorage, UserProfileConfiguration)} instead,
* because the {@link LoggingResourceManager} is not used anymore
*
* @param vertx vertx
* @param storage the storage
* @param loggingResourceManager manager for the logging resources
* @param userProfileConfiguration userProfileConfiguration
*/
@Deprecated
public UserProfileHandler(Vertx vertx, ResourceStorage storage, LoggingResourceManager loggingResourceManager, UserProfileConfiguration userProfileConfiguration) {
this(vertx, storage, userProfileConfiguration);
}
@Override
public void enableResourceLogging(boolean resourceLoggingEnabled) {
this.logUserProfileChanges = resourceLoggingEnabled;
}
public boolean isUserProfileRequest(HttpServerRequest request) {
return userProfileConfiguration.doesUrlMatchTheProfileUriPattern(request.path());
}
public void handle(final HttpServerRequest request) {
RequestLoggerFactory.getLogger(UserProfileHandler.class, request).info("handling " + request.method() + " " + request.path());
switch (request.method().name()) {
case "GET":
storage.get(request.path(), buffer -> {
request.response().headers().set("Content-Type", "application/json");
String userId = userProfileConfiguration.extractUserIdFromProfileUri(request.path());
if (buffer != null) {
request.response().setStatusCode(StatusCode.OK.getStatusCode());
JsonObject profile = new JsonObject(buffer.toString());
final int enrichUpdateCount = userProfileManipulater.enrichProfile(request.headers(),
profile, userId, userProfileConfiguration.getProfileProperties().values());
final JsonObject mergedProfile = mergeUserProfileWithRoleProfile(request, profile);
logPayload(request, StatusCode.OK.getStatusCode(), Buffer.buffer(mergedProfile.encode()),
request.response().headers());
ResponseStatusCodeLogUtil.info(request, StatusCode.OK, UserProfileHandler.class);
if (enrichUpdateCount == 0) {
// Normal case: Nothing changed in profile
request.response().end(mergedProfile.encode());
} else {
// Special case: Something updated in profile
log.debug("Updated the profile in a GET request (special case). Request path is {}.", request.path());
storage.put(request.path(), Buffer.buffer(profile.encode()), status -> request.response().end(mergedProfile.encode()));
}
} else {
// Not Found, returns the initial profile.
request.response().setStatusCode(StatusCode.OK.getStatusCode());
JsonObject profile = userProfileManipulater.createInitialProfile(request.headers(), userId, userProfileConfiguration.getProfileProperties().values());
final JsonObject mergedProfile = mergeUserProfileWithRoleProfile(request, profile);
// NEMO-3200, store the profile, otherwise the next request will fail,
// cause the server cannot enrich the request create the profile data
storage.put(request.path(), Buffer.buffer(profile.encode()), status -> {
logPayload(request, status, Buffer.buffer(mergedProfile.encode()), request.response().headers());
ResponseStatusCodeLogUtil.info(request, StatusCode.OK, UserProfileHandler.class);
request.response().end(mergedProfile.encode());
});
}
});
break;
case "PUT":
request.pause();
storage.get(request.path(), existingBuffer -> {
if (existingBuffer != null) {
request.resume();
} else {
// Not Found, create a new profile.
log.debug("Tried to put (merge) a profile, path is '{}', but profile was not found. Create a new profile.",
((request.path() == null || request == null) ? "" : request.path()));
JsonObject profile = userProfileManipulater.createProfileWithLanguage(request.headers());
cleanupUserProfile(profile, updatedProfile -> storage.put(request.path() + "?merge=true", Buffer.buffer(updatedProfile.encode()), status -> request.resume()));
}
});
/**
* After request.resume() the bodyHandler will be called
*/
request.bodyHandler(newBuffer -> {
JsonObject profile = null;
try {
profile = new JsonObject(newBuffer.toString());
} catch (DecodeException ex) {
ResponseStatusCodeLogUtil.info(request, StatusCode.BAD_REQUEST, UserProfileHandler.class);
request.response().setStatusCode(StatusCode.BAD_REQUEST.getStatusCode());
request.response().end(StatusCode.BAD_REQUEST.getStatusMessage());
return;
}
cleanupUserProfile(profile, updatedProfile -> storage.put(request.uri() + "?merge=true", Buffer.buffer(updatedProfile.encode()), status -> {
logPayload(request, status, Buffer.buffer(updatedProfile.encode()), MultiMap.caseInsensitiveMultiMap());
ResponseStatusCodeLogUtil.info(request, StatusCode.fromCode(status), UserProfileHandler.class);
request.response().setStatusCode(status);
request.response().end();
}));
});
break;
case "DELETE":
storage.delete(request.path(), status -> {
ResponseStatusCodeLogUtil.info(request, StatusCode.fromCode(status), UserProfileHandler.class);
request.response().setStatusCode(status);
request.response().end();
});
break;
default:
ResponseStatusCodeLogUtil.info(request, StatusCode.METHOD_NOT_ALLOWED, UserProfileHandler.class);
request.response().setStatusCode(StatusCode.METHOD_NOT_ALLOWED.getStatusCode());
request.response().end(StatusCode.METHOD_NOT_ALLOWED.getStatusMessage());
}
}
protected void cleanupUserProfile(final JsonObject profile, final Handler profileCallback) {
log.debug("About to remove 'not allowed' properties from user profile");
JsonObject profileCopy = profile.copy();
Set profileFieldNames = profileCopy.fieldNames();
profileFieldNames.stream().filter(fieldName -> !userProfileConfiguration.isAllowedProfileProperty(fieldName)).forEach(fieldName -> {
log.debug("Removing property '{}' from user profile", fieldName);
profile.remove(fieldName);
});
profileCallback.handle(profile);
}
private JsonObject mergeUserProfileWithRoleProfile(HttpServerRequest request, JsonObject userProfile) {
Set roles = roleExtractor.extractRoles(request);
JsonObject roleProfileAccumulated = new JsonObject();
if (roles != null) {
for (String role : roles) {
JsonObject roleProfile = roleProfiles.get(role);
if (roleProfile != null) {
roleProfileAccumulated.mergeIn(roleProfile);
}
}
}
return roleProfileAccumulated.mergeIn(userProfile);
}
private void updateRoleProfiles() {
storage.get(userProfileConfiguration.getRoleProfilesRoot(), buffer -> {
if (buffer != null) {
roleProfiles = new HashMap<>();
for (Object profileObject : new JsonObject(buffer.toString()).getJsonArray("v1")) {
String role = (String) profileObject;
role = role.replaceAll("/$", "");
role = role.replaceAll("^/", "");
updateRoleProfile(role);
}
} else {
log.debug("No Role Profiles in storage, remove all roles");
roleProfiles.clear();
}
});
}
private void updateRoleProfile(final String role) {
storage.get(userProfileConfiguration.getRoleProfilesRoot() + role + "/" + roleProfileKey, buffer -> {
if (buffer != null) {
try {
log.debug("Applying role profile for {}", role);
mergeRole(role, buffer);
} catch (IllegalArgumentException e) {
log.error("Could not reconfigure routing", e);
}
} else {
log.error("No profile for role {} found in storage", role);
}
});
}
private void mergeRole(String role, Buffer buffer) {
JsonObject roleProfile = new JsonObject(buffer.toString());
roleProfiles.put(role, roleProfile);
}
private void logPayload(final HttpServerRequest request, final Integer status, Buffer data, final MultiMap responseHeaders) {
if(logUserProfileChanges){
RequestLogger.logRequest(vertx.eventBus(), request, status, data, responseHeaders);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy