Skip to content
Snippets Groups Projects
Commit f6433f5d authored by OZGCloud's avatar OZGCloud
Browse files

Merge branch 'master' into ozg-2502

parents 6843f496 8a84e490
Branches
Tags
No related merge requests found
Showing
with 501 additions and 61 deletions
......@@ -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'
}
}
}
......
......@@ -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,9 +138,8 @@
<!-- Logging -->
<dependency>
<groupId>io.quarkiverse.loggingjson</groupId>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-logging-json</artifactId>
<version>${quarkus-logging-json.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.logmanager</groupId>
......
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 {
}
......@@ -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);
}
}
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;
}
......
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;
}
}
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);
}
}
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);
}
}
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;
}
}
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();
}
}
......@@ -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
......@@ -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
......@@ -57,7 +62,7 @@ public class UserSettingsResource {
}
var updatedSettings = userSettingsService.updateUserSettingsByUserId(userSettings, userId);
return resourceAssembler.toUserSettingResource(updatedSettings, userId);
return resourceAssembler.toUserSettingResource(updatedSettings, userId, uriInfo);
}
void checkUserAccess(String userId) {
......
......@@ -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;
}
......
......@@ -8,7 +8,6 @@ quarkus:
origins: http://localhost:4300
kop:
keycloak:
url: https://sso.dev.ozg-sh.de/auth
sync:
cron: "* */10 * * * ?"
api:
......
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");
}
}
}
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;
......@@ -31,13 +36,14 @@ class UserProfileResourceITCase {
@Test
void shouldGetUserWithHyperLinks() {
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
......@@ -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;
......
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;
}
}
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);
}
}
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);
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment