diff --git a/Jenkinsfile b/Jenkinsfile
index 5c20846045d5bbd76178db777912239d71a6c638..c3da1a0c0878ed560dfd5702e47ec1313a29dd96 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -95,7 +95,7 @@ pipeline {
 					container("quarkus-22"){
 						withCredentials([usernamePassword(credentialsId: 'jenkins-docker-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
 							configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) {
-								sh './mvnw -pl user-manager-server -s $MAVEN_SETTINGS clean package -DskipTests -Pnative -Dquarkus.container-image.registry=docker.ozg-sh.de -Dquarkus.container-image.username=${USER} -Dquarkus.container-image.password=${PASSWORD} -Dquarkus.container-image.push=true -Dquarkus.container-image.build=true -Dmaven.wagon.http.retryHandler.count=3'
+								sh './mvnw -pl user-manager-server -s $MAVEN_SETTINGS clean verify -Pnative -Dquarkus.container-image.registry=docker.ozg-sh.de -Dquarkus.container-image.username=${USER} -Dquarkus.container-image.password=${PASSWORD} -Dquarkus.container-image.push=true -Dquarkus.container-image.build=true -Dmaven.wagon.http.retryHandler.count=3'
 							} 	
 						}
 					}    
diff --git a/user-manager-server/pom.xml b/user-manager-server/pom.xml
index cc6499439a4d8d9bfbb65222a94e49662932f067..3439e27d92f0347ef4733a213a79bcf78b7db01f 100644
--- a/user-manager-server/pom.xml
+++ b/user-manager-server/pom.xml
@@ -18,11 +18,10 @@
 		<kop-common.version>1.1.4-SNAPSHOT</kop-common.version>
 
 		<jacoco.plugin.version>0.8.8</jacoco.plugin.version>
-		<quarkus.platform.version>2.10.3.Final</quarkus.platform.version>
+		<quarkus.platform.version>2.12.2.Final</quarkus.platform.version>
 		<surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
 		<maven-failsafe-plugin.version>3.0.0-M7</maven-failsafe-plugin.version>
 		<git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>
-		<quarkus-logging-json.version>1.1.1</quarkus-logging-json.version>
 
 		<!-- Versions already declared in kop-common-dependencies bom -> use bom as parent!? -->
 		<mapstruct.version>1.5.1.Final</mapstruct.version>
@@ -82,15 +81,15 @@
 		</dependency>
 		<dependency>
 			<groupId>io.quarkus</groupId>
-			<artifactId>quarkus-arc</artifactId>
+			<artifactId>quarkus-resteasy-reactive-links</artifactId>
 		</dependency>
 		<dependency>
 			<groupId>io.quarkus</groupId>
-			<artifactId>quarkus-hal</artifactId>
+			<artifactId>quarkus-arc</artifactId>
 		</dependency>
 		<dependency>
 			<groupId>io.quarkus</groupId>
-			<artifactId>quarkus-resteasy-reactive-links</artifactId>
+			<artifactId>quarkus-hal</artifactId>
 		</dependency>
 		<dependency>
 			<groupId>io.quarkus</groupId>
@@ -116,10 +115,6 @@
 			<groupId>io.quarkus</groupId>
 			<artifactId>quarkus-mongodb-panache</artifactId>
 		</dependency>
-		<dependency>
-			<groupId>io.quarkus</groupId>
-			<artifactId>quarkus-resteasy-reactive</artifactId>
-		</dependency>
 		<dependency>
 			<groupId>io.quarkus</groupId>
 			<artifactId>quarkus-scheduler</artifactId>
@@ -143,13 +138,12 @@
 
 		<!-- Logging -->
 		<dependency>
-			<groupId>io.quarkiverse.loggingjson</groupId>
-			<artifactId>quarkus-logging-json</artifactId>
-			<version>${quarkus-logging-json.version}</version>
+		    <groupId>io.quarkus</groupId>
+		    <artifactId>quarkus-logging-json</artifactId>
 		</dependency>
 		<dependency>
-		    <groupId>org.jboss.logmanager</groupId>
-		    <artifactId>log4j2-jboss-logmanager</artifactId>
+			<groupId>org.jboss.logmanager</groupId>
+			<artifactId>log4j2-jboss-logmanager</artifactId>
 		</dependency>
 
 		<!-- Tools -->
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/NativeConfig.java b/user-manager-server/src/main/java/de/itvsh/kop/user/NativeConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..b2edd0a2acfa1fddc3e0429a13d2b49087b3880b
--- /dev/null
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/NativeConfig.java
@@ -0,0 +1,25 @@
+package de.itvsh.kop.user;
+
+import io.quarkus.runtime.annotations.RegisterForReflection;
+
+@RegisterForReflection(targets = {
+		org.apache.logging.log4j.message.ReusableMessageFactory.class,
+		org.apache.logging.log4j.message.DefaultFlowMessageFactory.class,
+		org.jboss.resteasy.plugins.providers.multipart.MultipartReader.class,
+		org.jboss.resteasy.plugins.providers.multipart.ListMultipartReader.class,
+		org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataReader.class,
+		org.jboss.resteasy.plugins.providers.multipart.MultipartRelatedReader.class,
+		org.jboss.resteasy.plugins.providers.multipart.MapMultipartFormDataReader.class,
+		org.jboss.resteasy.plugins.providers.multipart.MultipartWriter.class,
+		org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataWriter.class,
+		org.jboss.resteasy.plugins.providers.multipart.MultipartRelatedWriter.class,
+		org.jboss.resteasy.plugins.providers.multipart.ListMultipartWriter.class,
+		org.jboss.resteasy.plugins.providers.multipart.MapMultipartFormDataWriter.class,
+		org.jboss.resteasy.plugins.providers.multipart.MultipartFormAnnotationReader.class,
+		org.jboss.resteasy.plugins.providers.multipart.MultipartFormAnnotationWriter.class,
+		org.jboss.resteasy.plugins.providers.multipart.MimeMultipartProvider.class,
+		org.jboss.resteasy.plugins.providers.multipart.XopWithMultipartRelatedReader.class,
+		org.jboss.resteasy.plugins.providers.multipart.XopWithMultipartRelatedWriter.class
+})
+public class NativeConfig {
+}
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/UserProfileResource.java b/user-manager-server/src/main/java/de/itvsh/kop/user/UserProfileResource.java
index 0c33bd5835b83c5d53c173d41a9e542da4bdffc1..772094c67aa2d3ea5df9927857abfa7362ce3cee 100644
--- a/user-manager-server/src/main/java/de/itvsh/kop/user/UserProfileResource.java
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/UserProfileResource.java
@@ -5,26 +5,31 @@ import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
 
-import io.quarkus.hal.HalEntityWrapper;
 import org.jboss.resteasy.reactive.common.util.RestMediaType;
 
+import io.quarkus.hal.HalEntityWrapper;
+
 @Path("/api/userProfiles")
 public class UserProfileResource {
 
-	public static final String USER_PROFILE_RESOURCE_PATH_PREFIX = "/api/userProfiles/";
+	static final String USER_PROFILE_RESOURCE_PATH_TEMPLATE = "api/userProfiles/{0}"; // NOSONAR
 
 	@Inject
 	UserService userService;
 	@Inject
 	UserProfileResourceAssembler userProfileResourceAssembler;
+	@Context
+	UriInfo uriInfo;
 
 	@GET
 	@Path("/{id}")
 	@Produces({ MediaType.APPLICATION_JSON, RestMediaType.APPLICATION_HAL_JSON })
 	public HalEntityWrapper findById(@PathParam(value = "id") String id) {
 		User user = userService.findById(id);
-		return userProfileResourceAssembler.toUserProfileResource(user);
+		return userProfileResourceAssembler.toUserProfileResource(user, uriInfo);
 	}
 }
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/UserProfileResourceAssembler.java b/user-manager-server/src/main/java/de/itvsh/kop/user/UserProfileResourceAssembler.java
index c93f500ce0462162051e754300164c5621b6e4db..d65ec03d12c8108be4fc5b9c81b9e53526064de6 100644
--- a/user-manager-server/src/main/java/de/itvsh/kop/user/UserProfileResourceAssembler.java
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/UserProfileResourceAssembler.java
@@ -1,18 +1,25 @@
 package de.itvsh.kop.user;
 
-import de.itvsh.kop.user.settings.UserSettingsResource;
-import io.quarkus.hal.HalEntityWrapper;
-
 import javax.enterprise.context.ApplicationScoped;
 import javax.ws.rs.core.Link;
+import javax.ws.rs.core.UriInfo;
+
+import de.itvsh.kop.user.settings.UserSettingsResource;
+import io.quarkus.hal.HalEntityWrapper;
 
 @ApplicationScoped
 public class UserProfileResourceAssembler {
+	static final String REL_SETTINGS = "settings";
+	static final String REL_SELF = "self";
 
-	HalEntityWrapper toUserProfileResource(User user) {
+	HalEntityWrapper toUserProfileResource(User user, UriInfo uriInfo) {
 		var result = new HalEntityWrapper(user);
-		result.addLinks(Link.fromPath(UserProfileResource.USER_PROFILE_RESOURCE_PATH_PREFIX + user.getId().toHexString()).rel("self").build());
-		result.addLinks(Link.fromPath(UserSettingsResource.SETTINGS_LINK_PATTERN.formatted(user.getId().toHexString())).rel("settings").build());
+
+		result.addLinks(
+				Link.fromUri(uriInfo.getBaseUri().toString() + UserProfileResource.USER_PROFILE_RESOURCE_PATH_TEMPLATE).rel(REL_SELF)
+						.build(user.getId().toHexString()),
+				Link.fromUri(uriInfo.getBaseUri().toString() + UserSettingsResource.SETTINGS_LINK_PATTERN).rel(REL_SETTINGS)
+						.build(user.getId().toHexString()));
 
 		return result;
 	}
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/CallContextUser.java b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/CallContextUser.java
new file mode 100644
index 0000000000000000000000000000000000000000..c13b5a8ce70b2b4b231203f2f7c95561db7752f0
--- /dev/null
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/CallContextUser.java
@@ -0,0 +1,36 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Optional;
+
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Singular;
+
+@Builder
+@Getter
+public class CallContextUser implements Serializable, Principal {
+
+	private static final long serialVersionUID = 1L;
+
+	private final String clientName;
+
+	@Builder.Default
+	private final transient Optional<String> userId = Optional.empty();
+	@Builder.Default
+	private final transient Optional<String> userName = Optional.empty();
+	@Singular
+	private final Collection<String> organisatorischeEinheitenIds;
+	@Builder.Default
+	private final transient boolean organisationEinheitenIdCheckNecessary = false;
+
+	@Builder.Default
+	private final transient boolean authenticated = false;
+
+	@Override
+	public String getName() {
+		return clientName;
+	}
+}
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/CurrentCallContextUserService.java b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/CurrentCallContextUserService.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3293e92809dd95bc690e4371cb53b56f0458a54
--- /dev/null
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/CurrentCallContextUserService.java
@@ -0,0 +1,19 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import java.util.Optional;
+
+import javax.enterprise.context.RequestScoped;
+
+@RequestScoped
+public class CurrentCallContextUserService {
+	private CallContextUser user;
+
+	void setCallContextUser(CallContextUser callContextUser) {
+		this.user = callContextUser;
+	}
+
+	public Optional<CallContextUser> getCurrentCallContextUser() {
+		return Optional.ofNullable(user);
+	}
+
+}
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/GrpcCallContextInterceptor.java b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/GrpcCallContextInterceptor.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef9cf393a22a38fb7acdb37eb622e01b84e10afe
--- /dev/null
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/GrpcCallContextInterceptor.java
@@ -0,0 +1,138 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.Optional;
+import java.util.UUID;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+
+import org.apache.logging.log4j.CloseableThreadContext;
+
+import io.grpc.ForwardingServerCallListener;
+import io.grpc.Metadata;
+import io.grpc.Metadata.Key;
+import io.grpc.ServerCall;
+import io.grpc.ServerCall.Listener;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import io.quarkus.grpc.GlobalInterceptor;
+import lombok.extern.log4j.Log4j2;
+
+@ApplicationScoped
+@GlobalInterceptor
+@Log4j2
+public class GrpcCallContextInterceptor implements ServerInterceptor {
+
+	private static final String REQUEST_ID_KEY = "requestId";
+
+	static final String KEY_USER_ID = "USER_ID-bin";
+	static final String KEY_USER_NAME = "USER_NAME-bin";
+	static final String KEY_CLIENT_NAME = "CLIENT_NAME-bin";
+	static final String KEY_REQUEST_ID = "REQUEST_ID-bin";
+	static final String KEY_ACCESS_LIMITED_ORGAID = "ACCESS_LIMITED_TO_ORGANISATORISCHEEINHEITENID-bin";
+	static final String KEY_ACCESS_LIMITED = "ACCESS_LIMITED-bin";
+
+	static final String REQUEST_ID_PLACEHOLDER = "no-requestId-given";
+
+	@Inject
+	CurrentCallContextUserService userService;
+
+	@Override
+	public <Q, S> Listener<Q> interceptCall(ServerCall<Q, S> call, Metadata headers, ServerCallHandler<Q, S> next) {
+		return new LogContextSettingListener<>(next.startCall(call, headers), headers);
+	}
+
+	class LogContextSettingListener<A> extends ForwardingServerCallListener.SimpleForwardingServerCallListener<A> {
+		private final String requestId;
+		private final Metadata headers;
+
+		public LogContextSettingListener(ServerCall.Listener<A> delegate, Metadata headers) {
+			super(delegate);
+			this.headers = headers;
+			this.requestId = getRequestId();
+		}
+
+		@Override
+		public void onMessage(A message) {
+			doSurroundOn(() -> super.onMessage(message));
+		}
+
+		@Override
+		public void onHalfClose() {
+			doSurroundOn(super::onHalfClose);
+		}
+
+		@Override
+		public void onCancel() {
+			doSurroundOn(super::onCancel);
+		}
+
+		@Override
+		public void onComplete() {
+			doSurroundOn(super::onComplete);
+		}
+
+		@Override
+		public void onReady() {
+			doSurroundOn(super::onReady);
+		}
+
+		void doSurroundOn(Runnable runnable) {
+			try (CloseableThreadContext.Instance ctc = CloseableThreadContext.put(REQUEST_ID_KEY, requestId)) {
+				startSecurityContext();
+				runnable.run();
+			}
+		}
+
+		String getRequestId() {
+			return getFromHeaders(KEY_REQUEST_ID, headers).orElseGet(() -> UUID.randomUUID().toString());
+		}
+
+		void startSecurityContext() {
+			userService.setCallContextUser(createCallContextUser());
+
+		}
+
+		CallContextUser createCallContextUser() {
+			var builder = CallContextUser.builder()
+					.userId(getFromHeaders(GrpcCallContextInterceptor.KEY_USER_ID, headers))
+					.userName(getFromHeaders(KEY_USER_NAME, headers))
+					.authenticated(true)
+					.organisatorischeEinheitenIds(getCollection(KEY_ACCESS_LIMITED_ORGAID, headers));
+
+			getFromHeaders(KEY_ACCESS_LIMITED, headers).map(Boolean::parseBoolean).ifPresentOrElse(
+					builder::organisationEinheitenIdCheckNecessary,
+					() -> builder.organisationEinheitenIdCheckNecessary(false));
+
+			// TODO throw exception if missing required data as soon all clients are fine
+			// with using headers for auth data.
+			getFromHeaders(KEY_CLIENT_NAME, headers).ifPresentOrElse(builder::clientName, () -> LOG.warn("Missing client name in grpc header."));
+
+			return builder.build();
+		}
+	}
+
+	// TODO move to a grpcUtil class in common
+	static Optional<String> getFromHeaders(String key, Metadata headers) {
+		return Optional.ofNullable(headers.get(createKeyOf(key))).map(val -> new String(val, StandardCharsets.UTF_8));
+	}
+
+	// TODO move to a grpcUtil class in common
+	static Collection<String> getCollection(String key, Metadata headers) {
+		return Optional.ofNullable(headers.getAll(createKeyOf(key)))
+				.map(vals -> StreamSupport.stream(vals.spliterator(), false))
+				.orElseGet(Stream::empty)
+				.map(bytes -> new String(bytes, StandardCharsets.UTF_8))
+				.toList();
+	}
+
+	// TODO move to a grpcUtil class in common
+	static Key<byte[]> createKeyOf(String key) {
+		return Key.of(key, Metadata.BINARY_BYTE_MARSHALLER);
+	}
+
+}
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/common/HttpRequestInterceptor.java b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/HttpRequestInterceptor.java
similarity index 65%
rename from user-manager-server/src/main/java/de/itvsh/kop/user/common/HttpRequestInterceptor.java
rename to user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/HttpRequestInterceptor.java
index 90d148c87317430848c5aee922289426dec670bb..d9151b813c0ed1e57bdec1152cf82ba8e211ca59 100644
--- a/user-manager-server/src/main/java/de/itvsh/kop/user/common/HttpRequestInterceptor.java
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/HttpRequestInterceptor.java
@@ -1,4 +1,4 @@
-package de.itvsh.kop.user.common;
+package de.itvsh.kop.user.common.callcontext;
 
 import java.io.IOException;
 
@@ -12,19 +12,23 @@ import org.apache.logging.log4j.CloseableThreadContext;
 
 @Provider
 public class HttpRequestInterceptor implements ReaderInterceptor {
-	private static final String REQUEST_ID_KEY = "requestId";
-	private static final String REQUEST_ID_PLACEHOLDER = "no-requestId-given";
+	static final String REQUEST_ID_KEY = "requestId";
+	static final String REQUEST_ID_PLACEHOLDER = "no-requestId-given";
 
 	@Override
 	public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
-		var requestId = context.getHeaders().getFirst(REQUEST_ID_KEY);
+
 		Object obj = null;
 
-		try (final CloseableThreadContext.Instance ctc = CloseableThreadContext.put(REQUEST_ID_KEY,
-				StringUtils.isNotEmpty(requestId) ? requestId : REQUEST_ID_PLACEHOLDER)) {
+		try (final CloseableThreadContext.Instance ctc = CloseableThreadContext.put(REQUEST_ID_KEY, getRequestId(context))) {
 			obj = context.proceed();
 		}
 
 		return obj;
 	}
+
+	String getRequestId(ReaderInterceptorContext context) {
+		var requestId = context.getHeaders().getFirst(REQUEST_ID_KEY);
+		return StringUtils.isNotEmpty(requestId) ? requestId : REQUEST_ID_PLACEHOLDER;
+	}
 }
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/HttpSecurityFilter.java b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/HttpSecurityFilter.java
new file mode 100644
index 0000000000000000000000000000000000000000..0386d543bd51507a6a45cee4c55c76f9db2484f9
--- /dev/null
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/common/callcontext/HttpSecurityFilter.java
@@ -0,0 +1,65 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.ext.Provider;
+
+import org.eclipse.microprofile.jwt.JsonWebToken;
+
+@Provider
+@PreMatching
+public class HttpSecurityFilter implements ContainerRequestFilter {
+	public static final String ORGANSIATIONSEINHEIT_IDS_CLAIM = "organisationseinheitIds";
+	public static final String USERID_CLAIM = "userId";
+
+	@Inject
+	CurrentCallContextUserService userService;
+
+	@Inject
+	JsonWebToken jwt;
+
+	@Override
+	public void filter(ContainerRequestContext requestContext) throws IOException {
+		userService.setCallContextUser(createCallContextUser());
+	}
+
+	CallContextUser createCallContextUser() {
+		var builder = CallContextUser.builder()
+				.authenticated(true)
+				.userName(Optional.ofNullable(jwt.getSubject()))
+				.organisatorischeEinheitenIds(getOrganisationseinheitIds())
+				.userId(getUserId());
+
+		return builder.build();
+	}
+
+	@SuppressWarnings("unchecked")
+	List<String> getOrganisationseinheitIds() {
+		List<String> organisationseinheitIds = new ArrayList<>();
+
+		var claimValue = jwt.getClaim(ORGANSIATIONSEINHEIT_IDS_CLAIM);
+		if (Objects.nonNull(claimValue)) {
+			organisationseinheitIds.addAll((Collection<String>) claimValue);
+		}
+
+		return organisationseinheitIds;
+	}
+
+	private Optional<String> getUserId() {
+		var claimValue = jwt.getClaim(USERID_CLAIM);
+		if (Objects.nonNull(claimValue)) {
+			return Optional.of(claimValue.toString());
+		}
+
+		return Optional.empty();
+	}
+}
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/keycloak/KeycloakApiService.java b/user-manager-server/src/main/java/de/itvsh/kop/user/keycloak/KeycloakApiService.java
index e98bc4b43da8940e5a12c6bee1324e18642cb4f6..57c9c658d003d9e0e9899edf3e9c6c95646aea74 100644
--- a/user-manager-server/src/main/java/de/itvsh/kop/user/keycloak/KeycloakApiService.java
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/keycloak/KeycloakApiService.java
@@ -17,9 +17,7 @@ import org.keycloak.representations.idm.UserRepresentation;
 import de.itvsh.kop.user.RemoteUserIterator;
 import de.itvsh.kop.user.User;
 import de.itvsh.kop.user.UserResourceMapper;
-import de.itvsh.kop.user.common.errorhandling.KeycloakClientException;
 import de.itvsh.kop.user.common.errorhandling.KeycloakUnavailableException;
-import io.quarkus.logging.Log;
 
 @ApplicationScoped
 class KeycloakApiService {
@@ -54,14 +52,8 @@ class KeycloakApiService {
 	private <T> T handlingKeycloakException(Supplier<T> runnable) {
 		try {
 			return runnable.get();
-		} catch (ClientErrorException e) {
-			var exception = new KeycloakClientException(properties.user(), properties.realm(), keycloakUrl, e);
-			Log.error(exception.getMessage());
-			throw exception;
-		} catch (ProcessingException | IllegalStateException e) {
-			var exception = new KeycloakUnavailableException(properties.user(), properties.realm(), keycloakUrl, e);
-			Log.error(exception.getMessage());
-			throw exception;
+		} catch (ClientErrorException | ProcessingException | IllegalStateException e) {
+			throw new KeycloakUnavailableException(properties.user(), properties.realm(), keycloakUrl, e);
 		}
 	}
 }
\ No newline at end of file
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/settings/UserSettingsResource.java b/user-manager-server/src/main/java/de/itvsh/kop/user/settings/UserSettingsResource.java
index ece380b283a92bf7a9ae754c67e8ffbdaa2bd38d..6714e440a255e37009ef04aad1d473c58403234b 100644
--- a/user-manager-server/src/main/java/de/itvsh/kop/user/settings/UserSettingsResource.java
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/settings/UserSettingsResource.java
@@ -8,14 +8,16 @@ import javax.ws.rs.PATCH;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
 
-import de.itvsh.kop.user.User;
-import de.itvsh.kop.user.UserService;
 import org.eclipse.microprofile.jwt.JsonWebToken;
 import org.jboss.resteasy.reactive.ResponseStatus;
 import org.jboss.resteasy.reactive.common.util.RestMediaType;
 
+import de.itvsh.kop.user.User;
+import de.itvsh.kop.user.UserService;
 import de.itvsh.kop.user.common.errorhandling.AccessForbiddenException;
 import de.itvsh.kop.user.common.errorhandling.FunctionalException;
 import io.quarkus.hal.HalEntityWrapper;
@@ -23,7 +25,7 @@ import io.quarkus.hal.HalEntityWrapper;
 @Path("/api/user")
 public class UserSettingsResource {
 
-	public static final String SETTINGS_LINK_PATTERN = "/api/user/%s/settings";
+	public static final String SETTINGS_LINK_PATTERN = "api/user/{0}/settings";
 	@Inject
 	UserSettingsService userSettingsService;
 
@@ -36,13 +38,16 @@ public class UserSettingsResource {
 	@Inject
 	UserSettingsResourceAssembler resourceAssembler;
 
+	@Context
+	UriInfo uriInfo;
+
 	@GET
 	@Path("/{id}/settings")
 	@Produces({ MediaType.APPLICATION_JSON, RestMediaType.APPLICATION_HAL_JSON })
 	public HalEntityWrapper getUserSettings(@PathParam("id") String userId) {
 		checkUserAccess(userId);
 
-		return resourceAssembler.toUserSettingResource(userSettingsService.findUserSettings(userId), userId);
+		return resourceAssembler.toUserSettingResource(userSettingsService.findUserSettings(userId), userId, uriInfo);
 	}
 
 	@PATCH
@@ -56,8 +61,8 @@ public class UserSettingsResource {
 			throw new FunctionalException(() -> "Request Body missing.");
 		}
 		var updatedSettings = userSettingsService.updateUserSettingsByUserId(userSettings, userId);
-		
-		return resourceAssembler.toUserSettingResource(updatedSettings, userId);
+
+		return resourceAssembler.toUserSettingResource(updatedSettings, userId, uriInfo);
 	}
 
 	void checkUserAccess(String userId) {
diff --git a/user-manager-server/src/main/java/de/itvsh/kop/user/settings/UserSettingsResourceAssembler.java b/user-manager-server/src/main/java/de/itvsh/kop/user/settings/UserSettingsResourceAssembler.java
index 579c85fb64cd9635168236dd297274e5295411fd..3b847b0010314e67e10de3a9d007accb9073e672 100644
--- a/user-manager-server/src/main/java/de/itvsh/kop/user/settings/UserSettingsResourceAssembler.java
+++ b/user-manager-server/src/main/java/de/itvsh/kop/user/settings/UserSettingsResourceAssembler.java
@@ -2,6 +2,7 @@ package de.itvsh.kop.user.settings;
 
 import javax.enterprise.context.ApplicationScoped;
 import javax.ws.rs.core.Link;
+import javax.ws.rs.core.UriInfo;
 
 import io.quarkus.hal.HalEntityWrapper;
 
@@ -10,11 +11,12 @@ class UserSettingsResourceAssembler {
 	static final String REL_SELF = "self";
 	static final String REL_EDIT = "edit";
 
-	HalEntityWrapper toUserSettingResource(UserSettings setting, String userId) {
+	HalEntityWrapper toUserSettingResource(UserSettings setting, String userId, UriInfo uriInfo) {
 		var result = new HalEntityWrapper(setting);
 
-		result.addLinks(Link.fromPath(UserSettingsResource.SETTINGS_LINK_PATTERN.formatted(userId)).rel(REL_EDIT).build());
-		result.addLinks(Link.fromPath(UserSettingsResource.SETTINGS_LINK_PATTERN.formatted(userId)).rel(REL_SELF).build());
+		result.addLinks(
+				Link.fromUri(uriInfo.getBaseUri().toString() + UserSettingsResource.SETTINGS_LINK_PATTERN).rel(REL_EDIT).build(userId),
+				Link.fromUri(uriInfo.getBaseUri().toString() + UserSettingsResource.SETTINGS_LINK_PATTERN).rel(REL_SELF).build(userId));
 
 		return result;
 	}
diff --git a/user-manager-server/src/main/resources/application-remotekc.yaml b/user-manager-server/src/main/resources/application-remotekc.yaml
index 4384aa9b58610c4fe9cc823e81ecfb815865dbdf..e7b0d18daad528f4061e8c58c8d74a6f4983ad8d 100644
--- a/user-manager-server/src/main/resources/application-remotekc.yaml
+++ b/user-manager-server/src/main/resources/application-remotekc.yaml
@@ -8,7 +8,6 @@ quarkus:
       origins: http://localhost:4300
 kop:
   keycloak:
-    url: https://sso.dev.ozg-sh.de/auth
     sync:
       cron: "* */10 * * * ?"
     api:
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserProfileResourceAssemblerTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/UserProfileResourceAssemblerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..db47ad1cdcaef0f83bec931d5820e992e5b7f13b
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/UserProfileResourceAssemblerTest.java
@@ -0,0 +1,54 @@
+package de.itvsh.kop.user;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.bson.types.ObjectId;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+class UserProfileResourceAssemblerTest {
+
+	@Nested
+	class TestAssembler {
+
+		@Spy
+		private UserProfileResourceAssembler assembler;
+
+		@Mock
+		private UriInfo uriInfo;
+
+		private static final ObjectId ID = new ObjectId();
+
+		@BeforeEach
+		void init() throws URISyntaxException {
+			when(uriInfo.getBaseUri()).thenReturn(new URI("http://test:8080/"));
+
+		}
+
+		@Test
+		void shouldAddSelfLink() {
+			var res = assembler.toUserProfileResource(UserTestFactory.createBuilder().id(ID).build(), uriInfo);
+
+			assertThat(res.getLinks().get(UserProfileResourceAssembler.REL_SELF)).isNotNull().hasFieldOrPropertyWithValue("href",
+					"http://test:8080/api/userProfiles/" + ID.toHexString());
+		}
+
+		@Test
+		void shouldAddSettingsLink() {
+			var res = assembler.toUserProfileResource(UserTestFactory.createBuilder().id(ID).build(), uriInfo);
+
+			assertThat(res.getLinks().get(UserProfileResourceAssembler.REL_SETTINGS)).isNotNull().hasFieldOrPropertyWithValue("href",
+					"http://test:8080/api/user/" + ID.toHexString() + "/settings");
+		}
+	}
+
+}
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserProfileResourceITCase.java b/user-manager-server/src/test/java/de/itvsh/kop/user/UserProfileResourceITCase.java
index add9fe736b4e08bf662de4a04ae8c6064fb5c236..626ba347b5153baecdc03b5cff8fc336e7369aeb 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserProfileResourceITCase.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/UserProfileResourceITCase.java
@@ -1,22 +1,27 @@
 package de.itvsh.kop.user;
 
-import de.itvsh.kop.user.settings.UserSettingsResource;
-import io.quarkus.test.junit.QuarkusTest;
-import io.quarkus.test.junit.TestProfile;
+import static io.restassured.RestAssured.*;
+
+import java.text.MessageFormat;
+
+import javax.inject.Inject;
+import javax.ws.rs.core.MediaType;
+
 import org.apache.http.HttpStatus;
 import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import javax.inject.Inject;
-import javax.ws.rs.core.MediaType;
-
-import static io.restassured.RestAssured.*;
+import de.itvsh.kop.user.settings.UserSettingsResourceTestProfile;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.junit.TestProfile;
 
 @QuarkusTest
 @TestProfile(UserSettingsResourceTestProfile.class)
 class UserProfileResourceITCase {
 
+	private static final String HTTP_LOCALHOST = "http://localhost:8081/";
+
 	@Inject
 	private UserRepository userRepository;
 
@@ -30,14 +35,15 @@ class UserProfileResourceITCase {
 
 	@Test
 	void shouldGetUserWithHyperLinks() {
-		var userId  = user.getId();
+		var userId = user.getId();
+		String url = MessageFormat.format(UserProfileResource.USER_PROFILE_RESOURCE_PATH_TEMPLATE, userId);
 		given()
 				.when()
 				.accept(MediaType.APPLICATION_JSON)
-				.get(UserProfileResource.USER_PROFILE_RESOURCE_PATH_PREFIX + userId)
+				.get(url)
 				.then()
 				.statusCode(HttpStatus.SC_OK)
-				.body("_links.settings.href", CoreMatchers.is(UserSettingsResource.SETTINGS_LINK_PATTERN.formatted(userId)))
-				.body("_links.self.href", CoreMatchers.is(UserProfileResource.USER_PROFILE_RESOURCE_PATH_PREFIX + userId));
+				.body("_links.settings.href", CoreMatchers.is(HTTP_LOCALHOST + "api/user/" + userId + "/settings"))
+				.body("_links.self.href", CoreMatchers.is(HTTP_LOCALHOST + url));
 	}
 }
\ No newline at end of file
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserTestFactory.java b/user-manager-server/src/test/java/de/itvsh/kop/user/UserTestFactory.java
index f6f3d9b9027508d59af36a8e1f7386fc755dafeb..d497b0140f3a760a95bdf6401a468e5e9a9ffb27 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserTestFactory.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/UserTestFactory.java
@@ -7,6 +7,8 @@ import org.bson.types.ObjectId;
 
 import com.thedeanda.lorem.LoremIpsum;
 
+import de.itvsh.kop.user.User;
+import de.itvsh.kop.user.User.UserBuilder;
 import de.itvsh.kop.user.settings.NotificationsSendFor;
 import de.itvsh.kop.user.settings.UserSettings;
 
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/CallContextMetadataTestFactory.java b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/CallContextMetadataTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..09d92cea80d86e729ec0fa8a731419c6d1dea45f
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/CallContextMetadataTestFactory.java
@@ -0,0 +1,35 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import java.util.UUID;
+
+import org.bson.types.ObjectId;
+
+import com.thedeanda.lorem.LoremIpsum;
+
+import io.grpc.Metadata;
+
+public class CallContextMetadataTestFactory {
+
+	public static final String CLIENT = "testclient-0817";
+	public static final String ID = new ObjectId().toHexString();
+	public static final String NAME = LoremIpsum.getInstance().getName();
+	public static final String ORGANISATORISCHE_EINHEITEN_ID = "0815";
+
+	public static final String REQUEST_ID = UUID.randomUUID().toString();
+	public static final Boolean DO_ORGA_ID_ACCESS_CHECK = Boolean.TRUE;
+
+	public static Metadata createMetadata() {
+		var result = new Metadata();
+		result.put(GrpcCallContextInterceptor.createKeyOf(GrpcCallContextInterceptor.KEY_USER_ID), ID.getBytes());
+		result.put(GrpcCallContextInterceptor.createKeyOf(GrpcCallContextInterceptor.KEY_USER_NAME), NAME.getBytes());
+		result.put(GrpcCallContextInterceptor.createKeyOf(GrpcCallContextInterceptor.KEY_CLIENT_NAME), CLIENT.getBytes());
+		result.put(GrpcCallContextInterceptor.createKeyOf(GrpcCallContextInterceptor.KEY_ACCESS_LIMITED_ORGAID),
+				ORGANISATORISCHE_EINHEITEN_ID.getBytes());
+		result.put(GrpcCallContextInterceptor.createKeyOf(GrpcCallContextInterceptor.KEY_REQUEST_ID), REQUEST_ID.getBytes());
+		result.put(GrpcCallContextInterceptor.createKeyOf(GrpcCallContextInterceptor.KEY_ACCESS_LIMITED),
+				DO_ORGA_ID_ACCESS_CHECK.toString().getBytes());
+
+		return result;
+	}
+
+}
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/CallContextUserTestFactory.java b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/CallContextUserTestFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..696e5a497bb20b5598dbe3dfe7ec16c89b5bd59f
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/CallContextUserTestFactory.java
@@ -0,0 +1,24 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import java.util.List;
+import java.util.Optional;
+
+public class CallContextUserTestFactory {
+	static final String NAME = CallContextMetadataTestFactory.NAME;
+	static final String ID = CallContextMetadataTestFactory.ID;
+	static final List<String> ORGANISATORISCHE_EINHEITEN_ID = List.of(CallContextMetadataTestFactory.ORGANISATORISCHE_EINHEITEN_ID);
+
+	public static CallContextUser create() {
+		return createBuilder().build();
+	}
+
+	public static CallContextUser.CallContextUserBuilder createBuilder() {
+		return CallContextUser.builder()
+				.clientName(CallContextMetadataTestFactory.CLIENT)
+				.userId(Optional.of(CallContextMetadataTestFactory.ID))
+				.userName(Optional.of(CallContextMetadataTestFactory.NAME))
+				.organisatorischeEinheitenId(CallContextMetadataTestFactory.ORGANISATORISCHE_EINHEITEN_ID)
+				.authenticated(true)
+				.organisationEinheitenIdCheckNecessary(CallContextMetadataTestFactory.DO_ORGA_ID_ACCESS_CHECK);
+	}
+}
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/CurrentCallContextUserServiceTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/CurrentCallContextUserServiceTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..601a68b5fbf07ec5f22f8777d925cc79104c0175
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/CurrentCallContextUserServiceTest.java
@@ -0,0 +1,28 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import static org.assertj.core.api.Assertions.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Spy;
+
+class CurrentCallContextUserServiceTest {
+	@Spy
+	private CurrentCallContextUserService userService;
+
+	@Nested
+	class TestService {
+		CallContextUser user = CallContextUserTestFactory.create();
+
+		@BeforeEach
+		void init() {
+			userService.setCallContextUser(user);
+		}
+
+		@Test
+		void shouldGetUser() {
+			assertThat(userService.getCurrentCallContextUser()).isPresent().get().isEqualTo(user);
+		}
+	}
+}
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/GrpcCallContextInterceptorTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/GrpcCallContextInterceptorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2be53e0cfa83f2c9fbe7050581ada500cfb1c584
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/GrpcCallContextInterceptorTest.java
@@ -0,0 +1,231 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import static de.itvsh.kop.user.common.callcontext.GrpcCallContextInterceptor.*;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import de.itvsh.kop.user.common.callcontext.GrpcCallContextInterceptor.LogContextSettingListener;
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+
+class CallContextHandleInterceptorTest {
+
+	@InjectMocks
+	private GrpcCallContextInterceptor interceptor;
+
+	@Mock
+	private ServerCall.Listener<Object> delegate;
+
+	@Spy
+	private CurrentCallContextUserService userService;
+
+	@Nested
+	class TestCallListener {
+		private LogContextSettingListener<?> listener;
+		private Metadata headers = CallContextMetadataTestFactory.createMetadata();
+
+		@BeforeEach
+		void createListener() {
+			listener = spy(interceptor.new LogContextSettingListener<>(delegate, headers));
+		}
+
+		@Nested
+		class TestGetRequestId {
+			@Test
+			void shouldReturnIdFromHeader() {
+				var reqId = listener.getRequestId();
+
+				assertThat(reqId).isEqualTo(CallContextMetadataTestFactory.REQUEST_ID);
+			}
+
+			@Test
+			void shouldReturnNewRequestIdIfNoGiven() {
+				headers.removeAll(createKeyOf(KEY_REQUEST_ID));
+
+				var reqId = listener.getRequestId();
+
+				assertThat(reqId).isNotBlank().isNotEqualTo(CallContextMetadataTestFactory.REQUEST_ID);
+			}
+
+		}
+
+		@DisplayName("Map headers to user")
+		@Nested
+		class TestHeadersToUser {
+			@Test
+			void shouldFilledUser() {
+				var user = buildListener().createCallContextUser();
+
+				assertThat(user).usingRecursiveComparison().isEqualTo(CallContextUserTestFactory.create());
+			}
+
+			@Test
+			void shouldFillOrgaIdCollection() {
+				Metadata metadata = CallContextMetadataTestFactory.createMetadata();
+				metadata.put(createKeyOf(KEY_ACCESS_LIMITED_ORGAID), "orgaid_2".getBytes());
+
+				var user = buildListener(metadata).createCallContextUser();
+
+				assertThat(user.getOrganisatorischeEinheitenIds()).hasSize(2).contains(
+						CallContextMetadataTestFactory.ORGANISATORISCHE_EINHEITEN_ID,
+						"orgaid_2");
+			}
+
+			@Test
+			void shouldFillEmptyListIfNoOrgaIds() {
+				Metadata metadata = CallContextMetadataTestFactory.createMetadata();
+				metadata.removeAll(createKeyOf(KEY_ACCESS_LIMITED_ORGAID));
+
+				var user = buildListener(metadata).createCallContextUser();
+
+				assertThat(user.getOrganisatorischeEinheitenIds()).isEmpty();
+			}
+
+			@DisplayName("for key isOrganisationEinheitenIdCheckNecessary")
+			@Nested
+			class TestIsOrganisationEinheitenIdCheckNecessary {
+
+				@Test
+				void shouldMapValueIfKeyIsPresent() {
+					var metadata = CallContextMetadataTestFactory.createMetadata();
+
+					var user = buildListener(metadata).createCallContextUser();
+
+					assertThat(user.isOrganisationEinheitenIdCheckNecessary()).isTrue();
+				}
+
+				@Test
+				void shouldMapFalseIfKeyIsNotPresent() {
+					var metadata = CallContextMetadataTestFactory.createMetadata();
+					metadata.removeAll(createKeyOf(KEY_ACCESS_LIMITED));
+
+					var user = buildListener(metadata).createCallContextUser();
+
+					assertThat(user.isOrganisationEinheitenIdCheckNecessary()).isFalse();
+				}
+			}
+
+		}
+
+		@Nested
+		class TestStartSecurityContext {
+			@Test
+			void shouldHaveAuthenticatedAuthorization() {
+
+				listener.startSecurityContext();
+
+				var userOptional = userService.getCurrentCallContextUser();
+
+				assertThat(userOptional).isPresent();
+				assertThat(userOptional.get().isAuthenticated()).isTrue();
+			}
+
+		}
+
+		@Nested
+		class TestDoSurround {
+
+			private Runnable mockRunnable = mock(Runnable.class);
+
+			@Test
+			void shouldRunRunnable() {
+				listener.doSurroundOn(mockRunnable);
+
+				verify(mockRunnable).run();
+			}
+
+			@Test
+			void shouldStartSecurityContext() {
+				listener.doSurroundOn(mockRunnable);
+
+				verify(listener).startSecurityContext();
+			}
+
+			@Test
+			void shouldClearSecurityContext() {
+				listener.doSurroundOn(mockRunnable);
+
+				// verify(listener).clearSecurityContext();
+			}
+
+			@Test
+			void shouldClearSecurityContextOnException() {
+				var testException = new RuntimeException() {
+				};
+
+				assertThatThrownBy(() -> listener.doSurroundOn(() -> {
+					throw testException;
+				})).isSameAs(testException);
+
+				// verify(listener).clearSecurityContext();
+			}
+		}
+
+		@Nested
+		class TestOnFunctions {
+
+			private LogContextSettingListener<Object> listener;
+
+			@BeforeEach
+			void initListener() {
+				listener = buildListener();
+			}
+
+			@Test
+			void onMessageShouldCallSurround() {
+				Object message = mock(Object.class);
+
+				listener.onMessage(message);
+
+				verify(listener).doSurroundOn(any());
+			}
+
+			@Test
+			void onHalfCloseShouldCallSurround() {
+				listener.onHalfClose();
+
+				verify(listener).doSurroundOn(any());
+			}
+
+			@Test
+			void onCancelShouldCallSurround() {
+				listener.onCancel();
+
+				verify(listener).doSurroundOn(any());
+			}
+
+			@Test
+			void onCompleteShouldCallSurround() {
+				listener.onComplete();
+
+				verify(listener).doSurroundOn(any());
+			}
+
+			@Test
+			void onReadyShouldCallSurround() {
+				listener.onReady();
+
+				verify(listener).doSurroundOn(any());
+			}
+		}
+
+		private LogContextSettingListener<Object> buildListener() {
+			return buildListener(headers);
+		}
+
+		private LogContextSettingListener<Object> buildListener(Metadata headers) {
+			return spy(interceptor.new LogContextSettingListener<Object>(delegate, headers));
+		}
+
+	}
+
+}
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/HttpRequestInterceptorTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/HttpRequestInterceptorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2c89ff8cbb028401ea295edb52e79f1f0623ccb1
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/HttpRequestInterceptorTest.java
@@ -0,0 +1,69 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import java.util.List;
+import java.util.UUID;
+
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.ext.ReaderInterceptorContext;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+import de.itvsh.kop.user.common.callcontext.HttpRequestInterceptor;
+
+class HttpRequestInterceptorTest {
+	@Spy
+	private HttpRequestInterceptor interceptor;
+
+	@Nested
+	class TestInterceptor {
+
+		@Mock
+		private ReaderInterceptorContext context;
+
+		@Nested
+		class WithRequestId {
+			String id = UUID.randomUUID().toString();
+
+			@BeforeEach
+			void initHeader() {
+				var headers = new MultivaluedHashMap<String, String>();
+
+				headers.put(HttpRequestInterceptor.REQUEST_ID_KEY, List.of(id));
+				when(context.getHeaders()).thenReturn(headers);
+			}
+
+			@Test
+			void shouldSetRequestId() {
+				var requestId = interceptor.getRequestId(context);
+
+				assertThat(requestId).isEqualTo(id);
+			}
+		}
+
+		@Nested
+		class WithoutRequestId {
+
+			@BeforeEach
+			void initHeader() {
+				var headers = new MultivaluedHashMap<String, String>();
+				headers.put("xxx", List.of("y"));
+				when(context.getHeaders()).thenReturn(headers);
+			}
+
+			@Test
+			void shouldSetDummyRequestId() {
+				var requestId = interceptor.getRequestId(context);
+
+				assertThat(requestId).isEqualTo(HttpRequestInterceptor.REQUEST_ID_PLACEHOLDER);
+			}
+		}
+
+	}
+}
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/HttpSecurityFilterTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/HttpSecurityFilterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3bb0723aa53e612793c44577859bab5d3753c6f
--- /dev/null
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/common/callcontext/HttpSecurityFilterTest.java
@@ -0,0 +1,94 @@
+package de.itvsh.kop.user.common.callcontext;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+
+import javax.ws.rs.container.ContainerRequestContext;
+
+import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Spy;
+
+class HttpSecurityFilterTest {
+	@Spy
+	@InjectMocks
+	private HttpSecurityFilter securityFilter;
+
+	@Mock
+	private JsonWebToken token;
+
+	@Mock
+	private CurrentCallContextUserService contextUserService;
+
+	@Mock
+	private ContainerRequestContext requestContext;
+
+	@Nested
+	class TestSettingUser {
+		@Test
+		void shouldSetUser() throws IOException {
+			when(token.getClaim(HttpSecurityFilter.USERID_CLAIM)).thenReturn(CallContextUserTestFactory.ID);
+			when(token.getClaim(HttpSecurityFilter.ORGANSIATIONSEINHEIT_IDS_CLAIM))
+					.thenReturn(CallContextUserTestFactory.ORGANISATORISCHE_EINHEITEN_ID);
+
+			securityFilter.filter(requestContext);
+
+			verify(contextUserService).setCallContextUser(any());
+		}
+	}
+
+	@Nested
+	class TestCreateCallContextUser {
+		@BeforeEach
+		void init() {
+			when(token.getClaim(HttpSecurityFilter.USERID_CLAIM)).thenReturn(CallContextUserTestFactory.ID);
+			when(token.getSubject()).thenReturn(CallContextUserTestFactory.NAME);
+			when(token.getClaim(HttpSecurityFilter.ORGANSIATIONSEINHEIT_IDS_CLAIM))
+					.thenReturn(CallContextUserTestFactory.ORGANISATORISCHE_EINHEITEN_ID);
+		}
+
+		@Test
+		void shouldBeAuthenticated() {
+			var user = securityFilter.createCallContextUser();
+
+			assertThat(user.isAuthenticated()).isTrue();
+		}
+
+		@Test
+		void shouldHaveUserId() {
+			var user = securityFilter.createCallContextUser();
+
+			assertThat(user.getUserId()).isPresent().get().isEqualTo(CallContextUserTestFactory.ID);
+		}
+
+		@Test
+		void shouldHaveUserName() {
+			var user = securityFilter.createCallContextUser();
+
+			assertThat(user.getUserName()).isPresent().get().isEqualTo(CallContextUserTestFactory.NAME);
+		}
+
+		@Test
+		void shouldHaveOrganistaionsEinheitenIds() {
+			var user = securityFilter.createCallContextUser();
+
+			assertThat(user.getOrganisatorischeEinheitenIds()).isNotEmpty();
+		}
+
+		@Test
+		void shouldNotHaveUserName() {
+			when(token.getSubject()).thenReturn(null);
+
+			var user = securityFilter.createCallContextUser();
+
+			assertThat(user.getUserName()).isNotPresent();
+		}
+	}
+}
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/recipient/RecipientMapperTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/recipient/RecipientMapperTest.java
index 8ee4f8edca5b1745cbf8eec8f015112a2e5d8209..fa367a57aafbf9b29f49357e14b9d533d400e34b 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/recipient/RecipientMapperTest.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/recipient/RecipientMapperTest.java
@@ -1,9 +1,10 @@
 package de.itvsh.kop.user.recipient;
 
-import de.itvsh.kop.user.UserTestFactory;
 import org.junit.jupiter.api.Test;
 import org.mapstruct.factory.Mappers;
 
+import de.itvsh.kop.user.UserTestFactory;
+
 import static org.assertj.core.api.Assertions.*;
 
 class RecipientMapperTest {
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/recipient/RecipientTestFactory.java b/user-manager-server/src/test/java/de/itvsh/kop/user/recipient/RecipientTestFactory.java
index 83ac2a5ceda4342bdd01da0a536ee5a0ed7eb2b7..01a03d7027c438c8f979055d83fcaffe6761a926 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/recipient/RecipientTestFactory.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/recipient/RecipientTestFactory.java
@@ -1,11 +1,11 @@
 package de.itvsh.kop.user.recipient;
 
+import static de.itvsh.kop.user.UserTestFactory.*;
+
 import de.itvsh.kop.user.UserTestFactory;
 import de.itvsh.kop.user.grpc.Recipient;
 import de.itvsh.kop.user.grpc.RecipientSearchRequest;
 
-import static de.itvsh.kop.user.UserTestFactory.*;
-
 public class RecipientTestFactory {
 
 	public static final String ORGANISTATIONSEINHEITEN_ID = UserTestFactory.ORGANISTATIONSEINHEITEN_IDS.get(0);
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingResourceAssemblerTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingResourceAssemblerTest.java
index 298c5a019540a8ae0e978fecc5f2e2e8e3dba291..fd42b488728ac41a1ab824b84f833d6ac8e24271 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingResourceAssemblerTest.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingResourceAssemblerTest.java
@@ -1,13 +1,19 @@
 package de.itvsh.kop.user.settings;
 
 import static org.assertj.core.api.Assertions.*;
+import static org.mockito.Mockito.*;
 
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.ws.rs.core.UriInfo;
+
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
 import org.mockito.Spy;
 
-import de.itvsh.kop.user.UserSettingsTestFactory;
-
 class UserSettingResourceAssemblerTest {
 
 	@Nested
@@ -16,20 +22,30 @@ class UserSettingResourceAssemblerTest {
 		@Spy
 		private UserSettingsResourceAssembler assembler;
 
+		@Mock
+		private UriInfo uriInfo;
+
 		private static final String ID = "id";
 
+		@BeforeEach
+		void init() throws URISyntaxException {
+			when(uriInfo.getBaseUri()).thenReturn(new URI("http://test:8080/"));
+		}
+
 		@Test
 		void shouldAddSelfLink() {
-			var res = assembler.toUserSettingResource(UserSettingsTestFactory.create(), ID);
+			var res = assembler.toUserSettingResource(UserSettingsTestFactory.create(), ID, uriInfo);
 
-			assertThat(res.getLinks().get(UserSettingsResourceAssembler.REL_SELF)).isNotNull();
+			assertThat(res.getLinks().get(UserSettingsResourceAssembler.REL_SELF)).isNotNull().hasFieldOrPropertyWithValue("href",
+					"http://test:8080/api/user/" + ID + "/settings");
 		}
 
 		@Test
 		void shouldAddEditLink() {
-			var res = assembler.toUserSettingResource(UserSettingsTestFactory.create(), ID);
+			var res = assembler.toUserSettingResource(UserSettingsTestFactory.create(), ID, uriInfo);
 
-			assertThat(res.getLinks().get(UserSettingsResourceAssembler.REL_EDIT)).isNotNull();
+			assertThat(res.getLinks().get(UserSettingsResourceAssembler.REL_EDIT)).isNotNull().hasFieldOrPropertyWithValue("href",
+					"http://test:8080/api/user/" + ID + "/settings");
 		}
 	}
 
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsResourceITCase.java b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceITCase.java
similarity index 80%
rename from user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsResourceITCase.java
rename to user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceITCase.java
index b914f98246305b90f2bef421b0a04f3a35c7f5a4..d25783fccb642a610c688c597839c1a41039e6fe 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsResourceITCase.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceITCase.java
@@ -1,4 +1,4 @@
-package de.itvsh.kop.user;
+package de.itvsh.kop.user.settings;
 
 import static io.restassured.RestAssured.*;
 import static org.hamcrest.CoreMatchers.*;
@@ -11,8 +11,8 @@ import org.eclipse.microprofile.jwt.JsonWebToken;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import de.itvsh.kop.user.settings.NotificationsSendFor;
-import de.itvsh.kop.user.settings.UserSettings;
+import de.itvsh.kop.user.UserRepository;
+import de.itvsh.kop.user.UserTestFactory;
 import io.quarkus.test.junit.QuarkusTest;
 import io.quarkus.test.junit.TestProfile;
 
@@ -21,6 +21,8 @@ import io.quarkus.test.junit.TestProfile;
 class UserSettingsResourceITCase {
 	private static final String API_PATTERN = "/api/user/{id}/settings";
 
+	private static final String HTTP_LOCALHOST = "http://localhost:8081/";
+
 	@Inject
 	UserRepository userRepository;
 
@@ -48,7 +50,7 @@ class UserSettingsResourceITCase {
 				.then()
 				.statusCode(HttpStatus.SC_OK)
 				.body("notificationsSendFor", is("ALL"))
-				.body("_links.edit.href", is("/api/user/" + userId + "/settings"));
+				.body("_links.edit.href", is(HTTP_LOCALHOST + "api/user/" + userId + "/settings"));
 	}
 
 	@Test
@@ -62,6 +64,6 @@ class UserSettingsResourceITCase {
 				.then()
 				.statusCode(HttpStatus.SC_OK)
 				.body("notificationsSendFor", is("NONE"))
-				.body("_links.self.href", is("/api/user/" + userId	 + "/settings"));
+				.body("_links.self.href", is(HTTP_LOCALHOST + "api/user/" + userId + "/settings"));
 	}
 }
\ No newline at end of file
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceTest.java
index ca0a519a93f6c6bdff6e5e9675a01b8694619adf..e763f9bebbf9e0ed7b7c917a4597929fce431e49 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceTest.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceTest.java
@@ -4,11 +4,12 @@ import static org.assertj.core.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.*;
 import static org.mockito.Mockito.*;
 
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.UUID;
 
-import de.itvsh.kop.user.UserService;
-import de.itvsh.kop.user.UserSettingsTestFactory;
-import de.itvsh.kop.user.UserTestFactory;
+import javax.ws.rs.core.UriInfo;
+
 import org.bson.types.ObjectId;
 import org.eclipse.microprofile.jwt.JsonWebToken;
 import org.junit.jupiter.api.BeforeEach;
@@ -18,6 +19,8 @@ import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.Spy;
 
+import de.itvsh.kop.user.UserService;
+import de.itvsh.kop.user.UserTestFactory;
 import de.itvsh.kop.user.common.errorhandling.AccessForbiddenException;
 
 class UserSettingsResourceTest {
@@ -35,6 +38,9 @@ class UserSettingsResourceTest {
 	@Mock
 	private UserSettingsService userSettingsService;
 
+	@Mock
+	private UriInfo uriInfo;
+
 	@Spy
 	private UserSettingsResourceAssembler resourceAssembler;
 
@@ -42,13 +48,13 @@ class UserSettingsResourceTest {
 
 	@Nested
 	class TestAccess {
-
 		@BeforeEach
-		void init() {
+		void init() throws URISyntaxException {
 			when(token.getSubject()).thenReturn(UserTestFactory.EXTERNAL_ID);
 			var user = UserTestFactory.createBuilder().id(new ObjectId()).build();
 			userId = user.getId().toHexString();
 			when(userService.findUserByExternalId(UserTestFactory.EXTERNAL_ID)).thenReturn(user);
+			when(uriInfo.getBaseUri()).thenReturn(new URI("http://test:8080"));
 		}
 
 		@Test
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsResourceTestProfile.java b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceTestProfile.java
similarity index 86%
rename from user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsResourceTestProfile.java
rename to user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceTestProfile.java
index 2985730a2b70d52079302f15ac00492191aef1e6..35774d07c2f153a6b2a5844c3ecd410d40b5ece0 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsResourceTestProfile.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsResourceTestProfile.java
@@ -1,13 +1,13 @@
-package de.itvsh.kop.user;
-
-import io.quarkus.test.junit.QuarkusTestProfile;
+package de.itvsh.kop.user.settings;
 
 import java.util.Map;
 
+import io.quarkus.test.junit.QuarkusTestProfile;
+
 public class UserSettingsResourceTestProfile implements QuarkusTestProfile {
 
 	@Override
-	public Map<String,String> getConfigOverrides() {
+	public Map<String, String> getConfigOverrides() {
 		return Map.of("quarkus.http.auth.permission.deny-api.enabled", "false",
 				"quarkus.http.auth.permission.deny-api.policy", "permit",
 				"quarkus.http.auth.permission.permit-migration.enabled", "false",
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsServiceTest.java b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsServiceTest.java
similarity index 88%
rename from user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsServiceTest.java
rename to user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsServiceTest.java
index c2c4ffa086e7a5a74f7dd2364f3cc4b3b859aeef..709e59481fbe2001d7a3a5b9bd76327fafd0e71e 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsServiceTest.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsServiceTest.java
@@ -1,4 +1,4 @@
-package de.itvsh.kop.user;
+package de.itvsh.kop.user.settings;
 
 import static org.assertj.core.api.Assertions.*;
 import static org.junit.jupiter.api.Assertions.*;
@@ -6,15 +6,15 @@ import static org.mockito.Mockito.*;
 
 import java.util.Optional;
 
-import de.itvsh.kop.user.settings.UserSettingsService;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 
+import de.itvsh.kop.user.User;
+import de.itvsh.kop.user.UserRepository;
+import de.itvsh.kop.user.UserTestFactory;
 import de.itvsh.kop.user.common.errorhandling.ResourceNotFoundException;
-import de.itvsh.kop.user.settings.NotificationsSendFor;
-import de.itvsh.kop.user.settings.UserSettings;
 
 public class UserSettingsServiceTest {
 	@InjectMocks
@@ -50,6 +50,5 @@ public class UserSettingsServiceTest {
 
 			assertThat(updatedUserSettings.getNotificationsSendFor()).isEqualTo(NotificationsSendFor.NONE);
 		}
-
 	}
 }
diff --git a/user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsTestFactory.java b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsTestFactory.java
similarity index 90%
rename from user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsTestFactory.java
rename to user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsTestFactory.java
index 40ddd295d5dd1d6e2e44c3067fbcbb53d2bce241..499be6404d8793a5b8bd118760bcacd96c1ed10c 100644
--- a/user-manager-server/src/test/java/de/itvsh/kop/user/UserSettingsTestFactory.java
+++ b/user-manager-server/src/test/java/de/itvsh/kop/user/settings/UserSettingsTestFactory.java
@@ -1,4 +1,4 @@
-package de.itvsh.kop.user;
+package de.itvsh.kop.user.settings;
 
 import de.itvsh.kop.user.settings.NotificationsSendFor;
 import de.itvsh.kop.user.settings.UserSettings;