RED-8123 - privilege escalation is not checked/validated

This commit is contained in:
Timo Bejan 2024-01-04 14:27:18 +02:00
parent 7462b38f9f
commit 8a41d70701
21 changed files with 229 additions and 156 deletions

View File

@ -1,9 +1,15 @@
package com.knecon.fforesight.tenantusermanagement.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Object containing the request to reset a password.")
public class ResetPasswordRequest {

View File

@ -14,16 +14,6 @@ import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.core.Response;
import org.apache.commons.validator.routines.EmailValidator;
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.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
@ -46,10 +36,19 @@ import com.knecon.fforesight.tenantusermanagement.model.UpdateMyProfileRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateProfileRequest;
import com.knecon.fforesight.tenantusermanagement.model.User;
import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties;
import io.micrometer.common.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.validator.routines.EmailValidator;
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.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
@Slf4j
@Service
@ -127,16 +126,6 @@ public class UserService {
}
private void sendResetPasswordEmail(String userId) {
try {
this.getTenantUsersResource().get(userId).executeActionsEmail(Collections.singletonList("UPDATE_PASSWORD"), 86400);
} catch (Exception e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Failed to send email.", e);
}
}
@CacheEvict(value = "${commons.keycloak.userCache}", allEntries = true, beforeInvocation = true)
public User setRoles(String userId, Set<String> newRoles, Set<String> currentUserRoles) {
@ -200,31 +189,12 @@ public class UserService {
}
private RoleRepresentation getRoleRepresentation(String role) {
RoleRepresentation realmRole;
try {
realmRole = realmService.realm(TenantContext.getTenantId()).roles().get(role).toRepresentation();
} catch (NotFoundException e) {
log.warn("The realm role {} is not found.", role);
throw new NotFoundException("The realm role " + role + " is not found.", e);
}
return realmRole;
}
public Optional<User> getUserById(String userId) {
return userListingService.getAllUsers(TenantContext.getTenantId()).stream().filter(u -> u.getUserId().equalsIgnoreCase(userId)).findAny();
}
public List<User> getUsersByIds(Collection<String> userIds) {
return userListingService.getAllUsers(TenantContext.getTenantId()).stream().filter(u -> userIds.contains(u.getUserId())).collect(Collectors.toList());
}
@CacheEvict(value = "${commons.keycloak.userCache}", allEntries = true, beforeInvocation = true)
public User updateMyProfile(UpdateMyProfileRequest updateProfileRequest) {
@ -366,20 +336,14 @@ public class UserService {
throw new ResponseStatusException(HttpStatus.CONFLICT, "Cannot delete self");
}
var currentUserResource = getUserResource(KeycloakSecurity.getUserId());
var currentRoles = getRoles(currentUserResource.toRepresentation().getId());
var executionValid = isExecutionRankValid(KeycloakSecurity.getUserId(), userId);
var userResource = getUserResource(userId);
var userRoles = getRoles(userId);
var roleMapping = tenantUserManagementProperties.getKcRoleMapping();
var maxRank = currentRoles.stream().map(r -> roleMapping.getRole(r).getRank()).max(Integer::compare).orElse(-1);
var toDeleteUserMaxRank = userRoles.stream().map(r -> roleMapping.getRole(r).getRank()).max(Integer::compare).orElse(-1);
if (toDeleteUserMaxRank > maxRank) {
if (!executionValid) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "It is not allowed to delete a user with higher ranking roles");
}
var userResource = getUserResource(userId);
var userToBeRemoved = getUserByUsername(userResource.toRepresentation().getUsername());
userResource.remove();
@ -443,12 +407,19 @@ public class UserService {
public void resetPassword(String userId, ResetPasswordRequest resetPasswordRequest) {
var executionValid = isExecutionRankValid(KeycloakSecurity.getUserId(), userId);
if (!executionValid) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "It is not allowed to reset the password of a user with higher ranking roles");
}
try {
CredentialRepresentation request = new CredentialRepresentation();
request.setType(CredentialRepresentation.PASSWORD);
request.setTemporary(resetPasswordRequest.isTemporary());
request.setValue(resetPasswordRequest.getPassword());
realmService.realm(TenantContext.getTenantId()).users().get(userId).resetPassword(request);
log.info("User {} resetted password for user {}", KeycloakSecurity.getUserId(), userId);
} catch (Exception e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Could not reset password. It does not match the password policy.", e);
}
@ -460,4 +431,43 @@ public class UserService {
return userListingService.getAllUsers(TenantContext.getTenantId());
}
private RoleRepresentation getRoleRepresentation(String role) {
RoleRepresentation realmRole;
try {
realmRole = realmService.realm(TenantContext.getTenantId()).roles().get(role).toRepresentation();
} catch (NotFoundException e) {
log.warn("The realm role {} is not found.", role);
throw new NotFoundException("The realm role " + role + " is not found.", e);
}
return realmRole;
}
private void sendResetPasswordEmail(String userId) {
try {
this.getTenantUsersResource().get(userId).executeActionsEmail(Collections.singletonList("UPDATE_PASSWORD"), 86400);
} catch (Exception e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Failed to send email.", e);
}
}
private boolean isExecutionRankValid(String executingUserId, String targetUserId) {
var currentUserResource = getUserResource(executingUserId);
var currentRoles = getRoles(currentUserResource.toRepresentation().getId());
var userRoles = getRoles(targetUserId);
var roleMapping = tenantUserManagementProperties.getKcRoleMapping();
var maxRank = currentRoles.stream().map(r -> roleMapping.getRole(r).getRank()).max(Integer::compare).orElse(-1);
var targetRank = userRoles.stream().map(r -> roleMapping.getRole(r).getRank()).max(Integer::compare).orElse(-1);
return targetRank <= maxRank;
}
}

View File

@ -1,7 +1,5 @@
package com.knecon.fforesight;
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;
@ -17,7 +15,6 @@ 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 org.testcontainers.containers.GenericContainer;
import com.knecon.fforesight.feigntestclients.external.TenantsClient;
import com.knecon.fforesight.feigntestclients.internal.InternalTenantsClient;
@ -27,9 +24,11 @@ import com.knecon.fforesight.testcontainers.MinioTestContainer;
import com.knecon.fforesight.testcontainers.RedisTestContainer;
import com.knecon.fforesight.testcontainers.SpringPostgreSQLTestContainer;
import com.knecon.fforesight.utils.TestTenantService;
import com.knecon.fforesight.utils.TokenService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(SpringExtension.class)
@EnableFeignClients(basePackageClasses = {TenantsClient.class, InternalTenantsClient.class})
@ -38,17 +37,14 @@ import lombok.extern.slf4j.Slf4j;
@SpringBootTest(classes = TenantUserManagementServiceApplication.class, webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
public class AbstractTenantUserManagementIntegrationTest {
public final static String TEST_TENANT_ID = "test-tenant-fforesight";
public static int minioPort;
@MockBean
protected RabbitTemplate rabbitTemplate;
@Autowired
protected TestTenantService testTenantService;
public final static String TEST_TENANT_ID = "test-tenant-fforesight";
private static GenericContainer minioServer;
public static int minioPort;
@Autowired
protected TokenService tokenService;
@BeforeEach

View File

@ -4,7 +4,7 @@ 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:}")
@FeignClient(name = "GeneralSettingsClient", url = "http://localhost:${server.port}", path = "${fforesight.tenant-user-management.base-path:}")
public interface GeneralSettingsClient extends GeneralSettingsResource {
}

View File

@ -4,7 +4,7 @@ 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:}")
@FeignClient(name = "SMTPConfigurationClient", url = "http://localhost:${server.port}", path = "${fforesight.tenant-user-management.base-path:}")
public interface SMTPConfigurationClient extends SMTPConfigurationResource {
}

View File

@ -4,7 +4,7 @@ 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:}")
@FeignClient(name = "TenantsClient", url = "http://localhost:${server.port}", path = "${fforesight.tenant-user-management.base-path:}")
public interface TenantsClient extends TenantsResource {
}

