This commit is contained in:
Timo Bejan 2023-07-10 15:49:45 +03:00
parent 8a2f8c0cb1
commit bad1226376
51 changed files with 990 additions and 130 deletions

View File

@ -31,6 +31,8 @@ services:
ports:
- 5672:5672
- 15672:15672
- 5671:5671
- 4369:4369
minio:
mem_limit: 1000m
image: minio/minio

View File

@ -78,14 +78,16 @@ dependencies {
implementation("commons-validator:commons-validator:1.7")
implementation("org.springframework.boot:spring-boot-configuration-processor")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.cloud:spring-cloud-starter-openfeign")
testImplementation("org.projectlombok:lombok")
compileOnly("org.projectlombok:lombok")
developmentOnly("org.springframework.boot:spring-boot-devtools")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
annotationProcessor("org.projectlombok:lombok")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.amqp:spring-rabbit-test")
testImplementation("org.testcontainers:elasticsearch:1.17.6")
testImplementation("org.testcontainers:postgresql:1.18.3")
testImplementation("com.github.dasniko:testcontainers-keycloak:2.5.0")
testAnnotationProcessor("org.projectlombok:lombok")
}

View File

@ -15,8 +15,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface GeneralSettingsResource {
String API_PATH = "/configuration/general";

View File

@ -0,0 +1,8 @@
package com.knecon.fforesight.tenantusermanagement.api.external;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface PublicResource {
}

View File

@ -16,8 +16,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface SMTPConfigurationResource {
String SMTP_PATH = "/configuration/smtp";

View File

@ -21,7 +21,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface TenantsResource {
String TENANT_ID_PARAM = "tenantId";

View File

@ -18,8 +18,6 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface UserPreferenceResource {
String PREFERENCES_PATH = "/attributes";

View File

@ -26,8 +26,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
@RequestMapping("${fforesight.tenant-user-management.base-path:}")
public interface UserResource {
String USER_REST_PATH = "/user";
@ -61,19 +59,19 @@ public interface UserResource {
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Update your own user-profile.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to update profile, e-mail cannot be empty")})
@PostMapping(value = UPDATE_USER_PROFILE_PATH + USER_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE)
void updateProfile(@PathVariable(USER_ID) String userId, @RequestBody UpdateProfileRequest updateProfileRequest);
@PostMapping(value = UPDATE_USER_PROFILE_PATH + USER_ID_PATH_VARIABLE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
User updateProfile(@PathVariable(USER_ID) String userId, @RequestBody UpdateProfileRequest updateProfileRequest);
@ResponseBody
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Update your own user-profile.", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "OK"), @ApiResponse(responseCode = "400", description = "Failed to update profile, e-mail cannot be empty")})
@PostMapping(value = UPDATE_MY_USER_PROFILE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void updateMyProfile(@RequestBody UpdateMyProfileRequest updateProfileRequest);
@PostMapping(value = UPDATE_MY_USER_PROFILE_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
User updateMyProfile(@RequestBody UpdateMyProfileRequest updateProfileRequest);
@ResponseBody
@ -106,11 +104,11 @@ public interface UserResource {
User getUserById(@PathVariable(USER_ID) String userId);
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@ResponseStatus(value = HttpStatus.OK)
@Operation(summary = "Add a role to users", description = "None")
@ApiResponses(value = {@ApiResponse(responseCode = "204", description = "No Content"), @ApiResponse(responseCode = "404", description = "The provided userId can not be found."), @ApiResponse(responseCode = "400", description = "One ore more roles are not valid.")})
@PostMapping(value = USER_ROLE_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE)
void setRoles(@PathVariable(USER_ID) String userId, @RequestBody Set<String> roles);
@PostMapping(value = USER_ROLE_REST_PATH, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
User setRoles(@PathVariable(USER_ID) String userId, @RequestBody Set<String> roles);
@ResponseStatus(value = HttpStatus.NO_CONTENT)

View File

@ -0,0 +1,8 @@
package com.knecon.fforesight.tenantusermanagement.api.internal;
import org.springframework.web.bind.annotation.RequestMapping;
@RequestMapping("/internal")
public interface InternalResource {
}

View File

@ -22,7 +22,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
@RequestMapping("/internal")
public interface InternalTenantsResource {
String TENANT_ID_PARAM = "tenantId";

View File

@ -17,7 +17,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@ResponseStatus(value = HttpStatus.OK)
@RequestMapping("/internal")
public interface InternalUserResource {
String USER_REST_PATH = "/user";

View File

@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.knecon.fforesight.tenantusermanagement.api.external.GeneralSettingsResource;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.model.GeneralConfigurationModel;
import com.knecon.fforesight.tenantusermanagement.service.GeneralConfigurationService;
@ -17,7 +18,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class GeneralSettingsController implements GeneralSettingsResource {
public class GeneralSettingsController implements GeneralSettingsResource, PublicResource {
private final GeneralConfigurationService generalConfigurationService;

View File

@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.knecon.fforesight.tenantcommons.EncryptionDecryptionService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.external.SMTPConfigurationResource;
import com.knecon.fforesight.tenantusermanagement.model.SMTPConfiguration;
import com.knecon.fforesight.tenantusermanagement.service.RealmService;
@ -24,9 +25,8 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class SMTPConfigurationController implements SMTPConfigurationResource {
public class SMTPConfigurationController implements SMTPConfigurationResource, PublicResource {
private final static String DEFAULT_PASSWORD = "********";
private final static String SMTP_PASSWORD_KEY = "FFORESIGHT_SMTP_PASSWORD";
private final RealmService realmService;
private final ObjectMapper objectMapper;
@ -51,7 +51,7 @@ public class SMTPConfigurationController implements SMTPConfigurationResource {
var propertiesMap = convertSMTPConfigurationModelToMap(smtpConfigurationModel);
realmRepresentation.setSmtpServer(propertiesMap);
if (!smtpConfigurationModel.getPassword().equalsIgnoreCase(DEFAULT_PASSWORD)) {
if (!smtpConfigurationModel.getPassword().matches("\\**")) {
realmRepresentation.getAttributesOrEmpty().put(SMTP_PASSWORD_KEY, encryptionDecryptionService.encrypt(smtpConfigurationModel.getPassword()));
}

View File

@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.external.TenantsResource;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
import com.knecon.fforesight.tenantusermanagement.model.SimpleTenantResponse;
@ -26,7 +27,7 @@ import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class TenantsController implements TenantsResource {
public class TenantsController implements TenantsResource, PublicResource {
private final TenantManagementService tenantManagementService;
private final DeploymentKeyService deploymentKeyService;

View File

@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.external.UserResource;
import com.knecon.fforesight.tenantusermanagement.model.CreateUserRequest;
import com.knecon.fforesight.tenantusermanagement.model.ResetPasswordRequest;
@ -34,7 +35,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class UserController implements UserResource {
public class UserController implements UserResource, PublicResource {
private final UserService userService;
private final TenantUserManagementProperties tenantUserManagementProperties;
@ -67,17 +68,17 @@ public class UserController implements UserResource {
@Override
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
public void updateProfile(@PathVariable(USER_ID) String userId, @RequestBody UpdateProfileRequest updateProfileRequest) {
public User updateProfile(@PathVariable(USER_ID) String userId, @RequestBody UpdateProfileRequest updateProfileRequest) {
this.userService.updateProfile(userId, updateProfileRequest);
return this.userService.updateProfile(userId, updateProfileRequest);
}
@Override
@PreAuthorize("hasAuthority('" + UPDATE_MY_PROFILE + "')")
public void updateMyProfile(@Valid @RequestBody UpdateMyProfileRequest updateProfileRequest) {
public User updateMyProfile(@Valid @RequestBody UpdateMyProfileRequest updateProfileRequest) {
this.userService.updateMyProfile(updateProfileRequest);
return this.userService.updateMyProfile(updateProfileRequest);
}
@ -118,9 +119,9 @@ public class UserController implements UserResource {
@Override
@PreAuthorize("hasAuthority('" + WRITE_USERS + "')")
public void setRoles(@PathVariable(USER_ID) String userId, @RequestBody Set<String> roles) {
public User setRoles(@PathVariable(USER_ID) String userId, @RequestBody Set<String> roles) {
userService.setRoles(userId, roles);
return userService.setRoles(userId, roles);
}

View File

@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.knecon.fforesight.keycloakcommons.security.KeycloakSecurity;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.external.UserPreferenceResource;
import com.knecon.fforesight.tenantusermanagement.permissions.UserManagementPermissions;
import com.knecon.fforesight.tenantusermanagement.service.UserService;
@ -17,7 +18,7 @@ import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class UserPreferenceController implements UserPreferenceResource {
public class UserPreferenceController implements UserPreferenceResource, PublicResource {
private final UserService userService;

View File

@ -10,6 +10,8 @@ import org.springframework.web.server.ResponseStatusException;
import com.knecon.fforesight.tenantcommons.model.TenantResponse;
import com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest;
import com.knecon.fforesight.tenantusermanagement.api.external.PublicResource;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalResource;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalTenantsResource;
import com.knecon.fforesight.tenantusermanagement.model.DeploymentKeyResponse;
import com.knecon.fforesight.tenantusermanagement.model.SimpleTenantResponse;
@ -22,7 +24,7 @@ import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class InternalTenantsController implements InternalTenantsResource {
public class InternalTenantsController implements InternalTenantsResource, InternalResource {
private final TenantManagementService tenantManagementService;
private final DeploymentKeyService deploymentKeyService;

View File

@ -5,6 +5,7 @@ import java.util.List;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalResource;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalUserResource;
import com.knecon.fforesight.tenantusermanagement.model.User;
import com.knecon.fforesight.tenantusermanagement.service.UserService;
@ -15,7 +16,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
@RestController
@RequiredArgsConstructor
public class InternalUserController implements InternalUserResource {
public class InternalUserController implements InternalUserResource, InternalResource {
private final UserService userService;

View File

@ -0,0 +1,26 @@
package com.knecon.fforesight.tenantusermanagement.dev;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
@Service
@Profile("dev")
@RequiredArgsConstructor
public class DevApplicationRunner implements ApplicationRunner {
private final DevTestTenantService devTestTenantService;
@Override
@SneakyThrows
public void run(ApplicationArguments args) {
devTestTenantService.createTestTenant("sampleTenant");
}
}

View File

@ -1,108 +1,79 @@
package com.knecon.fforesight.tenantusermanagement;
package com.knecon.fforesight.tenantusermanagement.dev;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import org.apache.commons.io.IOUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.StatementCallback;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.knecon.fforesight.tenantcommons.model.DatabaseConnection;
import com.knecon.fforesight.tenantcommons.model.S3StorageConnection;
import com.knecon.fforesight.tenantcommons.model.SearchConnection;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.TenantUser;
import com.knecon.fforesight.tenantusermanagement.model.User;
import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties;
import com.knecon.fforesight.tenantusermanagement.service.KeyCloakRoleManagerService;
import com.knecon.fforesight.tenantusermanagement.service.RealmService;
import com.knecon.fforesight.tenantusermanagement.service.TenantManagementService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Profile("dev")
@Configuration
public class DevConfiguration {
@Service
@RequiredArgsConstructor
public class DevTestTenantService {
@Autowired
private TenantManagementService tenantManagementService;
@Autowired
private KeyCloakRoleManagerService keyCloakRoleManagerService;
@Autowired
private DataSource dataSource;
private final TenantManagementService tenantManagementService;
private final DataSource dataSource;
@Value("${spring.datasource.url:}")
private String masterJDBCURL;
@Autowired
private TenantUserManagementProperties tenantUserManagementProperties;
@Autowired
private RealmService realmService;
@PostConstruct
@SneakyThrows
public void postConstruct() {
public void createTestTenantIfNotExists(String tenantId) {
var tenant = 99;
try {
tenantManagementService.getTenant("tenant" + tenant);
tenantManagementService.getTenant(tenantId);
} catch (Exception e) {
createDefaultTenant(tenant);
createTestTenant(tenantId);
}
}
@SneakyThrows
public void createDefaultTenant(int number) {
public void createTestTenant(String tenantId) {
String tenantsDBName = "tenants";
String tenantsDBPassword = "tenants";
String tenantName = "tenant" + number;
log.info("Creating Tenant {} ", tenantName);
log.info("Creating Tenant {} ", tenantId);
var jdbcUrl = masterJDBCURL.substring(0, masterJDBCURL.lastIndexOf('/') + 1) + tenantsDBName + "?currentSchema=" + tenantName;
var jdbcUrl = masterJDBCURL.substring(0, masterJDBCURL.lastIndexOf('/') + 1) + tenantsDBName + "?currentSchema=" + tenantId;
createDatabase(tenantsDBName, tenantsDBPassword);
createSchema(jdbcUrl, tenantName, tenantsDBName, tenantsDBPassword);
createSchema(jdbcUrl, tenantId, tenantsDBName, tenantsDBPassword);
var tenantRequest = TenantRequest.builder()
.tenantId(tenantName)
.displayName(tenantName)
.tenantId(tenantId)
.displayName(tenantId)
.guid(UUID.randomUUID().toString())
.databaseConnection(DatabaseConnection.builder()
.driver("postgresql")
.host("localhost")
.port("5432")
.database(tenantsDBName)
.schema(tenantName)
.schema(tenantId)
.username(tenantsDBName)
.password(tenantsDBPassword)
.build())
@ -156,28 +127,5 @@ public class DevConfiguration {
}
public byte[] pack(String sourceDirPath) throws IOException {
var bos = new ByteArrayOutputStream();
var p = Paths.get(sourceDirPath);
try (ZipOutputStream zs = new ZipOutputStream(bos)) {
Stream<Path> paths = Files.walk(p);
{
paths.filter(path -> !Files.isDirectory(path)).forEach(path -> {
ZipEntry zipEntry = new ZipEntry(p.relativize(path).toString());
try {
zs.putNextEntry(zipEntry);
Files.copy(path, zs);
zs.closeEntry();
} catch (IOException e) {
System.err.println(e);
}
});
}
}
return bos.toByteArray();
}
}

View File

@ -3,12 +3,14 @@ package com.knecon.fforesight.tenantusermanagement.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode
public class GeneralConfigurationModel {
private boolean forgotPasswordFunctionEnabled;

View File

@ -3,9 +3,15 @@ package com.knecon.fforesight.tenantusermanagement.model;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotEmpty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UpdateMyProfileRequest {
@Schema(description = "Email of user.")

View File

@ -4,9 +4,15 @@ import java.util.HashSet;
import java.util.Set;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class UpdateProfileRequest {
@Schema(description = "Email of user.")
@ -18,6 +24,7 @@ public class UpdateProfileRequest {
@Schema(description = "Last name of user.")
private String lastName;
@Builder.Default
@Schema(description = "Roles.")
private Set<String> roles = new HashSet<>();

View File

@ -7,12 +7,14 @@ import java.util.TreeSet;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "userId")
public class User implements Serializable {
private String userId;

View File

@ -17,6 +17,8 @@ public class TenantUserManagementProperties {
private String publicServerUrl;
private String realm;
private String applicationClientId;
private String swaggerClientId ="swagger-ui-client";
private String swaggerClientSecret;
private String clientId;
private String clientSecret;
private String basePath = "/";

View File

@ -19,7 +19,8 @@ public class DeploymentKeyService {
private final HashFunction hashFunction = Hashing.farmHashFingerprint64();
private final String hardcodedKey = "89274365-160c-49f2-ab8b-ad83fc43c2e1";
@Value("${redaction.kubernetes.id:someValue}")
@Value("${fforesight.kubernetes.id:someValue}")
private String redactionKubernetesId;

View File

@ -261,8 +261,9 @@ public class TenantManagementService implements TenantProvider {
var swaggerClient = new ClientRepresentation();
swaggerClient.setEnabled(true);
swaggerClient.setName("swagger-ui-client");
swaggerClient.setClientId("swagger-ui-client");
swaggerClient.setName(tenantUserManagementProperties.getSwaggerClientId());
swaggerClient.setClientId(tenantUserManagementProperties.getSwaggerClientId());
swaggerClient.setSecret(tenantUserManagementProperties.getSwaggerClientSecret());
swaggerClient.setStandardFlowEnabled(true);
swaggerClient.setImplicitFlowEnabled(false);
swaggerClient.setDirectAccessGrantsEnabled(false);

View File

@ -138,7 +138,7 @@ public class UserService {
@CacheEvict(value = "${commons.keycloak.userCache}", allEntries = true, beforeInvocation = true)
public void setRoles(String userId, Set<String> newRoles, Set<String> currentUserRoles) {
public User setRoles(String userId, Set<String> newRoles, Set<String> currentUserRoles) {
var allRoles = tenantUserManagementProperties.getKcRoleMapping().getAllRoles();
newRoles.forEach(role -> {
@ -180,22 +180,23 @@ public class UserService {
userResource.roles().realmLevel().remove(currentRolesAsRoleRepresentation);
userResource.roles().realmLevel().add(newMappedRoles);
this.rabbitTemplate.convertAndSend(userExchangeName,
"user.rolesUpdated",
(new UserRolesUpdatedEvent(getUserByUsername(userResource.toRepresentation().getUsername()), userRoles, newRoles, KeycloakSecurity.getUserId())));
var userWithNewRoles = getUserByUsername(userResource.toRepresentation().getUsername());
this.rabbitTemplate.convertAndSend(userExchangeName, "user.rolesUpdated", (new UserRolesUpdatedEvent(userWithNewRoles, userRoles, newRoles, KeycloakSecurity.getUserId())));
return userWithNewRoles;
}
@CacheEvict(value = "${commons.keycloak.userCache}", allEntries = true, beforeInvocation = true)
public void setRoles(String userId, Set<String> roles) {
public User setRoles(String userId, Set<String> roles) {
var currentUserResource = getUserResource(KeycloakSecurity.getUserId());
var currentUserRoles = currentUserResource.roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).collect(Collectors.toSet());
currentUserRoles = currentUserRoles.stream().filter(r -> tenantUserManagementProperties.getKcRoleMapping().isValidRole(r)).collect(Collectors.toSet());
setRoles(userId, roles, currentUserRoles);
var userWithNewRoles = setRoles(userId, roles, currentUserRoles);
return userWithNewRoles;
}
@ -225,7 +226,7 @@ public class UserService {
@CacheEvict(value = "${commons.keycloak.userCache}", allEntries = true, beforeInvocation = true)
public void updateMyProfile(UpdateMyProfileRequest updateProfileRequest) {
public User updateMyProfile(UpdateMyProfileRequest updateProfileRequest) {
var user = this.getUserResource(KeycloakSecurity.getUserId());
var userRepresentation = user.toRepresentation();
@ -257,6 +258,7 @@ public class UserService {
this.rabbitTemplate.convertAndSend(userExchangeName, "user.ownProfileUpdated", (new UserUpdatedOwnProfileEvent(updatedProfile)));
return updatedProfile;
}
@ -285,8 +287,7 @@ public class UserService {
.realm(TenantContext.getTenantId())
.username(username)
.password(password)
.clientId(tenantUserManagementProperties.getClientId())
.clientSecret(tenantUserManagementProperties.getClientSecret())
.clientId(tenantUserManagementProperties.getApplicationClientId())
.grantType(OAuth2Constants.PASSWORD)
.resteasyClient(new ResteasyClientBuilderImpl().connectionTTL(2, TimeUnit.SECONDS)
.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY)
@ -388,7 +389,7 @@ public class UserService {
@CacheEvict(value = "${commons.keycloak.userCache}", allEntries = true, beforeInvocation = true)
public void updateProfile(String userId, UpdateProfileRequest updateProfileRequest) {
public User updateProfile(String userId, UpdateProfileRequest updateProfileRequest) {
var user = this.getUserResource(userId);
var userRepresentation = user.toRepresentation();
@ -415,6 +416,7 @@ public class UserService {
this.rabbitTemplate.convertAndSend(userExchangeName, "user.updated", (new UserUpdatedEvent(updatedUser, KeycloakSecurity.getUserId())));
return updatedUser;
}

View File

@ -7,6 +7,7 @@ fforesight:
server-url: http://localhost:8080
client-secret: WJ4CIR2t65r55caWFBg4LWhdW2kOMjeC
client-id: manager
realm: master
kc-role-mapping:
roles:
- name: SUPER_USER

View File

@ -92,13 +92,6 @@ fforesight:
auth-server-url: '/auth'
enabled: true
default-client-id: 'swagger-ui-client'
jobs:
datasource:
url: jdbc:postgresql://${PSQL_HOST:localhost}:${PSQL_PORT:5432}/${PSQL_DATABASE:master}?cachePrepStmts=true&useServerPrepStmts=true&rewriteBatchedStatements=true
driverClassName: org.postgresql.Driver
username: ${PSQL_USERNAME:fforesight}
password: ${PSQL_PASSWORD:fforesight}
platform: org.hibernate.dialect.PostgreSQL95Dialect
tenant-user-management:
base-path: '/tenant-user-management'
tenant-exchange:

View File

@ -0,0 +1,88 @@
package com.knecon.fforesight;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.knecon.fforesight.feigntestclients.external.TenantsClient;
import com.knecon.fforesight.feigntestclients.internal.InternalTenantsClient;
import com.knecon.fforesight.tenantusermanagement.TenantUserManagementServiceApplication;
import com.knecon.fforesight.testcontainers.KeyCloakTestContainer;
import com.knecon.fforesight.testcontainers.RedisTestContainer;
import com.knecon.fforesight.testcontainers.SpringPostgreSQLTestContainer;
import com.knecon.fforesight.utils.TestTenantService;
import lombok.extern.slf4j.Slf4j;
@ExtendWith(SpringExtension.class)
@EnableFeignClients(basePackageClasses = {TenantsClient.class, InternalTenantsClient.class})
@Import(AbstractTenantUserManagementIntegrationTest.TestConfiguration.class)
@ContextConfiguration(initializers = {AbstractTenantUserManagementIntegrationTest.Initializer.class})
@SpringBootTest(classes = TenantUserManagementServiceApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class AbstractTenantUserManagementIntegrationTest {
@MockBean
protected RabbitTemplate rabbitTemplate;
@Autowired
protected TestTenantService testTenantService;
public final static String TEST_TENANT_ID = "test_tenant_fforesight";
@BeforeEach
public void createTestTenant(){
testTenantService.createTestTenantIfNotExists(TEST_TENANT_ID);
}
@Slf4j
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
var postgreSQLContainerMaster = SpringPostgreSQLTestContainer.getInstance().withDatabaseName("integration-tests-db-master").withUsername("sa").withPassword("sa");
postgreSQLContainerMaster.start();
var redisContainer = RedisTestContainer.getInstance();
redisContainer.start();
var connectionStringDetails = "?serverTimezone=UTC&cachePrepStmts=true&useServerPrepStmts=true&rewriteBatchedStatements=true";
var kcInstance = KeyCloakTestContainer.getInstance();
TestPropertyValues.of("spring.datasource.url=" + postgreSQLContainerMaster.getJdbcUrl() + connectionStringDetails,
"spring.datasource.username=" + postgreSQLContainerMaster.getUsername(),
"spring.datasource.password=" + postgreSQLContainerMaster.getPassword(),
"fforesight.tenant-user-management.serverUrl=" + kcInstance.getAuthServerUrl(),
"REDIS_PORT=" + redisContainer.getFirstMappedPort(),
"REDIS_HOST=" + redisContainer.getHost(),
"fforesight.jobs.enabled=false",
"fforesight.keycloak.enabled=true").applyTo(configurableApplicationContext.getEnvironment());
}
}
@Configuration
@ComponentScan
@EnableAutoConfiguration(exclude = {RabbitAutoConfiguration.class})
public static class TestConfiguration {
}
}

View File

@ -0,0 +1,10 @@
package com.knecon.fforesight.feigntestclients.external;
import org.springframework.cloud.openfeign.FeignClient;
import com.knecon.fforesight.tenantusermanagement.api.external.GeneralSettingsResource;
@FeignClient(name = "GeneralSettingsClient", url = "http://localhost:${server.port}" ,path = "${fforesight.tenant-user-management.base-path:}")
public interface GeneralSettingsClient extends GeneralSettingsResource {
}

View File

@ -0,0 +1,10 @@
package com.knecon.fforesight.feigntestclients.external;
import org.springframework.cloud.openfeign.FeignClient;
import com.knecon.fforesight.tenantusermanagement.api.external.SMTPConfigurationResource;
@FeignClient(name = "SMTPConfigurationClient", url = "http://localhost:${server.port}",path = "${fforesight.tenant-user-management.base-path:}")
public interface SMTPConfigurationClient extends SMTPConfigurationResource {
}

View File

@ -0,0 +1,10 @@
package com.knecon.fforesight.feigntestclients.external;
import org.springframework.cloud.openfeign.FeignClient;
import com.knecon.fforesight.tenantusermanagement.api.external.TenantsResource;
@FeignClient(name = "TenantsClient", url = "http://localhost:${server.port}",path = "${fforesight.tenant-user-management.base-path:}")
public interface TenantsClient extends TenantsResource {
}

View File

@ -0,0 +1,10 @@
package com.knecon.fforesight.feigntestclients.external;
import org.springframework.cloud.openfeign.FeignClient;
import com.knecon.fforesight.tenantusermanagement.api.external.UserResource;
@FeignClient(name = "UserClient", url = "http://localhost:${server.port}",path = "${fforesight.tenant-user-management.base-path:}")
public interface UserClient extends UserResource {
}

View File

@ -0,0 +1,10 @@
package com.knecon.fforesight.feigntestclients.external;
import org.springframework.cloud.openfeign.FeignClient;
import com.knecon.fforesight.tenantusermanagement.api.external.UserPreferenceResource;
@FeignClient(name = "UserPreferenceClient", url = "http://localhost:${server.port}", path = "${fforesight.tenant-user-management.base-path:}")
public interface UserPreferenceClient extends UserPreferenceResource {
}

View File

@ -0,0 +1,10 @@
package com.knecon.fforesight.feigntestclients.internal;
import org.springframework.cloud.openfeign.FeignClient;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalTenantsResource;
@FeignClient(name = "InternalTenantsClient", url = "http://localhost:${server.port}", path = "/internal")
public interface InternalTenantsClient extends InternalTenantsResource {
}

View File

@ -0,0 +1,66 @@
package com.knecon.fforesight.testcontainers;
import java.util.ArrayList;
import java.util.List;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import dasniko.testcontainers.keycloak.KeycloakContainer;
public class KeyCloakTestContainer {
private static final String IMAGE_VERSION = "quay.io/keycloak/keycloak:21.0.0";
private static KeycloakContainer keycloak = null;
private KeyCloakTestContainer() {
}
public static KeycloakContainer getInstance() {
if (keycloak == null) {
keycloak = new KeycloakContainer(IMAGE_VERSION).withAdminUsername("admin").withAdminPassword("admin");
keycloak.start();
var keycloakAdminClient = keycloak.getKeycloakAdminClient();
var masterRealm = keycloakAdminClient.realm("master");
var adminClient = new ClientRepresentation();
adminClient.setId("adminClient");
adminClient.setEnabled(true);
adminClient.setName("adminClient");
adminClient.setSecret("adminClientSecret");
adminClient.setServiceAccountsEnabled(true);
adminClient.setDirectAccessGrantsEnabled(true);
adminClient.setStandardFlowEnabled(true);
adminClient.setImplicitFlowEnabled(true);
adminClient.setDirectAccessGrantsEnabled(true);
masterRealm.clients().create(adminClient);
RealmResource myRealm = keycloakAdminClient.realm("master");
String userId = myRealm.clients().get("adminClient").getServiceAccountUser().getId();
UserResource serviceAccountUser = myRealm.users().get(userId);
ClientRepresentation clientThatOwnsRole = myRealm.clients().findByClientId("master-realm").get(0);
String clientIdOfRoleOwner = clientThatOwnsRole.getId();
List<RoleRepresentation> roles = new ArrayList<>();
roles.addAll(myRealm.clients().get(clientIdOfRoleOwner).roles().list());
serviceAccountUser.roles().clientLevel(clientIdOfRoleOwner).add(roles);
serviceAccountUser.roles().realmLevel().add(List.of(myRealm.roles().get("create-realm").toRepresentation()));
}
return keycloak;
}
}

View File

@ -0,0 +1,26 @@
package com.knecon.fforesight.testcontainers;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
public class RedisTestContainer extends GenericContainer<RedisTestContainer> {
private static final String IMAGE_VERSION = "redis:6.2.6";
private static RedisTestContainer container;
private RedisTestContainer() {
super(DockerImageName.parse(IMAGE_VERSION));
}
public static RedisTestContainer getInstance() {
if (container == null) {
container = new RedisTestContainer().withExposedPorts(6379);
}
return container;
}
}

View File

@ -0,0 +1,25 @@
package com.knecon.fforesight.testcontainers;
import org.testcontainers.containers.PostgreSQLContainer;
public class SpringPostgreSQLTestContainer extends PostgreSQLContainer<SpringPostgreSQLTestContainer> {
private static final String IMAGE_VERSION = "postgres:15.2";
private static SpringPostgreSQLTestContainer container;
private SpringPostgreSQLTestContainer() {
super(IMAGE_VERSION);
}
public static SpringPostgreSQLTestContainer getInstance() {
if (container == null) {
container = new SpringPostgreSQLTestContainer();
}
return container;
}
}

View File

@ -0,0 +1,42 @@
package com.knecon.fforesight.tests;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.knecon.fforesight.AbstractTenantUserManagementIntegrationTest;
import com.knecon.fforesight.feigntestclients.external.GeneralSettingsClient;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.model.GeneralConfigurationModel;
public class GeneralSettingsTest extends AbstractTenantUserManagementIntegrationTest {
@Autowired
private GeneralSettingsClient generalSettingsClient;
@Test
public void testGeneralSettings() {
TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID);
var generalConfig = generalSettingsClient.getGeneralConfigurations();
assertThat(generalConfig).isNotNull();
var updatedConfig = new GeneralConfigurationModel();
updatedConfig.setAuxiliaryName("Test");
updatedConfig.setDisplayName("Test");
updatedConfig.setForgotPasswordFunctionEnabled(true);
generalSettingsClient.updateGeneralConfigurations(updatedConfig);
generalConfig = generalSettingsClient.getGeneralConfigurations();
assertThat(generalConfig.getAuxiliaryName()).isEqualTo(updatedConfig.getAuxiliaryName());
assertThat(generalConfig.isForgotPasswordFunctionEnabled()).isEqualTo(updatedConfig.isForgotPasswordFunctionEnabled());
assertThat(generalConfig.getDisplayName()).contains(updatedConfig.getDisplayName());
TenantContext.clear();
}
}

View File

@ -1,4 +1,4 @@
package com.knecon.fforesight;
package com.knecon.fforesight.tests;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

View File

@ -0,0 +1,53 @@
package com.knecon.fforesight.tests;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.knecon.fforesight.AbstractTenantUserManagementIntegrationTest;
import com.knecon.fforesight.feigntestclients.external.SMTPConfigurationClient;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.model.SMTPConfiguration;
public class SMTPConfigurationTest extends AbstractTenantUserManagementIntegrationTest {
@Autowired
private SMTPConfigurationClient smtpConfigurationClient;
@Test
public void testSMTPConfiguration() {
TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID);
try {
var currentSMTPConfiguration = smtpConfigurationClient.getCurrentSMTPConfiguration();
} catch (Exception e) {
System.out.println(e.getMessage());
}
SMTPConfiguration newConfig = new SMTPConfiguration();
newConfig.setAuth(true);
newConfig.setFrom("from@knecon.com");
newConfig.setHost("test.knecon.com");
newConfig.setPassword("secret");
newConfig.setUser("user");
newConfig.setStarttls(true);
newConfig.setSsl(false);
smtpConfigurationClient.updateSMTPConfiguration(newConfig);
var currentSMTPConfiguration = smtpConfigurationClient.getCurrentSMTPConfiguration();
assertThat(currentSMTPConfiguration.getPassword()).matches("\\**");
assertThat(currentSMTPConfiguration.getUser()).isEqualTo("user");
assertThat(currentSMTPConfiguration.getHost()).isEqualTo("test.knecon.com");
assertThat(currentSMTPConfiguration.isSsl()).isFalse();
assertThat(currentSMTPConfiguration.isStarttls()).isTrue();
assertThat(currentSMTPConfiguration.isAuth()).isTrue();
assertThat(currentSMTPConfiguration.getFrom()).isEqualTo("from@knecon.com");
TenantContext.clear();
}
}

View File

@ -0,0 +1,25 @@
package com.knecon.fforesight.tests;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.knecon.fforesight.AbstractTenantUserManagementIntegrationTest;
import com.knecon.fforesight.feigntestclients.internal.InternalTenantsClient;
public class StartupTest extends AbstractTenantUserManagementIntegrationTest {
@Autowired
private InternalTenantsClient internalTenantsClient;
@Test
public void testStartup() {
var simpleTenants = internalTenantsClient.getSimpleTenants();
assertThat(simpleTenants).isNotEmpty();
}
}

View File

@ -0,0 +1,40 @@
package com.knecon.fforesight.tests;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.knecon.fforesight.AbstractTenantUserManagementIntegrationTest;
import com.knecon.fforesight.feigntestclients.external.TenantsClient;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.api.external.TenantsResource;
import com.knecon.fforesight.utils.TestTenantService;
public class TenantsTest extends AbstractTenantUserManagementIntegrationTest {
@Autowired
private TenantsClient tenantsClient;
@Autowired
private TestTenantService testTenantService;
@Test
public void testCreateNewTenant() {
testTenantService.createTestTenantIfNotExists("new_tenant");
TenantContext.setTenantId("new_tenant");
var deploymentKey = tenantsClient.getDeploymentKey("new_tenant");
assertThat(deploymentKey.getValue()).isNotBlank();
var tenant = tenantsClient.getTenant("new_tenant");
assertThat(tenant.getGuid()).isNotBlank();
assertThat(tenantsClient.getTenants().stream().anyMatch(t -> t.getTenantId().equals("new_tenant"))).isTrue();
TenantContext.clear();
}
}

View File

@ -0,0 +1,39 @@
package com.knecon.fforesight.tests;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.knecon.fforesight.AbstractTenantUserManagementIntegrationTest;
import com.knecon.fforesight.feigntestclients.external.UserPreferenceClient;
import com.knecon.fforesight.tenantcommons.TenantContext;
public class UserPreferenceTest extends AbstractTenantUserManagementIntegrationTest {
@Autowired
private UserPreferenceClient userPreferenceClient;
@Test
public void testUserPreferences(){
TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID);
var allMyAttributes = userPreferenceClient.getAllUserAttributes();
assertThat(allMyAttributes).isEmpty();
userPreferenceClient.setAttribute("test", List.of("1","2","3"));
allMyAttributes = userPreferenceClient.getAllUserAttributes();
assertThat(allMyAttributes).hasEntrySatisfying("test", k -> assertThat(k).containsExactly("1","2","3"));
userPreferenceClient.deleteAttribute("test");
allMyAttributes = userPreferenceClient.getAllUserAttributes();
assertThat(allMyAttributes).isEmpty();
TenantContext.clear();
}
}

View File

@ -0,0 +1,81 @@
package com.knecon.fforesight.tests;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.HashSet;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import com.knecon.fforesight.AbstractTenantUserManagementIntegrationTest;
import com.knecon.fforesight.feigntestclients.external.UserClient;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.model.CreateUserRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateMyProfileRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateProfileRequest;
import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties;
public class UserTest extends AbstractTenantUserManagementIntegrationTest {
@Autowired
private UserClient userClient;
@Autowired
private TenantUserManagementProperties tenantUserManagementProperties;
@Test
public void testUsers() {
TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID);
var allUsers = userClient.getAllUsers(true);
var testUserFound = allUsers.stream().anyMatch(u -> u.getEmail().equalsIgnoreCase("test@fforesight.com"));
assertThat(allUsers).isNotEmpty();
assertThat(testUserFound).isTrue();
allUsers = userClient.getApplicationSpecificUsers(true);
testUserFound = allUsers.stream().anyMatch(u -> u.getEmail().equalsIgnoreCase("test@fforesight.com"));
assertThat(allUsers).isNotEmpty();
assertThat(testUserFound).isTrue();
var testUserId = allUsers.iterator().next().getUserId();
var testUser = userClient.getUserById(testUserId);
assertThat(testUser).isNotNull();
testUser = userClient.updateMyProfile(UpdateMyProfileRequest.builder().email("test@fforesight.com").firstName("updateTestFirstName").lastName("updateTestLastName").build());
assertThat(testUser.getLastName()).isEqualTo("updateTestLastName");
assertThat(testUser.getFirstName()).isEqualTo("updateTestFirstName");
CreateUserRequest createUserRequest = new CreateUserRequest();
createUserRequest.setEmail("test.new.user@knecon.com");
createUserRequest.setFirstName("Test");
createUserRequest.setLastName("New User");
createUserRequest.setUsername(createUserRequest.getEmail());
createUserRequest.setRoles(tenantUserManagementProperties.getKcRoleMapping().getAllRoles());
var createdUser = userClient.createUser(createUserRequest);
allUsers = userClient.getAllUsers(true);
assertThat(allUsers).hasSize(2);
createdUser = userClient.activateProfile(createdUser.getUserId(), true);
assertThat(createdUser.isActive()).isTrue();
createdUser = userClient.activateProfile(createdUser.getUserId(), false);
assertThat(createdUser.isActive()).isFalse();
createdUser = userClient.setRoles(createdUser.getUserId(), new HashSet<>());
assertThat(createdUser.getRoles()).isEmpty();
createdUser = userClient.updateProfile(createdUser.getUserId(),
UpdateProfileRequest.builder().email("test.new.user@knecon.com").firstName("update test").lastName("update test").roles(tenantUserManagementProperties.getKcRoleMapping().getAllRoles()).build());
assertThat(createdUser.getRoles()).containsExactly(tenantUserManagementProperties.getKcRoleMapping().getAllRoles().toArray(new String[0]));
userClient.deleteUsers(List.of(createdUser.getUserId()));
allUsers = userClient.getAllUsers(true);
assertThat(allUsers).hasSize(1);
}
}

View File

@ -0,0 +1,25 @@
package com.knecon.fforesight.utils;
import org.springframework.stereotype.Component;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class FeignRequestInterceptor implements RequestInterceptor {
private final TokenService tokenService;
@Override
public void apply(RequestTemplate requestTemplate) {
if (!requestTemplate.path().startsWith("/internal") || requestTemplate.feignTarget().url().endsWith("/internal")) {
var token = tokenService.getToken();
requestTemplate.header("Authorization", "bearer " + token);
}
}
}

View File

@ -0,0 +1,71 @@
package com.knecon.fforesight.utils;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.springframework.stereotype.Service;
import com.knecon.fforesight.feigntestclients.external.TenantsClient;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.model.DatabaseConnection;
import com.knecon.fforesight.tenantcommons.model.S3StorageConnection;
import com.knecon.fforesight.tenantcommons.model.SearchConnection;
import com.knecon.fforesight.tenantusermanagement.api.internal.InternalTenantsResource;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.TenantUser;
import com.knecon.fforesight.testcontainers.SpringPostgreSQLTestContainer;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class TestTenantService {
private final InternalTenantsResource internalTenantsResource;
private final TenantsClient tenantsClient;
private final TokenService tokenService;
public void createTestTenantIfNotExists(String testTenantId) {
try {
var tenantExists = internalTenantsResource.getTenant(testTenantId);
assertThat(tenantExists.getGuid()).isNotBlank();
}catch (Exception e) {
// not found
var tenantRequest = TenantRequest.builder()
.tenantId(testTenantId)
.displayName(testTenantId)
.guid(UUID.randomUUID().toString())
.defaultUsers(List.of(TenantUser.builder().roles(Set.of("SUPER_USER")).username("test@fforesight.com").password("secret").email("test@fforesight.com").build()))
.databaseConnection(DatabaseConnection.builder()
.driver("postgresql")
.host(SpringPostgreSQLTestContainer.getInstance().getHost())
.port(String.valueOf(SpringPostgreSQLTestContainer.getInstance().getFirstMappedPort()))
.database(SpringPostgreSQLTestContainer.getInstance().getDatabaseName())
.schema(testTenantId)
.username(SpringPostgreSQLTestContainer.getInstance().getUsername())
.password(SpringPostgreSQLTestContainer.getInstance().getPassword())
.build())
.searchConnection(SearchConnection.builder().hosts(Set.of("localhost")).port(9200).scheme("http").numberOfShards("1").numberOfReplicas("5").build())
.s3StorageConnection(S3StorageConnection.builder().key("minioadmin").secret("minioadmin").bucketName("redaction").endpoint("http://localhost:9000").build())
.build();
var response = internalTenantsResource.createTenant(tenantRequest);
assertThat(response.getGuid()).isNotBlank();
TenantContext.setTenantId(testTenantId);
tokenService.setUser("test@fforesight.com", "secret");
var tenant = tenantsClient.getTenant(testTenantId);
assertThat(tenant.getGuid()).isNotBlank();
}
}
}

View File

@ -0,0 +1,81 @@
package com.knecon.fforesight.utils;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.BadRequestException;
import org.apache.commons.io.IOUtils;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.KeycloakBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class TokenService {
@Autowired
private TenantUserManagementProperties tenantUserManagementProperties;
private String username;
private String password;
private String accessToken = null;
public void setUser(String username, String password) {
this.username = username;
this.password = password;
this.accessToken = null;
}
@SneakyThrows
public String getToken() {
if(TenantContext.getTenantId() == null){
return null;
}
if (accessToken != null) {
return accessToken;
}
var tokenClient = KeycloakBuilder.builder()
.serverUrl(tenantUserManagementProperties.getServerUrl())
.realm(TenantContext.getTenantId())
.username(username)
.password(password)
.clientId(tenantUserManagementProperties.getApplicationClientId())
.grantType(OAuth2Constants.PASSWORD)
.resteasyClient(new ResteasyClientBuilderImpl().connectionTTL(2, TimeUnit.SECONDS)
.hostnameVerification(ResteasyClientBuilder.HostnameVerificationPolicy.ANY)
.connectionPoolSize(tenantUserManagementProperties.getConnectionPoolSize())
.disableTrustManager()
.build())
.build();
try {
accessToken = tokenClient.tokenManager().getAccessTokenString();
return accessToken;
}catch (BadRequestException e){
var response = IOUtils.toString(new InputStreamReader((InputStream) e.getResponse().getEntity()));
log.warn("Exception getting token: {}",response, e);
throw e;
} finally {
tokenClient.close();
}
}
}

View File

@ -0,0 +1,131 @@
server:
port: 28181
management:
endpoint:
metrics.enabled: ${monitoring.enabled:false}
prometheus.enabled: ${monitoring.enabled:false}
health.enabled: true
endpoints.web.exposure.include: prometheus, health, metrics
metrics.export.prometheus.enabled: ${monitoring.enabled:false}
info:
description: Tenant User Management Service
spring:
datasource:
url: jdbc:postgresql://${PSQL_HOST:localhost}:${PSQL_PORT:5432}/${PSQL_DATABASE:master}?cachePrepStmts=true&useServerPrepStmts=true&rewriteBatchedStatements=true
driverClassName: org.postgresql.Driver
username: ${PSQL_USERNAME:fforesight}
password: ${PSQL_PASSWORD:fforesight}
platform: org.hibernate.dialect.PostgreSQL95Dialect
hikari:
maximumPoolSize: 2
minimum-idle: 2
data-source-properties:
cachePrepStmts: true
prepStmtCacheSize: 1000
prepStmtCacheSqlLimit: 2048
jackson:
serialization:
write-dates-as-timestamps: false
deserialization:
accept-single-value-as-array: true
main:
allow-bean-definition-overriding: true
allow-circular-references: true
jpa:
open-in-view: true
database-platform: org.hibernate.dialect.PostgreSQL95Dialect
hibernate:
ddl-auto: none
naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
properties:
hibernate:
jdbc:
batch_size: 1000
order_inserts: true
order_updates: true
cache:
type: redis
mvc:
pathmatch:
matching-strategy: ant-path-matcher
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
rabbitmq:
host: ${RABBITMQ_HOST:localhost}
port: ${RABBITMQ_PORT:5672}
username: ${RABBITMQ_USERNAME:user}
password: ${RABBITMQ_PASSWORD:rabbitmq}
listener:
simple:
acknowledge-mode: AUTO
concurrency: 5
retry:
enabled: true
max-attempts: 3
max-interval: 15000
prefetch: 1
liquibase:
change-log: classpath:/db/changelog/db.changelog-master.yaml
enabled: true
application:
name: tenant-user-management
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
fforesight:
keycloak:
ignored-endpoints: [ '/actuator/health', '/tenant-user-management','/internal/**','/tenant-user-management/docs/**','/tenant-user-management/docs','/tenant-user-management/tenants/simple' ]
enabled: true
springdoc:
base-path: '/tenant-user-management'
auth-server-url: '/auth'
enabled: true
default-client-id: 'swagger-ui-client'
default-tenant: 'fforesight'
tenant-exchange:
name: 'tenants-exchange'
user-exchange:
name: 'users-exchange'
tenant-user-management:
base-path: '/tenant-user-management'
realm: master
server-url: http://localhost:28181
client-secret: adminClientSecret
client-id: adminClient
kc-role-mapping:
roles:
- name: SUPER_USER
set-by-default: true
rank: 100
permissions:
- 'fforesight-read-general-configuration'
- 'fforesight-write-general-configuration'
- 'fforesight-manage-user-preferences'
- 'fforesight-read-users'
- 'fforesight-read-all-users'
- 'fforesight-write-users'
- 'fforesight-update-my-profile'
- 'fforesight-create-tenant'
- 'fforesight-get-tenants'
- 'fforesight-deployment-info'
- 'fforesight-read-smtp-configuration'
- 'fforesight-write-smtp-configuration'
access-token-life-span: 86400
application-name: tenant-user-management
application-client-id: tenant-user-management
swagger-client-secret: 'testSecret123!'
cors.enabled: true
springdoc:
packages-to-scan: [ 'com.knecon.fforesight.tenantusermanagement.controller.external' ]