View File

@ -4,7 +4,7 @@ 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:}")
@FeignClient(name = "UserClient", url = "http://localhost:${server.port}", path = "${fforesight.tenant-user-management.base-path:}")
public interface UserClient extends UserResource {
}

View File

@ -3,13 +3,12 @@ package com.knecon.fforesight.testcontainers;
import java.util.ArrayList;
import java.util.List;
import dasniko.testcontainers.keycloak.KeycloakContainer;
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 final class KeyCloakTestContainer {
private static final String IMAGE_VERSION = "quay.io/keycloak/keycloak:21.0.0";

View File

@ -8,6 +8,7 @@ public final class MinioTestContainer extends GenericContainer<MinioTestContaine
private static final String IMAGE_VERSION = "minio/minio";
private static MinioTestContainer container;
private MinioTestContainer() {
super(DockerImageName.parse(IMAGE_VERSION));
@ -24,4 +25,5 @@ public final class MinioTestContainer extends GenericContainer<MinioTestContaine
}
return container;
}
}

View File

@ -2,21 +2,13 @@ package com.knecon.fforesight.tests;
import static org.assertj.core.api.Assertions.assertThat;
import java.time.Duration;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.testcontainers.Testcontainers;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.startupcheck.OneShotStartupCheckStrategy;
import org.testcontainers.containers.wait.strategy.HttpWaitStrategy;
import org.testcontainers.junit.jupiter.Container;
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;
import org.junit.jupiter.api.Test;
public class GeneralSettingsTest extends AbstractTenantUserManagementIntegrationTest {

View File

@ -6,9 +6,9 @@ import org.junit.jupiter.api.Test;
public class IdentityTest {
@Test
public void test(){
public void test() {
assumeTrue(true);
}

View File

@ -3,18 +3,19 @@ package com.knecon.fforesight.tests;
import static dasniko.testcontainers.keycloak.ExtendableKeycloakContainer.MASTER_REALM;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import org.keycloak.representations.idm.RealmRepresentation;
import org.springframework.beans.factory.annotation.Autowired;
import com.knecon.fforesight.AbstractTenantUserManagementIntegrationTest;
import com.knecon.fforesight.tenantusermanagement.service.RealmService;
import org.junit.jupiter.api.Test;
import org.keycloak.representations.idm.RealmRepresentation;
public class RealmTest extends AbstractTenantUserManagementIntegrationTest {
@Autowired
private RealmService realmService;
@Test
public void testInitialRealm() {
@ -22,4 +23,5 @@ public class RealmTest extends AbstractTenantUserManagementIntegrationTest {
assertEquals(realmRepresentation.getDisplayName(), "tenant-user-management");
assertEquals(realmRepresentation.getLoginTheme(), "redaction");
}
}

View File

@ -3,13 +3,13 @@ package com.knecon.fforesight.tests;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
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;
import org.junit.jupiter.api.Test;
public class SMTPConfigurationTest extends AbstractTenantUserManagementIntegrationTest {
@ -43,17 +43,6 @@ public class SMTPConfigurationTest extends AbstractTenantUserManagementIntegrati
}
@Test
public void testSMTPConnection() {
TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID);
SMTPConfiguration smtpConfiguration = provideTestSMTPConfiguration();
assertThatThrownBy(() -> smtpConfigurationClient.testSMTPConfiguration(smtpConfiguration)).hasMessageContaining("Couldn't connect to host, port: test.knecon.com, 25; timeout 10000");
TenantContext.clear();
}
private SMTPConfiguration provideTestSMTPConfiguration() {
@ -70,4 +59,17 @@ public class SMTPConfigurationTest extends AbstractTenantUserManagementIntegrati
}
@Test
public void testSMTPConnection() {
TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID);
SMTPConfiguration smtpConfiguration = provideTestSMTPConfiguration();
assertThatThrownBy(() -> smtpConfigurationClient.testSMTPConfiguration(smtpConfiguration)).hasMessageContaining(
"Couldn't connect to host, port: test.knecon.com, 25; timeout 10000");
TenantContext.clear();
}
}

View File

@ -2,11 +2,11 @@ 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;
import org.junit.jupiter.api.Test;
public class StartupTest extends AbstractTenantUserManagementIntegrationTest {

View File

@ -6,12 +6,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.Map;
import java.util.Set;
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.EncryptionDecryptionService;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.tenantcommons.model.AzureStorageConnection;
import com.knecon.fforesight.tenantcommons.model.DatabaseConnection;
@ -19,29 +17,22 @@ import com.knecon.fforesight.tenantcommons.model.S3StorageConnection;
import com.knecon.fforesight.tenantcommons.model.SearchConnection;
import com.knecon.fforesight.tenantusermanagement.model.SearchConnectionRequest;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties;
import com.knecon.fforesight.tenantusermanagement.service.RealmService;
import com.knecon.fforesight.utils.TestTenantService;
import feign.FeignException;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.regions.Region;
public class TenantsTest extends AbstractTenantUserManagementIntegrationTest {
private static final String PASSWORD = "**********";
@Autowired
private TenantsClient tenantsClient;
@Autowired
private TestTenantService testTenantService;
@Autowired
private EncryptionDecryptionService encryptionService;
@Autowired
private RealmService realmService;
private static final String PASSWORD = "**********";
@Test
public void testCreateNewTenant() {
@ -68,6 +59,7 @@ public class TenantsTest extends AbstractTenantUserManagementIntegrationTest {
@Test
public void testUpdateTenant() {
String tenantId = "new_tenant";
testTenantService.createTestTenantIfNotExists(tenantId, minioPort);
TenantContext.setTenantId(tenantId);
@ -131,6 +123,21 @@ public class TenantsTest extends AbstractTenantUserManagementIntegrationTest {
}
private SearchConnection convert(SearchConnectionRequest sc, String tenantId) {
return SearchConnection.builder()
.hosts(sc.getHosts())
.port(sc.getPort())
.scheme(sc.getScheme())
.username(sc.getUsername())
.password(sc.getPassword())
.numberOfShards(sc.getNumberOfShards())
.numberOfReplicas(sc.getNumberOfReplicas())
.indexPrefix("fforesight_" + tenantId)
.build();
}
@Test
public void testUpdateTenantWithIncorrectS3Storage() {
@ -253,17 +260,4 @@ public class TenantsTest extends AbstractTenantUserManagementIntegrationTest {
TenantContext.clear();
}
private SearchConnection convert(SearchConnectionRequest sc, String tenantId) {
return SearchConnection.builder()
.hosts(sc.getHosts())
.port(sc.getPort())
.scheme(sc.getScheme())
.username(sc.getUsername())
.password(sc.getPassword())
.numberOfShards(sc.getNumberOfShards())
.numberOfReplicas(sc.getNumberOfReplicas())
.indexPrefix("fforesight_" + tenantId)
.build();
}
}

View File

@ -4,29 +4,30 @@ 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;
import org.junit.jupiter.api.Test;
public class UserPreferenceTest extends AbstractTenantUserManagementIntegrationTest {
@Autowired
private UserPreferenceClient userPreferenceClient;
@Test
public void testUserPreferences(){
public void testUserPreferences() {
TenantContext.setTenantId(AbstractTenantUserManagementIntegrationTest.TEST_TENANT_ID);
var allMyAttributes = userPreferenceClient.getAllUserAttributes();
assertThat(allMyAttributes).isEmpty();
userPreferenceClient.setAttribute("test", List.of("1","2","3"));
userPreferenceClient.setAttribute("test", List.of("1", "2", "3"));
allMyAttributes = userPreferenceClient.getAllUserAttributes();
assertThat(allMyAttributes).hasEntrySatisfying("test", k -> assertThat(k).containsExactly("1","2","3"));
assertThat(allMyAttributes).hasEntrySatisfying("test", k -> assertThat(k).containsExactly("1", "2", "3"));
userPreferenceClient.deleteAttribute("test");

View File

@ -1,20 +1,25 @@
package com.knecon.fforesight.tests;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
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.ResetPasswordRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateMyProfileRequest;
import com.knecon.fforesight.tenantusermanagement.model.UpdateProfileRequest;
import com.knecon.fforesight.tenantusermanagement.properties.TenantUserManagementProperties;
import feign.FeignException;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
public class UserTest extends AbstractTenantUserManagementIntegrationTest {
@ -44,7 +49,8 @@ public class UserTest extends AbstractTenantUserManagementIntegrationTest {
var testUser = userClient.getUserById(testUserId);
assertThat(testUser).isNotNull();
testUser = userClient.updateMyProfile(UpdateMyProfileRequest.builder().email("test@fforesight.com").firstName("updateTestFirstName").lastName("updateTestLastName").build());
testUser =
userClient.updateMyProfile(UpdateMyProfileRequest.builder().email("test@fforesight.com").firstName("updateTestFirstName").lastName("updateTestLastName").build());
assertThat(testUser.getLastName()).isEqualTo("updateTestLastName");
assertThat(testUser.getFirstName()).isEqualTo("updateTestFirstName");
@ -69,7 +75,12 @@ public class UserTest extends AbstractTenantUserManagementIntegrationTest {
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());
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()));
@ -78,4 +89,49 @@ public class UserTest extends AbstractTenantUserManagementIntegrationTest {
assertThat(allUsers).hasSize(1);
}
@Test
@SneakyThrows
public void testResetPassword() {
testTenantService.createTestTenantIfNotExists("test-password-reset", minioPort);
TenantContext.setTenantId("test-password-reset");
var createUserRequest = new CreateUserRequest();
createUserRequest.setEmail("less.super.user.1@knecon.com");
createUserRequest.setUsername(createUserRequest.getEmail());
createUserRequest.setRoles(Set.of("LESS_SUPER_USER"));
var createdUser1 = userClient.createUser(createUserRequest);
createUserRequest = new CreateUserRequest();
createUserRequest.setEmail("less.super.user.2@knecon.com");
createUserRequest.setUsername(createUserRequest.getEmail());
createUserRequest.setRoles(Set.of("LESS_SUPER_USER"));
var createdUser2 = userClient.createUser(createUserRequest);
//reset password as super-user for less-super-user
userClient.resetPassword(createdUser1.getUserId(), ResetPasswordRequest.builder().password("Secret@secured!23").build());
//reset password as super-user for less-super-user
userClient.resetPassword(createdUser2.getUserId(), ResetPasswordRequest.builder().password("Secret@secured!23").build());
//authenticate as less-super-user
tokenService.setUser("less.super.user.1@knecon.com", "Secret@secured!23");
//reset password as less-super-user for less-super-user
userClient.resetPassword(createdUser2.getUserId(), ResetPasswordRequest.builder().password("Secret@secured!2345").build());
var allUsers = userClient.getAllUsers(true);
var initialSuperUser = allUsers.stream().filter(u -> u.getEmail().equalsIgnoreCase("test@fforesight.com")).findAny().get();
try {
//reset password as less-super-user for super-user
userClient.resetPassword(initialSuperUser.getUserId(), ResetPasswordRequest.builder().password("ShouldNotWork@123").build());
fail("Should not be allowed to reset password of higher ranking user");
} catch (FeignException e) {
// forbidden to reset password of higher ranking user
assertThat(e.status()).isEqualTo(403);
}
}
}

View File

@ -4,13 +4,6 @@ import static org.mockito.Mockito.when;
import java.util.concurrent.TimeUnit;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.KeycloakBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@ -26,6 +19,11 @@ import com.knecon.fforesight.feigntestclients.internal.InternalTenantsClient;
import com.knecon.fforesight.tenantusermanagement.TenantUserManagementServiceApplication;
import com.knecon.fforesight.tenantusermanagement.service.KeyCloakRoleManagerService;
import com.knecon.fforesight.tenantusermanagement.service.RealmService;
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
import org.junit.jupiter.api.extension.ExtendWith;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.KeycloakBuilder;
@ActiveProfiles(profiles = "taas")
@ExtendWith(SpringExtension.class)
@ -49,7 +47,7 @@ public class TenantSyncUtils {
KeyCloakRoleManagerService keyCloakRoleManagerService;
// @Test
// @Test
// @Disabled
public void syncTenant() {

View File

@ -12,13 +12,11 @@ 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.SearchConnectionRequest;
import com.knecon.fforesight.tenantusermanagement.model.TenantRequest;
import com.knecon.fforesight.tenantusermanagement.model.TenantUser;
import com.knecon.fforesight.testcontainers.SpringPostgreSQLTestContainer;
import lombok.RequiredArgsConstructor;
import software.amazon.awssdk.regions.Region;
@ -46,19 +44,6 @@ public class TestTenantService {
}
public void createTestTenantWithoutStorageIfNotExist(String testTenantId) {
try {
var tenantExists = internalTenantsResource.getTenant(testTenantId);
assertThat(tenantExists.getGuid()).isNotBlank();
} catch (Exception e) {
// not found
createUser(testTenantId, 0, false);
}
}
private void createUser(String testTenantId, int actualPort, boolean withStorage) {
// not found
TenantRequest tenantRequest;
@ -106,4 +91,17 @@ public class TestTenantService {
assertThat(tenant.getGuid()).isNotBlank();
}
public void createTestTenantWithoutStorageIfNotExist(String testTenantId) {
try {
var tenantExists = internalTenantsResource.getTenant(testTenantId);
assertThat(tenantExists.getGuid()).isNotBlank();
} catch (Exception e) {
// not found
createUser(testTenantId, 0, false);
}
}
}

View File

@ -6,19 +6,18 @@ 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;
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;
@Slf4j
@Service
@ -30,6 +29,7 @@ public class TokenService {
private String password;
private String accessToken;
@SuppressWarnings("PMD")
public void setUser(String username, String password) {
@ -42,7 +42,7 @@ public class TokenService {
@SneakyThrows
public String getToken() {
if(TenantContext.getTenantId() == null){
if (TenantContext.getTenantId() == null) {
return null;
}
@ -67,9 +67,9 @@ public class TokenService {
try {
accessToken = tokenClient.tokenManager().getAccessTokenString();
return accessToken;
}catch (BadRequestException e){
} catch (BadRequestException e) {
var response = IOUtils.toString(new InputStreamReader((InputStream) e.getResponse().getEntity()));
log.warn("Exception getting token: {}",response, e);
log.warn("Exception getting token: {}", response, e);
throw e;
} finally {
tokenClient.close();

View File

@ -123,6 +123,23 @@ fforesight:
- 'fforesight-deployment-info'
- 'fforesight-read-smtp-configuration'
- 'fforesight-write-smtp-configuration'
- name: LESS_SUPER_USER
set-by-default: true
rank: 10
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-update-tenant'
- '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