rename Credentials->Profile + Context->Scope (#202)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/202 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
-168
@@ -1,168 +0,0 @@
|
||||
package net.hostsharing.hsadminng.hs.accounts;
|
||||
|
||||
import net.hostsharing.hsadminng.config.MessageTranslator;
|
||||
import net.hostsharing.hsadminng.accounts.generated.api.v1.model.ContextResource;
|
||||
import net.hostsharing.hsadminng.accounts.generated.api.v1.model.CredentialsPatchResource;
|
||||
import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@TestInstance(PER_CLASS)
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HsCredentialsEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
CredentialsPatchResource,
|
||||
HsCredentialsEntity
|
||||
> {
|
||||
|
||||
private static final UUID INITIAL_CREDENTIALS_UUID = UUID.randomUUID();
|
||||
|
||||
private static final Boolean INITIAL_ACTIVE = true;
|
||||
private static final String INITIAL_EMAIL_ADDRESS = "initial@example.com";
|
||||
private static final List<String> INITIAL_TOTP_SECRETS = List.of("initial_2fa");
|
||||
private static final String INITIAL_SMS_NUMBER = "initial_sms";
|
||||
private static final String INITIAL_PHONE_PASSWORD = "initial_phone_pw";
|
||||
|
||||
private static final Boolean PATCHED_ACTIVE = false;
|
||||
private static final String PATCHED_EMAIL_ADDRESS = "patched@example.com";
|
||||
private static final List<String> PATCHED_TOTP_SECRETS = List.of("patched_2fa");
|
||||
private static final String PATCHED_SMS_NUMBER = "patched_sms";
|
||||
private static final String PATCHED_PHONE_PASSWORD = "patched_phone_pw";
|
||||
|
||||
// Contexts
|
||||
private static final UUID CONTEXT_UUID_1 = UUID.randomUUID();
|
||||
private static final UUID CONTEXT_UUID_2 = UUID.randomUUID();
|
||||
private static final UUID CONTEXT_UUID_3 = UUID.randomUUID();
|
||||
|
||||
private final HsCredentialsContextRealEntity initialContextEntity1 = HsCredentialsContextRealEntity.builder()
|
||||
.uuid(CONTEXT_UUID_1)
|
||||
.type("HSADMIN")
|
||||
.qualifier("prod")
|
||||
.build();
|
||||
private final HsCredentialsContextRealEntity initialContextEntity2 = HsCredentialsContextRealEntity.builder()
|
||||
.uuid(CONTEXT_UUID_2)
|
||||
.type("SSH")
|
||||
.qualifier("dev")
|
||||
.build();
|
||||
|
||||
private ContextResource patchContextResource2;
|
||||
private ContextResource patchContextResource3;
|
||||
|
||||
// This is what em.find should return for CONTEXT_UUID_3
|
||||
private final HsCredentialsContextRealEntity newContextEntity3 = HsCredentialsContextRealEntity.builder()
|
||||
.uuid(CONTEXT_UUID_3)
|
||||
.type("HSADMIN")
|
||||
.qualifier("test")
|
||||
.build();
|
||||
|
||||
private final Set<HsCredentialsContextRealEntity> initialContextEntities = Set.of(initialContextEntity1, initialContextEntity2);
|
||||
private List<ContextResource> patchedContextResources;
|
||||
private final Set<HsCredentialsContextRealEntity> expectedPatchedContextEntities = Set.of(initialContextEntity2, newContextEntity3);
|
||||
|
||||
@Mock
|
||||
private EntityManager em;
|
||||
|
||||
@BeforeEach
|
||||
void initMocks() {
|
||||
// Mock em.find for contexts that are part of the patch and need to be fetched
|
||||
lenient().when(em.find(eq(HsCredentialsContextRealEntity.class), eq(CONTEXT_UUID_1))).thenReturn(initialContextEntity1);
|
||||
lenient().when(em.find(eq(HsCredentialsContextRealEntity.class), eq(CONTEXT_UUID_2))).thenReturn(initialContextEntity2);
|
||||
lenient().when(em.find(eq(HsCredentialsContextRealEntity.class), eq(CONTEXT_UUID_3))).thenReturn(newContextEntity3);
|
||||
|
||||
patchContextResource2 = new ContextResource();
|
||||
patchContextResource2.setUuid(CONTEXT_UUID_2);
|
||||
patchContextResource2.setType("SSH");
|
||||
patchContextResource2.setQualifier("dev");
|
||||
|
||||
patchContextResource3 = new ContextResource();
|
||||
patchContextResource3.setUuid(CONTEXT_UUID_3);
|
||||
patchContextResource3.setType("HSADMIN");
|
||||
patchContextResource3.setQualifier("test");
|
||||
|
||||
patchedContextResources = List.of(patchContextResource2, patchContextResource3);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HsCredentialsEntity newInitialEntity() {
|
||||
final var entity = new HsCredentialsEntity();
|
||||
entity.setUuid(INITIAL_CREDENTIALS_UUID);
|
||||
entity.setActive(INITIAL_ACTIVE);
|
||||
entity.setEmailAddress(INITIAL_EMAIL_ADDRESS);
|
||||
entity.setTotpSecrets(INITIAL_TOTP_SECRETS);
|
||||
entity.setSmsNumber(INITIAL_SMS_NUMBER);
|
||||
entity.setPhonePassword(INITIAL_PHONE_PASSWORD);
|
||||
// Ensure loginContexts is a mutable set for the patcher
|
||||
entity.setLoginContexts(new HashSet<>(initialContextEntities));
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CredentialsPatchResource newPatchResource() {
|
||||
return new CredentialsPatchResource();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HsCredentialsEntityPatcher createPatcher(final HsCredentialsEntity entity) {
|
||||
final var contextMapper = new CredentialContextResourceToEntityMapper(em, mock(MessageTranslator.class));
|
||||
return new HsCredentialsEntityPatcher(contextMapper, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<Property> propertyTestDescriptors() {
|
||||
return Stream.of(
|
||||
new SimpleProperty<>(
|
||||
"active",
|
||||
CredentialsPatchResource::setActive,
|
||||
PATCHED_ACTIVE,
|
||||
HsCredentialsEntity::setActive,
|
||||
PATCHED_ACTIVE)
|
||||
.notNullable(),
|
||||
new JsonNullableProperty<>(
|
||||
"emailAddress",
|
||||
CredentialsPatchResource::setEmailAddress,
|
||||
PATCHED_EMAIL_ADDRESS,
|
||||
HsCredentialsEntity::setEmailAddress,
|
||||
PATCHED_EMAIL_ADDRESS),
|
||||
new SimpleProperty<>(
|
||||
"totpSecret",
|
||||
CredentialsPatchResource::setTotpSecrets,
|
||||
PATCHED_TOTP_SECRETS,
|
||||
HsCredentialsEntity::setTotpSecrets,
|
||||
PATCHED_TOTP_SECRETS)
|
||||
.notNullable(),
|
||||
new JsonNullableProperty<>(
|
||||
"smsNumber",
|
||||
CredentialsPatchResource::setSmsNumber,
|
||||
PATCHED_SMS_NUMBER,
|
||||
HsCredentialsEntity::setSmsNumber,
|
||||
PATCHED_SMS_NUMBER),
|
||||
new JsonNullableProperty<>(
|
||||
"phonePassword",
|
||||
CredentialsPatchResource::setPhonePassword,
|
||||
PATCHED_PHONE_PASSWORD,
|
||||
HsCredentialsEntity::setPhonePassword,
|
||||
PATCHED_PHONE_PASSWORD),
|
||||
new SimpleProperty<>(
|
||||
"contexts",
|
||||
CredentialsPatchResource::setContexts,
|
||||
patchedContextResources,
|
||||
HsCredentialsEntity::setLoginContexts,
|
||||
expectedPatchedContextEntities)
|
||||
.notNullable()
|
||||
);
|
||||
}
|
||||
}
|
||||
+78
-115
@@ -4,7 +4,7 @@ import io.restassured.RestAssured;
|
||||
import io.restassured.http.ContentType;
|
||||
import lombok.val;
|
||||
import net.hostsharing.hsadminng.rbac.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.accounts.HsCredentialsEntity.HsCredentialsEntityBuilder;
|
||||
import net.hostsharing.hsadminng.hs.accounts.HsProfileEntity.HsProfileEntityBuilder;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealRepository;
|
||||
import net.hostsharing.hsadminng.rbac.subject.RbacSubjectEntity;
|
||||
@@ -27,7 +27,6 @@ import jakarta.persistence.PersistenceContext;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.hostsharing.hsadminng.config.JwtFakeBearer.bearer;
|
||||
import static net.hostsharing.hsadminng.hs.office.person.HsOfficePersonType.LEGAL_PERSON;
|
||||
@@ -36,9 +35,6 @@ import static net.hostsharing.hsadminng.test.JsonMatcher.lenientlyEquals;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
@Tag("generalIntegrationTest")
|
||||
@Transactional
|
||||
@@ -47,7 +43,7 @@ import static org.hamcrest.Matchers.nullValue;
|
||||
)
|
||||
@ActiveProfiles("fake-jwt")
|
||||
// too complex database interaction for just a RestTest, thus a fully integrated test
|
||||
class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
class HsProfileControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
@LocalServerPort
|
||||
Integer port;
|
||||
@@ -62,13 +58,13 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
HsOfficePersonRealRepository realPersonRepo;
|
||||
|
||||
@Autowired
|
||||
HsCredentialsContextRealRepository contextRepo;
|
||||
HsProfileScopeRealRepository scopeRepo;
|
||||
|
||||
@Autowired
|
||||
HsCredentialsRepository credentialsRepo;
|
||||
HsProfileRepository profileRepo;
|
||||
|
||||
@Autowired
|
||||
HsCredentialsContextRbacRepository loginContextRbacRepo;
|
||||
HsProfileScopeRbacRepository scopeRbacRepo;
|
||||
|
||||
@Autowired
|
||||
JpaAttempt jpaAttempt;
|
||||
@@ -105,23 +101,23 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
}
|
||||
|
||||
@Nested
|
||||
class GetCredentialsByUuid {
|
||||
class GetProfileByUuid {
|
||||
|
||||
@Test
|
||||
void shouldFilterInvalidContextsRegardingNonNaturalPerson() {
|
||||
void shouldFilterInvalidScopesRegardingNonNaturalPerson() {
|
||||
// given
|
||||
val legalPerson = givenLegalPerson("selfregistered-user-drew@hostsharing.org");
|
||||
val credentialsEntity = givenNewCredentials("selfregistered-user-drew@hostsharing.org",
|
||||
val profileEntity = givenNewProfile("selfregistered-user-drew@hostsharing.org",
|
||||
"test-subject1", legalPerson, builder -> {
|
||||
builder.loginContexts(new HashSet<>(contextRepo.findAll()));
|
||||
builder.scopes(new HashSet<>(scopeRepo.findAll()));
|
||||
});
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("Authorization", bearer(credentialsEntity.getSubject().getName()))
|
||||
.header("Authorization", bearer(profileEntity.getSubject().getName()))
|
||||
.port(port)
|
||||
.when()
|
||||
.get("http://localhost/api/hs/accounts/credentials/" + credentialsEntity.getUuid())
|
||||
.get("http://localhost/api/hs/accounts/profiles/" + profileEntity.getUuid())
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
@@ -143,8 +139,7 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"active": false,
|
||||
"globalUid": null,
|
||||
"globalGid": null,
|
||||
"onboardingToken": null,
|
||||
"contexts": [
|
||||
"scopes": [
|
||||
{
|
||||
"uuid": "33333333-3333-3333-3333-333333333333",
|
||||
"type": "SSH",
|
||||
@@ -166,8 +161,7 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"onlyForNaturalPersons": false,
|
||||
"publicAccess": true
|
||||
}
|
||||
],
|
||||
"lastUsed": null
|
||||
]
|
||||
}
|
||||
"""));
|
||||
// @formatter:on
|
||||
@@ -175,14 +169,14 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
}
|
||||
|
||||
@Nested
|
||||
class PostNewCredentials {
|
||||
class PostNewProfile {
|
||||
|
||||
@Test
|
||||
void shouldRejectCreatingCredentialsForUnrepresentedPerson() {
|
||||
void shouldRejectCreatingProfileForUnrepresentedPerson() {
|
||||
// given
|
||||
val testPerson = givenPersonWithUuid("selfregistered-user-drew@hostsharing.org");
|
||||
val publicContext = contextRepo.findByTypeAndQualifier("SSH", "external").orElseThrow();
|
||||
assertThat(publicContext.isPublicAccess()).as("precondition failed").isTrue();
|
||||
val publicScope = scopeRepo.findByTypeAndQualifier("SSH", "external").orElseThrow();
|
||||
assertThat(publicScope.isPublicAccess()).as("precondition failed").isTrue();
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
@@ -196,16 +190,16 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"active": true,
|
||||
"globalUid": 30001,
|
||||
"globalGid": 40001,
|
||||
"contexts": [
|
||||
"scopes": [
|
||||
{
|
||||
"uuid" : "%s"
|
||||
}
|
||||
]
|
||||
}
|
||||
""".formatted(testPerson.getUuid(), publicContext.getUuid()))
|
||||
""".formatted(testPerson.getUuid(), publicScope.getUuid()))
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/accounts/credentials")
|
||||
.post("http://localhost/api/hs/accounts/profiles")
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(400)
|
||||
.contentType("application/json")
|
||||
@@ -214,15 +208,15 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectCreatingCredentialsWithPrivateContextForNormalUser() {
|
||||
void shouldRejectCreatingProfileWithPrivateScopeForNormalUser() {
|
||||
// given
|
||||
val drewPerson = realPersonRepo.findPersonByOptionalNameLike("Drew").getFirst();
|
||||
val privateInternalSshContext = contextRepo.findByTypeAndQualifier("SSH", "internal")
|
||||
.map(HsCredentialsControllerAcceptanceTest::asPrivateContext).orElseThrow();
|
||||
val privateInternalMatrixContext = contextRepo.findByTypeAndQualifier("MATRIX", "internal")
|
||||
.map(HsCredentialsControllerAcceptanceTest::asPrivateContext).orElseThrow();
|
||||
val publicExternalMatrixContext = contextRepo.findByTypeAndQualifier("MATRIX", "external")
|
||||
.map(HsCredentialsControllerAcceptanceTest::asPublicContext).orElseThrow();
|
||||
val privateInternalSshScope = scopeRepo.findByTypeAndQualifier("SSH", "internal")
|
||||
.map(HsProfileControllerAcceptanceTest::asPrivateScope).orElseThrow();
|
||||
val privateInternalMatrixScope = scopeRepo.findByTypeAndQualifier("MATRIX", "internal")
|
||||
.map(HsProfileControllerAcceptanceTest::asPrivateScope).orElseThrow();
|
||||
val publicExternalMatrixScope = scopeRepo.findByTypeAndQualifier("MATRIX", "external")
|
||||
.map(HsProfileControllerAcceptanceTest::asPublicScope).orElseThrow();
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
@@ -236,7 +230,7 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"active": true,
|
||||
"globalUid": 30001,
|
||||
"globalGid": 40001,
|
||||
"contexts": [
|
||||
"scopes": [
|
||||
{ "uuid" : "%s" },
|
||||
{ "uuid" : "%s" },
|
||||
{ "uuid" : "%s" }
|
||||
@@ -244,25 +238,25 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
}
|
||||
""".formatted(
|
||||
drewPerson.getUuid(),
|
||||
publicExternalMatrixContext.getUuid(),
|
||||
privateInternalSshContext.getUuid(),
|
||||
privateInternalMatrixContext.getUuid()))
|
||||
publicExternalMatrixScope.getUuid(),
|
||||
privateInternalSshScope.getUuid(),
|
||||
privateInternalMatrixScope.getUuid()))
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/accounts/credentials")
|
||||
.post("http://localhost/api/hs/accounts/profiles")
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(400)
|
||||
.contentType("application/json")
|
||||
.body("message", containsString("Kontext-Zugriff verweigert: 'MATRIX:internal', 'SSH:internal'"));
|
||||
.body("message", containsString("Zugriff auf Geltungsbereich verweigert: 'MATRIX:internal', 'SSH:internal'"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectCreatingCredentialsWithNaturalPersonRequirementForNonNaturalPerson() {
|
||||
void shouldRejectCreatingProfileWithNaturalPersonRequirementForNonNaturalPerson() {
|
||||
// given
|
||||
val firstGmbHPerson = realPersonRepo.findPersonByOptionalNameLike("First").getFirst();
|
||||
val hsadminProdContextOnlyForNaturalPersons = contextRepo.findByTypeAndQualifier("HSADMIN", "prod")
|
||||
.map(HsCredentialsControllerAcceptanceTest::asNaturalPersonContext).orElseThrow();
|
||||
val hsadminProdScopeOnlyForNaturalPersons = scopeRepo.findByTypeAndQualifier("HSADMIN", "prod")
|
||||
.map(HsProfileControllerAcceptanceTest::asNaturalPersonScope).orElseThrow();
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
@@ -276,39 +270,39 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
"active": true,
|
||||
"globalUid": 30001,
|
||||
"globalGid": 40001,
|
||||
"contexts": [
|
||||
"scopes": [
|
||||
{ "uuid" : "%s" }
|
||||
]
|
||||
}
|
||||
""".formatted(
|
||||
firstGmbHPerson.getUuid(),
|
||||
hsadminProdContextOnlyForNaturalPersons.getUuid()))
|
||||
hsadminProdScopeOnlyForNaturalPersons.getUuid()))
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/accounts/credentials")
|
||||
.post("http://localhost/api/hs/accounts/profiles")
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(400)
|
||||
.contentType("application/json")
|
||||
.body("message", containsString("Kontext verlangt eine natürliche Person: 'HSADMIN:prod'"));
|
||||
.body("message", containsString("Geltungsbereich verlangt eine natürliche Person: 'HSADMIN:prod'"));
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class PatchCredentials {
|
||||
class PatchProfile {
|
||||
|
||||
@Test
|
||||
void shouldRejectPatchingCredentialsWithPrivateContextForNormalUser() {
|
||||
void shouldRejectPatchingProfileWithPrivateScopeForNormalUser() {
|
||||
// given
|
||||
context.define("selfregistered-user-drew@hostsharing.org");
|
||||
val drewCredentialsUuid = credentialsRepo.findByCurrentSubject().stream().findFirst().orElseThrow()
|
||||
val drewProfileUuid = profileRepo.findByCurrentSubject().stream().findFirst().orElseThrow()
|
||||
.getSubject().getUuid();
|
||||
val privateInternalSshContext = contextRepo.findByTypeAndQualifier("SSH", "internal")
|
||||
.map(HsCredentialsControllerAcceptanceTest::asPrivateContext).orElseThrow();
|
||||
val privateInternalMatrixContext = contextRepo.findByTypeAndQualifier("MATRIX", "internal")
|
||||
.map(HsCredentialsControllerAcceptanceTest::asPrivateContext).orElseThrow();
|
||||
val publicExternalMatrixContext = contextRepo.findByTypeAndQualifier("MATRIX", "external")
|
||||
.map(HsCredentialsControllerAcceptanceTest::asPublicContext).orElseThrow();
|
||||
val privateInternalSshScope = scopeRepo.findByTypeAndQualifier("SSH", "internal")
|
||||
.map(HsProfileControllerAcceptanceTest::asPrivateScope).orElseThrow();
|
||||
val privateInternalMatrixScope = scopeRepo.findByTypeAndQualifier("MATRIX", "internal")
|
||||
.map(HsProfileControllerAcceptanceTest::asPrivateScope).orElseThrow();
|
||||
val publicExternalMatrixScope = scopeRepo.findByTypeAndQualifier("MATRIX", "external")
|
||||
.map(HsProfileControllerAcceptanceTest::asPublicScope).orElseThrow();
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
@@ -317,34 +311,34 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"contexts": [
|
||||
"scopes": [
|
||||
{ "uuid" : "%s" },
|
||||
{ "uuid" : "%s" },
|
||||
{ "uuid" : "%s" }
|
||||
]
|
||||
}
|
||||
""".formatted(
|
||||
privateInternalSshContext.getUuid(),
|
||||
publicExternalMatrixContext.getUuid(),
|
||||
privateInternalMatrixContext.getUuid()))
|
||||
privateInternalSshScope.getUuid(),
|
||||
publicExternalMatrixScope.getUuid(),
|
||||
privateInternalMatrixScope.getUuid()))
|
||||
.port(port)
|
||||
.when()
|
||||
.patch("http://localhost/api/hs/accounts/credentials/" + drewCredentialsUuid)
|
||||
.patch("http://localhost/api/hs/accounts/profiles/" + drewProfileUuid)
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(400)
|
||||
.contentType("application/json")
|
||||
.body("message", containsString("Kontext-Zugriff verweigert: 'MATRIX:internal', 'SSH:internal'"));
|
||||
.body("message", containsString("Zugriff auf Geltungsbereich verweigert: 'MATRIX:internal', 'SSH:internal'"));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectPatchingCredentialsAndRemovingTheOwnHsadminCredentials() {
|
||||
void shouldRejectPatchingProfileAndRemovingTheOwnHsadminProfile() {
|
||||
// given
|
||||
context.define("selfregistered-user-drew@hostsharing.org");
|
||||
val drewCredentialsUuid = credentialsRepo.findByCurrentSubject().stream().findFirst().orElseThrow()
|
||||
val drewProfileUuid = profileRepo.findByCurrentSubject().stream().findFirst().orElseThrow()
|
||||
.getSubject().getUuid();
|
||||
val publicExternalMatrixContext = contextRepo.findByTypeAndQualifier("MATRIX", "external")
|
||||
.map(HsCredentialsControllerAcceptanceTest::asPublicContext).orElseThrow();
|
||||
val publicExternalMatrixScope = scopeRepo.findByTypeAndQualifier("MATRIX", "external")
|
||||
.map(HsProfileControllerAcceptanceTest::asPublicScope).orElseThrow();
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
@@ -353,49 +347,18 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
.contentType(ContentType.JSON)
|
||||
.body("""
|
||||
{
|
||||
"contexts": [
|
||||
"scopes": [
|
||||
{ "uuid" : "%s" }
|
||||
]
|
||||
}
|
||||
""".formatted(publicExternalMatrixContext.getUuid()))
|
||||
""".formatted(publicExternalMatrixScope.getUuid()))
|
||||
.port(port)
|
||||
.when()
|
||||
.patch("http://localhost/api/hs/accounts/credentials/" + drewCredentialsUuid)
|
||||
.patch("http://localhost/api/hs/accounts/profiles/" + drewProfileUuid)
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(400)
|
||||
.contentType("application/json")
|
||||
.body("message", containsString("die eigenen hsadmin-Credentials dürfen nicht entfernt werden"));
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class MarkCredentialsAsUsed {
|
||||
|
||||
@Test
|
||||
void markCredentialsAsUsed() {
|
||||
// given
|
||||
val testPerson = givenNaturalPerson("selfregistered-user-drew@hostsharing.org");
|
||||
val credentialsEntity = givenNewCredentials("selfregistered-user-drew@hostsharing.org",
|
||||
"test-subject2",
|
||||
testPerson, builder -> {
|
||||
builder.onboardingToken("some-onboarding-token");
|
||||
builder.loginContexts(contextRepo.findAll().stream()
|
||||
.filter(HsCredentialsContext::isPublicAccess).collect(Collectors.toSet()));
|
||||
});
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
.header("Authorization", bearer("superuser-alex@hostsharing.net"))
|
||||
.port(port)
|
||||
.when()
|
||||
.post("http://localhost/api/hs/accounts/credentials/" + credentialsEntity.getUuid() + "/used")
|
||||
.then().log().all().assertThat()
|
||||
.statusCode(200)
|
||||
.contentType("application/json")
|
||||
.body("uuid", is(credentialsEntity.getUuid().toString()))
|
||||
.body("onboardingToken", is(nullValue()))
|
||||
.body("lastUsed", is(not(nullValue())));
|
||||
.body("message", containsString("die eigenen hsadmin-Profile dürfen nicht entfernt werden"));
|
||||
// @formatter:on
|
||||
}
|
||||
}
|
||||
@@ -433,25 +396,25 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
}).returnedValue();
|
||||
}
|
||||
|
||||
private static HsCredentialsContextRealEntity asNaturalPersonContext(@NotNull HsCredentialsContextRealEntity context) {
|
||||
assertThat(context.isOnlyForNaturalPersons()).as("precondition failed").isTrue();
|
||||
return context;
|
||||
private static HsProfileScopeRealEntity asNaturalPersonScope(@NotNull HsProfileScopeRealEntity scope) {
|
||||
assertThat(scope.isOnlyForNaturalPersons()).as("precondition failed").isTrue();
|
||||
return scope;
|
||||
}
|
||||
|
||||
private static HsCredentialsContextRealEntity asPrivateContext(@NotNull HsCredentialsContextRealEntity context) {
|
||||
assertThat(context.isPublicAccess()).as("precondition failed").isFalse();
|
||||
return context;
|
||||
private static HsProfileScopeRealEntity asPrivateScope(@NotNull HsProfileScopeRealEntity scope) {
|
||||
assertThat(scope.isPublicAccess()).as("precondition failed").isFalse();
|
||||
return scope;
|
||||
}
|
||||
|
||||
private static HsCredentialsContextRealEntity asPublicContext(@NotNull HsCredentialsContextRealEntity context) {
|
||||
assertThat(context.isPublicAccess()).as("precondition failed").isTrue();
|
||||
return context;
|
||||
private static HsProfileScopeRealEntity asPublicScope(@NotNull HsProfileScopeRealEntity scope) {
|
||||
assertThat(scope.isPublicAccess()).as("precondition failed").isTrue();
|
||||
return scope;
|
||||
}
|
||||
|
||||
private HsCredentialsEntity givenNewCredentials(
|
||||
private HsProfileEntity givenNewProfile(
|
||||
final String executingSubjectName,
|
||||
final String newSubjectName, final HsOfficePersonRealEntity person,
|
||||
final Consumer<HsCredentialsEntityBuilder> modifier
|
||||
final Consumer<HsProfileEntityBuilder> modifier
|
||||
) {
|
||||
return jpaAttempt.transacted(() -> {
|
||||
context.define(executingSubjectName);
|
||||
@@ -462,12 +425,12 @@ class HsCredentialsControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
||||
|
||||
context.define(subject.getName());
|
||||
val attachedPerson = em.find(HsOfficePersonRealEntity.class, person.getUuid());
|
||||
val credentialsBuilder = HsCredentialsEntity.builder()
|
||||
val profileBuilder = HsProfileEntity.builder()
|
||||
.person(attachedPerson)
|
||||
.subject(subjectRepo.findByUuid(subject.getUuid()))
|
||||
.loginContexts(Set.of());
|
||||
modifier.accept(credentialsBuilder);
|
||||
return toCleanup(credentialsRepo.save(credentialsBuilder.build()));
|
||||
.scopes(Set.of());
|
||||
modifier.accept(profileBuilder);
|
||||
return toCleanup(profileRepo.save(profileBuilder.build()));
|
||||
}).assertSuccessful().returnedValue();
|
||||
}
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
package net.hostsharing.hsadminng.hs.accounts;
|
||||
|
||||
import lombok.val;
|
||||
import net.hostsharing.hsadminng.config.MessageTranslator;
|
||||
import net.hostsharing.hsadminng.accounts.generated.api.v1.model.ScopeResource;
|
||||
import net.hostsharing.hsadminng.accounts.generated.api.v1.model.ProfilePatchResource;
|
||||
import net.hostsharing.hsadminng.rbac.test.PatchUnitTestBase;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@TestInstance(PER_CLASS)
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class HsProfileEntityPatcherUnitTest extends PatchUnitTestBase<
|
||||
ProfilePatchResource,
|
||||
HsProfileEntity
|
||||
> {
|
||||
|
||||
private static final UUID INITIAL_PROFILE_UUID = UUID.randomUUID();
|
||||
|
||||
private static final Boolean INITIAL_ACTIVE = true;
|
||||
private static final String INITIAL_EMAIL_ADDRESS = "initial@example.com";
|
||||
private static final List<String> INITIAL_TOTP_SECRETS = List.of("initial_2fa");
|
||||
private static final String INITIAL_SMS_NUMBER = "initial_sms";
|
||||
private static final String INITIAL_PHONE_PASSWORD = "initial_phone_pw";
|
||||
|
||||
private static final Boolean PATCHED_ACTIVE = false;
|
||||
private static final String PATCHED_EMAIL_ADDRESS = "patched@example.com";
|
||||
private static final List<String> PATCHED_TOTP_SECRETS = List.of("patched_2fa");
|
||||
private static final String PATCHED_SMS_NUMBER = "patched_sms";
|
||||
private static final String PATCHED_PHONE_PASSWORD = "patched_phone_pw";
|
||||
|
||||
// Scopes
|
||||
private static final UUID SCOPE_UUID_1 = UUID.randomUUID();
|
||||
private static final UUID SCOPE_UUID_2 = UUID.randomUUID();
|
||||
private static final UUID SCOPE_UUID_3 = UUID.randomUUID();
|
||||
|
||||
private final HsProfileScopeRealEntity initialScopeEntity1 = HsProfileScopeRealEntity.builder()
|
||||
.uuid(SCOPE_UUID_1)
|
||||
.type("HSADMIN")
|
||||
.qualifier("prod")
|
||||
.build();
|
||||
private final HsProfileScopeRealEntity initialScopeEntity2 = HsProfileScopeRealEntity.builder()
|
||||
.uuid(SCOPE_UUID_2)
|
||||
.type("SSH")
|
||||
.qualifier("dev")
|
||||
.build();
|
||||
|
||||
// This is what em.find should return for SCOPE_UUID_3
|
||||
private final HsProfileScopeRealEntity newScopeEntity3 = HsProfileScopeRealEntity.builder()
|
||||
.uuid(SCOPE_UUID_3)
|
||||
.type("HSADMIN")
|
||||
.qualifier("test")
|
||||
.build();
|
||||
|
||||
private final Set<HsProfileScopeRealEntity> initialScopeEntities = Set.of(initialScopeEntity1, initialScopeEntity2);
|
||||
private List<ScopeResource> patchedScopeResources;
|
||||
private final Set<HsProfileScopeRealEntity> expectedPatchedScopeEntities = Set.of(initialScopeEntity2,
|
||||
newScopeEntity3);
|
||||
|
||||
@Mock
|
||||
private EntityManager em;
|
||||
|
||||
@BeforeEach
|
||||
void initMocks() {
|
||||
// Mock em.find for scopes that are part of the patch and need to be fetched
|
||||
lenient().when(em.find(eq(HsProfileScopeRealEntity.class), eq(SCOPE_UUID_1))).thenReturn(initialScopeEntity1);
|
||||
lenient().when(em.find(eq(HsProfileScopeRealEntity.class), eq(SCOPE_UUID_2))).thenReturn(initialScopeEntity2);
|
||||
lenient().when(em.find(eq(HsProfileScopeRealEntity.class), eq(SCOPE_UUID_3))).thenReturn(newScopeEntity3);
|
||||
|
||||
val patchScopeResource2 = new ScopeResource();
|
||||
patchScopeResource2.setUuid(SCOPE_UUID_2);
|
||||
patchScopeResource2.setType("SSH");
|
||||
patchScopeResource2.setQualifier("dev");
|
||||
|
||||
val patchScopeResource3 = new ScopeResource();
|
||||
patchScopeResource3.setUuid(SCOPE_UUID_3);
|
||||
patchScopeResource3.setType("HSADMIN");
|
||||
patchScopeResource3.setQualifier("test");
|
||||
|
||||
patchedScopeResources = List.of(patchScopeResource2, patchScopeResource3);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HsProfileEntity newInitialEntity() {
|
||||
final var entity = new HsProfileEntity();
|
||||
entity.setUuid(INITIAL_PROFILE_UUID);
|
||||
entity.setActive(INITIAL_ACTIVE);
|
||||
entity.setEmailAddress(INITIAL_EMAIL_ADDRESS);
|
||||
entity.setTotpSecrets(INITIAL_TOTP_SECRETS);
|
||||
entity.setSmsNumber(INITIAL_SMS_NUMBER);
|
||||
entity.setPhonePassword(INITIAL_PHONE_PASSWORD);
|
||||
// Ensure scopes is a mutable set for the patcher
|
||||
entity.setScopes(new HashSet<>(initialScopeEntities));
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProfilePatchResource newPatchResource() {
|
||||
return new ProfilePatchResource();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HsProfileEntityPatcher createPatcher(final HsProfileEntity entity) {
|
||||
final var scopeMapper = new ScopeResourceToEntityMapper(em, mock(MessageTranslator.class));
|
||||
return new HsProfileEntityPatcher(scopeMapper, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<Property> propertyTestDescriptors() {
|
||||
return Stream.of(
|
||||
new SimpleProperty<>(
|
||||
"active",
|
||||
ProfilePatchResource::setActive,
|
||||
PATCHED_ACTIVE,
|
||||
HsProfileEntity::setActive,
|
||||
PATCHED_ACTIVE)
|
||||
.notNullable(),
|
||||
new JsonNullableProperty<>(
|
||||
"emailAddress",
|
||||
ProfilePatchResource::setEmailAddress,
|
||||
PATCHED_EMAIL_ADDRESS,
|
||||
HsProfileEntity::setEmailAddress,
|
||||
PATCHED_EMAIL_ADDRESS),
|
||||
new SimpleProperty<>(
|
||||
"totpSecret",
|
||||
ProfilePatchResource::setTotpSecrets,
|
||||
PATCHED_TOTP_SECRETS,
|
||||
HsProfileEntity::setTotpSecrets,
|
||||
PATCHED_TOTP_SECRETS)
|
||||
.notNullable(),
|
||||
new JsonNullableProperty<>(
|
||||
"smsNumber",
|
||||
ProfilePatchResource::setSmsNumber,
|
||||
PATCHED_SMS_NUMBER,
|
||||
HsProfileEntity::setSmsNumber,
|
||||
PATCHED_SMS_NUMBER),
|
||||
new JsonNullableProperty<>(
|
||||
"phonePassword",
|
||||
ProfilePatchResource::setPhonePassword,
|
||||
PATCHED_PHONE_PASSWORD,
|
||||
HsProfileEntity::setPhonePassword,
|
||||
PATCHED_PHONE_PASSWORD),
|
||||
new SimpleProperty<>(
|
||||
"scopes",
|
||||
ProfilePatchResource::setScopes,
|
||||
patchedScopeResources,
|
||||
HsProfileEntity::setScopes,
|
||||
expectedPatchedScopeEntities)
|
||||
.notNullable()
|
||||
);
|
||||
}
|
||||
}
|
||||
+51
-51
@@ -34,7 +34,7 @@ import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
@DataJpaTest
|
||||
@Tag("generalIntegrationTest")
|
||||
@Import({ Context.class, JpaAttempt.class })
|
||||
class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
|
||||
class HsProfileRepositoryIntegrationTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
private static final String SUPERUSER_ALEX_SUBJECT_NAME = "superuser-alex@hostsharing.net";
|
||||
private static final String SUPERUSER_FRAN_SUBJECT_NAME = "superuser-fran@hostsharing.net";
|
||||
@@ -52,10 +52,10 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
private HsOfficePersonRealRepository personRepo;
|
||||
|
||||
@Autowired
|
||||
private HsCredentialsRepository credentialsRepository;
|
||||
private HsProfileRepository profileRepository;
|
||||
|
||||
@Autowired
|
||||
private HsCredentialsContextRealRepository loginContextRealRepo;
|
||||
private HsProfileScopeRealRepository scopeRealRepo;
|
||||
|
||||
// fetched UUIDs from test-data
|
||||
private RbacSubjectEntity alexSubject;
|
||||
@@ -76,7 +76,7 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
@Test
|
||||
public void historizationIsAvailable() {
|
||||
// given
|
||||
final String nativeQuerySql = "select * from hs_accounts.credentials_hv";
|
||||
final String nativeQuerySql = "select * from hs_accounts.profile_hv";
|
||||
|
||||
// when
|
||||
historicalContext(Timestamp.from(ZonedDateTime.now().minusDays(1).toInstant()));
|
||||
@@ -85,7 +85,7 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
|
||||
// then
|
||||
assertThat(rowsBefore)
|
||||
.as("hs_accounts.credentials_hv only contain no rows for a timestamp before test data creation")
|
||||
.as("hs_accounts.profile_hv only contain no rows for a timestamp before test data creation")
|
||||
.hasSize(0);
|
||||
|
||||
// and when
|
||||
@@ -95,86 +95,86 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
|
||||
// then
|
||||
assertThat(rowsAfter)
|
||||
.as("hs_accounts.credentials_hv should now contain the test-data rows for the current timestamp")
|
||||
.as("hs_accounts.profile_hv should now contain the test-data rows for the current timestamp")
|
||||
.hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void representativeShouldFindOwnAndRepresentedCredentialsByCurrentSubject() {
|
||||
void representativeShouldFindOwnAndRepresentedProfileByCurrentSubject() {
|
||||
// given
|
||||
final var firstGmbHPerson = givenPerson("First GmbH");
|
||||
givenRelation(REPRESENTATIVE)
|
||||
.withAnchorPersonLike(firstGmbHPerson)
|
||||
.withHolder(drewPerson)
|
||||
.withContact("some test contact");
|
||||
givenCredentials()
|
||||
givenProfile()
|
||||
.forSubject("first-gmbh")
|
||||
.forPerson(firstGmbHPerson)
|
||||
.withEMailAddress("first-gmbh@example.com");
|
||||
|
||||
// when
|
||||
final var foundCredentials = attempt(
|
||||
final var foundProfile = attempt(
|
||||
em, () -> {
|
||||
context(USER_DREW_SUBJECT_NAME);
|
||||
return credentialsRepository.findByCurrentSubject();
|
||||
return profileRepository.findByCurrentSubject();
|
||||
})
|
||||
.assertNotNull().returnedValue();
|
||||
|
||||
// then
|
||||
assertThat(foundCredentials).hasSize(2)
|
||||
.map(HsCredentialsEntity::getEmailAddress)
|
||||
assertThat(foundProfile).hasSize(2)
|
||||
.map(HsProfileEntity::getEmailAddress)
|
||||
.containsExactlyInAnyOrder("drew@example.org", "first-gmbh@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void globalAdminShouldFindOnlyOwnCredentialsByCurrentSubject() {
|
||||
void globalAdminShouldFindOnlyOwnProfileByCurrentSubject() {
|
||||
|
||||
// when
|
||||
final var foundCredentials = attempt(
|
||||
final var foundProfile = attempt(
|
||||
em, () -> {
|
||||
context(SUPERUSER_FRAN_SUBJECT_NAME);
|
||||
return credentialsRepository.findByCurrentSubject();
|
||||
return profileRepository.findByCurrentSubject();
|
||||
})
|
||||
.assertNotNull().returnedValue();
|
||||
|
||||
// then
|
||||
assertThat(foundCredentials).hasSize(1)
|
||||
.map(HsCredentialsEntity::getEmailAddress)
|
||||
assertThat(foundProfile).hasSize(1)
|
||||
.map(HsProfileEntity::getEmailAddress)
|
||||
.containsExactlyInAnyOrder("fran@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFindByUuidUsingTestData() {
|
||||
// when
|
||||
final var foundEntityOptional = credentialsRepository.findByUuid(alexSubject.getUuid());
|
||||
final var foundEntityOptional = profileRepository.findByUuid(alexSubject.getUuid());
|
||||
|
||||
// then
|
||||
assertThat(foundEntityOptional).isPresent()
|
||||
.map(HsCredentialsEntity::getEmailAddress).contains("alex@example.com");
|
||||
.map(HsProfileEntity::getEmailAddress).contains("alex@example.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveCredentialsWithExistingContext() {
|
||||
void shouldSaveProfileWithExistingScope() {
|
||||
// given
|
||||
final var existingContext = loginContextRealRepo.findByTypeAndQualifier("HSADMIN", "prod")
|
||||
final var existingScope = scopeRealRepo.findByTypeAndQualifier("HSADMIN", "prod")
|
||||
.orElseThrow();
|
||||
final var newCredentials = HsCredentialsEntity.builder()
|
||||
final var newProfile = HsProfileEntity.builder()
|
||||
.subject(testUserSubject)
|
||||
.person(testUserPerson)
|
||||
.active(true)
|
||||
.emailAddress("test-user@example.com")
|
||||
.globalUid(2011)
|
||||
.globalGid(2011)
|
||||
.loginContexts(mutableSetOf(existingContext))
|
||||
.scopes(mutableSetOf(existingScope))
|
||||
.build();
|
||||
|
||||
// when
|
||||
toCleanup(credentialsRepository.save(newCredentials));
|
||||
toCleanup(profileRepository.save(newProfile));
|
||||
em.flush();
|
||||
em.clear();
|
||||
|
||||
// then
|
||||
final var foundEntityOptional = credentialsRepository.findByUuid(testUserSubject.getUuid());
|
||||
final var foundEntityOptional = profileRepository.findByUuid(testUserSubject.getUuid());
|
||||
assertThat(foundEntityOptional).isPresent();
|
||||
final var foundEntity = foundEntityOptional.get();
|
||||
assertThat(foundEntity.getEmailAddress()).isEqualTo("test-user@example.com");
|
||||
@@ -182,29 +182,29 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
assertThat(foundEntity.getVersion()).isEqualTo(0); // Initial version
|
||||
assertThat(foundEntity.getGlobalUid()).isEqualTo(2011);
|
||||
|
||||
assertThat(foundEntity.getLoginContexts()).hasSize(1)
|
||||
.map(HsCredentialsContextRealEntity::toString).contains("loginContext(HSADMIN:prod:NP-ONLY:PUBLIC)");
|
||||
assertThat(foundEntity.getScopes()).hasSize(1)
|
||||
.map(HsProfileScopeRealEntity::toString).contains("scope(HSADMIN:prod:NP-ONLY:PUBLIC)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotSaveCredentialsWithNewContext() {
|
||||
void shouldNotSaveProfileWithNewScope() {
|
||||
// given
|
||||
final var newContext = HsCredentialsContextRealEntity.builder()
|
||||
final var newScope = HsProfileScopeRealEntity.builder()
|
||||
.type("MATRIX")
|
||||
.qualifier("forbidden")
|
||||
.build();
|
||||
final var newCredentials = HsCredentialsEntity.builder()
|
||||
final var newProfile = HsProfileEntity.builder()
|
||||
.subject(drewSubject)
|
||||
.active(true)
|
||||
.emailAddress("drew.new@example.com")
|
||||
.globalUid(2001)
|
||||
.globalGid(2001)
|
||||
.loginContexts(mutableSetOf(newContext))
|
||||
.scopes(mutableSetOf(newScope))
|
||||
.build();
|
||||
|
||||
// when
|
||||
final var exception = catchThrowable(() -> {
|
||||
credentialsRepository.save(newCredentials);
|
||||
profileRepository.save(newProfile);
|
||||
em.flush();
|
||||
});
|
||||
|
||||
@@ -213,9 +213,9 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveNewCredentialsWithoutContext() {
|
||||
void shouldSaveNewProfileWithoutScope() {
|
||||
// given
|
||||
final var newCredentials = HsCredentialsEntity.builder()
|
||||
final var newProfile = HsProfileEntity.builder()
|
||||
.subject(testUserSubject)
|
||||
.person(testUserPerson)
|
||||
.active(true)
|
||||
@@ -225,37 +225,37 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
.build();
|
||||
|
||||
// when
|
||||
credentialsRepository.save(newCredentials);
|
||||
profileRepository.save(newProfile);
|
||||
em.flush();
|
||||
em.clear();
|
||||
|
||||
// then
|
||||
final var foundEntityOptional = credentialsRepository.findByUuid(testUserSubject.getUuid());
|
||||
final var foundEntityOptional = profileRepository.findByUuid(testUserSubject.getUuid());
|
||||
assertThat(foundEntityOptional).isPresent();
|
||||
final var foundEntity = foundEntityOptional.get();
|
||||
assertThat(foundEntity.getEmailAddress()).isEqualTo("test.user.new@example.com");
|
||||
assertThat(foundEntity.isActive()).isTrue();
|
||||
assertThat(foundEntity.getGlobalUid()).isEqualTo(20002);
|
||||
assertThat(foundEntity.getGlobalGid()).isEqualTo(2002);
|
||||
assertThat(foundEntity.getLoginContexts()).isEmpty();
|
||||
assertThat(foundEntity.getScopes()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateExistingCredentials() {
|
||||
void shouldUpdateExistingProfile() {
|
||||
// given
|
||||
final var entityToUpdate = credentialsRepository.findByUuid(alexSubject.getUuid()).orElseThrow();
|
||||
final var entityToUpdate = profileRepository.findByUuid(alexSubject.getUuid()).orElseThrow();
|
||||
final var initialVersion = entityToUpdate.getVersion();
|
||||
|
||||
// when
|
||||
entityToUpdate.setActive(false);
|
||||
entityToUpdate.setEmailAddress("updated.user1@example.com");
|
||||
final var savedEntity = credentialsRepository.save(entityToUpdate);
|
||||
final var savedEntity = profileRepository.save(entityToUpdate);
|
||||
em.flush();
|
||||
em.clear();
|
||||
|
||||
// then
|
||||
assertThat(savedEntity.getVersion()).isGreaterThan(initialVersion);
|
||||
final var updatedEntityOptional = credentialsRepository.findByUuid(alexSubject.getUuid());
|
||||
final var updatedEntityOptional = profileRepository.findByUuid(alexSubject.getUuid());
|
||||
assertThat(updatedEntityOptional).isPresent();
|
||||
final var updatedEntity = updatedEntityOptional.get();
|
||||
assertThat(updatedEntity.isActive()).isFalse();
|
||||
@@ -307,8 +307,8 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
return new RelationBuilder(relationType);
|
||||
}
|
||||
|
||||
private CredentialsBuilder givenCredentials() {
|
||||
return new CredentialsBuilder();
|
||||
private ProfileBuilder givenProfile() {
|
||||
return new ProfileBuilder();
|
||||
}
|
||||
|
||||
private class RelationBuilder {
|
||||
@@ -349,11 +349,11 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
}
|
||||
}
|
||||
|
||||
private class CredentialsBuilder {
|
||||
private class ProfileBuilder {
|
||||
private RbacSubjectEntity subject;
|
||||
private HsOfficePersonRealEntity person;
|
||||
|
||||
public CredentialsBuilder forSubject(String subjectName) {
|
||||
public ProfileBuilder forSubject(String subjectName) {
|
||||
this.subject = RbacSubjectEntity.builder()
|
||||
.name(subjectName)
|
||||
.build();
|
||||
@@ -362,24 +362,24 @@ class HsCredentialsRepositoryIntegrationTest extends ContextBasedTestWithCleanup
|
||||
return this;
|
||||
}
|
||||
|
||||
public CredentialsBuilder forPerson(HsOfficePersonRealEntity person) {
|
||||
public ProfileBuilder forPerson(HsOfficePersonRealEntity person) {
|
||||
this.person = person;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HsCredentialsEntity withEMailAddress(String emailAddress) {
|
||||
public HsProfileEntity withEMailAddress(String emailAddress) {
|
||||
|
||||
final var credentials = HsCredentialsEntity.builder()
|
||||
final var profile = HsProfileEntity.builder()
|
||||
.uuid(subject.getUuid())
|
||||
.subject(subject)
|
||||
.person(em.find(HsOfficePersonRealEntity.class, person.getUuid()))
|
||||
.emailAddress(emailAddress)
|
||||
.active(true)
|
||||
.build();
|
||||
em.persist(credentials);
|
||||
toCleanup(credentials);
|
||||
em.persist(profile);
|
||||
toCleanup(profile);
|
||||
em.flush();
|
||||
return credentials;
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-19
@@ -38,13 +38,13 @@ import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import jakarta.persistence.SynchronizationType;
|
||||
|
||||
@WebMvcTest(HsCredentialsContextsController.class)
|
||||
@WebMvcTest(HsProfileScopeController.class)
|
||||
@Import({ StrictMapper.class,
|
||||
MessageTranslator.class,
|
||||
JsonObjectMapperConfiguration.class,
|
||||
WebSecurityConfigForWebMvcTests.class })
|
||||
@ActiveProfiles({"fake-jwt", "test"})
|
||||
class HsCredentialsContextsControllerRestTest {
|
||||
class HsProfileScopeControllerRestTest {
|
||||
|
||||
@Autowired
|
||||
MockMvc mockMvc;
|
||||
@@ -63,7 +63,7 @@ class HsCredentialsContextsControllerRestTest {
|
||||
EntityManagerFactory emf;
|
||||
|
||||
@MockitoBean
|
||||
HsCredentialsContextRbacRepository loginContextRbacRepo;
|
||||
HsProfileScopeRbacRepository scopeRbacRepo;
|
||||
|
||||
@TestConfiguration
|
||||
public static class TestConfig {
|
||||
@@ -82,14 +82,14 @@ class HsCredentialsContextsControllerRestTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getListOfLoginContextsReturnsOkWithEmptyList() throws Exception {
|
||||
void getListOfScopesReturnsOkWithEmptyList() throws Exception {
|
||||
|
||||
// given
|
||||
givenNoContextsInTheRepository();
|
||||
givenNoScopesInTheRepository();
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/hs/accounts/contexts")
|
||||
.get("/api/hs/accounts/scopes")
|
||||
.header("Authorization", bearer("superuser-alex@hostsharing.net"))
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(print())
|
||||
@@ -101,15 +101,15 @@ class HsCredentialsContextsControllerRestTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getListOfLoginContextsReturnsAllContextsForGlobalAdmin() throws Exception {
|
||||
void getListOfScopesReturnsAllScopesForGlobalAdmin() throws Exception {
|
||||
|
||||
// given
|
||||
givenSomeContextsInTheRepository();
|
||||
givenSomeScopesInTheRepository();
|
||||
when(contextMock.isGlobalAdmin()).thenReturn(true);
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/hs/accounts/contexts")
|
||||
.get("/api/hs/accounts/scopes")
|
||||
.header("Authorization", bearer("Bearer superuser-alex@hostsharing.net"))
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(print())
|
||||
@@ -143,15 +143,15 @@ class HsCredentialsContextsControllerRestTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getListOfLoginContextsReturnsOnlyPublicContextsForNormalUser() throws Exception {
|
||||
void getListOfScopesReturnsOnlyPublicScopesForNormalUser() throws Exception {
|
||||
|
||||
// given
|
||||
givenSomeContextsInTheRepository();
|
||||
givenSomeScopesInTheRepository();
|
||||
when(contextMock.isGlobalAdmin()).thenReturn(false);
|
||||
|
||||
// when
|
||||
mockMvc.perform(MockMvcRequestBuilders
|
||||
.get("/api/hs/accounts/contexts")
|
||||
.get("/api/hs/accounts/scopes")
|
||||
.header("Authorization", bearer("drew@hostsharing.org"))
|
||||
.accept(MediaType.APPLICATION_JSON))
|
||||
.andDo(print())
|
||||
@@ -178,27 +178,27 @@ class HsCredentialsContextsControllerRestTest {
|
||||
)));
|
||||
}
|
||||
|
||||
private void givenNoContextsInTheRepository() {
|
||||
when(loginContextRbacRepo.findAll()).thenReturn(emptyList());
|
||||
private void givenNoScopesInTheRepository() {
|
||||
when(scopeRbacRepo.findAll()).thenReturn(emptyList());
|
||||
}
|
||||
|
||||
private void givenSomeContextsInTheRepository() {
|
||||
when(loginContextRbacRepo.findAll()).thenReturn(List.of(
|
||||
HsCredentialsContextRbacEntity.builder()
|
||||
private void givenSomeScopesInTheRepository() {
|
||||
when(scopeRbacRepo.findAll()).thenReturn(List.of(
|
||||
HsProfileScopeRbacEntity.builder()
|
||||
.uuid(UUID.randomUUID())
|
||||
.type("HSADMIN")
|
||||
.qualifier("prod")
|
||||
.publicAccess(true)
|
||||
.onlyForNaturalPersons(true)
|
||||
.build(),
|
||||
HsCredentialsContextRbacEntity.builder()
|
||||
HsProfileScopeRbacEntity.builder()
|
||||
.uuid(UUID.randomUUID())
|
||||
.type("SSH")
|
||||
.qualifier("public")
|
||||
.publicAccess(true)
|
||||
.onlyForNaturalPersons(false)
|
||||
.build(),
|
||||
HsCredentialsContextRbacEntity.builder()
|
||||
HsProfileScopeRbacEntity.builder()
|
||||
.uuid(UUID.randomUUID())
|
||||
.type("SSH")
|
||||
.qualifier("internal")
|
||||
+4
-4
@@ -6,11 +6,11 @@ import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class HsCredentialsContextRbacEntityUnitTest {
|
||||
class HsProfileScopeRbacEntityUnitTest {
|
||||
|
||||
@Test
|
||||
void toShortStringContainsJustTypeAndQualifier() {
|
||||
final var entity = HsCredentialsContextRbacEntity.builder()
|
||||
final var entity = HsProfileScopeRbacEntity.builder()
|
||||
.uuid(UUID.randomUUID())
|
||||
.type("SSH")
|
||||
.qualifier("prod")
|
||||
@@ -21,12 +21,12 @@ class HsCredentialsContextRbacEntityUnitTest {
|
||||
|
||||
@Test
|
||||
void toStringContainsAllNonNullFields() {
|
||||
final var entity = HsCredentialsContextRbacEntity.builder()
|
||||
final var entity = HsProfileScopeRbacEntity.builder()
|
||||
.uuid(UUID.randomUUID())
|
||||
.type("SSH")
|
||||
.qualifier("prod")
|
||||
.publicAccess(true)
|
||||
.build();
|
||||
assertEquals("loginContext(SSH:prod:PUBLIC)", entity.toString());
|
||||
assertEquals("scope(SSH:prod:PUBLIC)", entity.toString());
|
||||
}
|
||||
}
|
||||
+27
-27
@@ -25,11 +25,11 @@ import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
@Tag("generalIntegrationTest")
|
||||
@Import({ Context.class, JpaAttempt.class })
|
||||
@Transactional
|
||||
class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest {
|
||||
class HsProfileScopeRbacRepositoryIntegrationTest extends ContextBasedTest {
|
||||
|
||||
// existing UUIDs from test data (Liquibase changeset 310-login-credentials-test-data.sql)
|
||||
private static final UUID TEST_CONTEXT_HSADMIN_PROD_UUID = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final UUID TEST_CONTEXT_MATRIX_INTERNAL_UUID = UUID.fromString("33333333-3333-3333-3333-333333333333");
|
||||
// existing UUIDs from test data (Liquibase changeset 310-login-profile-test-data.sql)
|
||||
private static final UUID TEST_SCOPE_HSADMIN_PROD_UUID = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final UUID TEST_SCOPE_MATRIX_INTERNAL_UUID = UUID.fromString("33333333-3333-3333-3333-333333333333");
|
||||
|
||||
private static final String SUPERUSER_ALEX_SUBJECT_NAME = "superuser-alex@hostsharing.net";
|
||||
private static final String TEST_USER_SUBJECT_NAME = "selfregistered-test-user@hostsharing.org";
|
||||
@@ -38,21 +38,21 @@ class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest
|
||||
HttpServletRequest request;
|
||||
|
||||
@Autowired
|
||||
private HsCredentialsContextRbacRepository loginContextRepository;
|
||||
private HsProfileScopeRbacRepository scopesRepository;
|
||||
|
||||
@Test
|
||||
void shouldFindAllByNormalUserUsingTestData() {
|
||||
context(TEST_USER_SUBJECT_NAME);
|
||||
|
||||
// when
|
||||
final var allContexts = loginContextRepository.findAll();
|
||||
final var allScopes = scopesRepository.findAll();
|
||||
|
||||
// then
|
||||
assertThat(allContexts)
|
||||
assertThat(allScopes)
|
||||
.isNotNull()
|
||||
.hasSizeGreaterThanOrEqualTo(1) // Expect at least the 1 public context from assumed test data
|
||||
.extracting(HsCredentialsContext::getUuid)
|
||||
.contains(TEST_CONTEXT_HSADMIN_PROD_UUID);
|
||||
.extracting(HsProfileScope::getUuid)
|
||||
.contains(TEST_SCOPE_HSADMIN_PROD_UUID);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -60,12 +60,12 @@ class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest
|
||||
context(SUPERUSER_ALEX_SUBJECT_NAME);
|
||||
|
||||
// when
|
||||
final var allContexts = loginContextRepository.findAll();
|
||||
final var allScopes = scopesRepository.findAll();
|
||||
|
||||
// then
|
||||
assertThat(allContexts)
|
||||
assertThat(allScopes)
|
||||
.isNotNull()
|
||||
.hasSizeGreaterThanOrEqualTo(3); // Expect at least the 1 public context from assumed test data
|
||||
.hasSizeGreaterThanOrEqualTo(3); // Expect at least the 1 public scope from assumed test data
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,11 +73,11 @@ class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest
|
||||
context(TEST_USER_SUBJECT_NAME);
|
||||
|
||||
// when
|
||||
final var foundEntityOptional = loginContextRepository.findByUuid(TEST_CONTEXT_HSADMIN_PROD_UUID);
|
||||
final var foundEntityOptional = scopesRepository.findByUuid(TEST_SCOPE_HSADMIN_PROD_UUID);
|
||||
|
||||
// then
|
||||
assertThat(foundEntityOptional).isPresent();
|
||||
assertThat(foundEntityOptional).map(Object::toString).contains("loginContext(HSADMIN:prod:NP-ONLY:PUBLIC)");
|
||||
assertThat(foundEntityOptional).map(Object::toString).contains("scope(HSADMIN:prod:NP-ONLY:PUBLIC)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -85,11 +85,11 @@ class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest
|
||||
context(SUPERUSER_ALEX_SUBJECT_NAME);
|
||||
|
||||
// when
|
||||
final var foundEntityOptional = loginContextRepository.findByTypeAndQualifier("SSH", "internal");
|
||||
final var foundEntityOptional = scopesRepository.findByTypeAndQualifier("SSH", "internal");
|
||||
|
||||
// then
|
||||
assertThat(foundEntityOptional).isPresent();
|
||||
assertThat(foundEntityOptional).map(Object::toString).contains("loginContext(SSH:internal:NP-ONLY:INTERNAL)");
|
||||
assertThat(foundEntityOptional).map(Object::toString).contains("scope(SSH:internal:NP-ONLY:INTERNAL)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,7 +100,7 @@ class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest
|
||||
final var nonExistentQualifier = "non-existent-qualifier";
|
||||
|
||||
// when
|
||||
final var foundEntityOptional = loginContextRepository.findByTypeAndQualifier(
|
||||
final var foundEntityOptional = scopesRepository.findByTypeAndQualifier(
|
||||
"HSADMIN", nonExistentQualifier);
|
||||
|
||||
// then
|
||||
@@ -108,19 +108,19 @@ class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveNewLoginContext() {
|
||||
void shouldSaveNewScope() {
|
||||
context(SUPERUSER_ALEX_SUBJECT_NAME);
|
||||
|
||||
// given
|
||||
final var newQualifier = "test@example.social";
|
||||
final var newType = "MASTODON";
|
||||
final var newContext = HsCredentialsContextRbacEntity.builder()
|
||||
final var newScope = HsProfileScopeRbacEntity.builder()
|
||||
.type(newType)
|
||||
.qualifier(newQualifier)
|
||||
.build();
|
||||
|
||||
// when
|
||||
final var savedEntity = loginContextRepository.save(newContext);
|
||||
final var savedEntity = scopesRepository.save(newScope);
|
||||
em.flush();
|
||||
em.clear();
|
||||
|
||||
@@ -131,7 +131,7 @@ class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest
|
||||
|
||||
// Fetch again using the generated UUID to confirm persistence
|
||||
context(SUPERUSER_ALEX_SUBJECT_NAME); // Re-set context if needed after clear
|
||||
final var foundEntityOptional = loginContextRepository.findByUuid(generatedUuid);
|
||||
final var foundEntityOptional = scopesRepository.findByUuid(generatedUuid);
|
||||
assertThat(foundEntityOptional).isPresent();
|
||||
final var foundEntity = foundEntityOptional.get();
|
||||
assertThat(foundEntity.getUuid()).isEqualTo(generatedUuid);
|
||||
@@ -140,21 +140,21 @@ class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPreventUpdateOfExistingLoginContext() {
|
||||
void shouldPreventUpdateOfExistingScope() {
|
||||
context(SUPERUSER_ALEX_SUBJECT_NAME);
|
||||
|
||||
// given an existing entity from test data
|
||||
final var entityToUpdateOptional = loginContextRepository.findByUuid(TEST_CONTEXT_MATRIX_INTERNAL_UUID);
|
||||
final var entityToUpdateOptional = scopesRepository.findByUuid(TEST_SCOPE_MATRIX_INTERNAL_UUID);
|
||||
assertThat(entityToUpdateOptional)
|
||||
.withFailMessage("Could not find existing LoginContext with UUID %s. Ensure test data exists.",
|
||||
TEST_CONTEXT_MATRIX_INTERNAL_UUID)
|
||||
.withFailMessage("Could not find existing scope with UUID %s. Ensure test data exists.",
|
||||
TEST_SCOPE_MATRIX_INTERNAL_UUID)
|
||||
.isPresent();
|
||||
final var entityToUpdate = entityToUpdateOptional.get();
|
||||
|
||||
// when
|
||||
entityToUpdate.setQualifier("updated");
|
||||
final var exception = catchThrowable( () -> {
|
||||
loginContextRepository.save(entityToUpdate);
|
||||
scopesRepository.save(entityToUpdate);
|
||||
em.flush();
|
||||
});
|
||||
|
||||
@@ -162,6 +162,6 @@ class HsCredentialsContextRbacRepositoryIntegrationTest extends ContextBasedTest
|
||||
assertThat(exception)
|
||||
.isInstanceOf(PersistenceException.class)
|
||||
.hasCauseInstanceOf(PSQLException.class)
|
||||
.hasMessageContaining("ERROR: Updates to hs_accounts.context are not allowed.");
|
||||
.hasMessageContaining("ERROR: Updates to hs_accounts.scope are not allowed.");
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -6,11 +6,11 @@ import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class HsCredentialsContextRealEntityUnitTest {
|
||||
class HsProfileScopeRealEntityUnitTest {
|
||||
|
||||
@Test
|
||||
void toShortStringContainsJustTypeAndQualifier() {
|
||||
final var entity = HsCredentialsContextRealEntity.builder()
|
||||
final var entity = HsProfileScopeRealEntity.builder()
|
||||
.uuid(UUID.randomUUID())
|
||||
.type("SSH")
|
||||
.qualifier("prod")
|
||||
@@ -21,12 +21,12 @@ class HsCredentialsContextRealEntityUnitTest {
|
||||
|
||||
@Test
|
||||
void toStringContainsAllNonNullFields() {
|
||||
final var entity = HsCredentialsContextRealEntity.builder()
|
||||
final var entity = HsProfileScopeRealEntity.builder()
|
||||
.uuid(UUID.randomUUID())
|
||||
.type("SSH")
|
||||
.qualifier("prod")
|
||||
.publicAccess(true)
|
||||
.build();
|
||||
assertEquals("loginContext(SSH:prod:PUBLIC)", entity.toString());
|
||||
assertEquals("scope(SSH:prod:PUBLIC)", entity.toString());
|
||||
}
|
||||
}
|
||||
+28
-28
@@ -25,12 +25,12 @@ import static org.assertj.core.api.Assertions.catchThrowable;
|
||||
@ActiveProfiles("test")
|
||||
@Tag("generalIntegrationTest")
|
||||
@Import({ Context.class, JpaAttempt.class })
|
||||
class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest {
|
||||
class HsProfileScopeRealRepositoryIntegrationTest extends ContextBasedTest {
|
||||
|
||||
// existing UUIDs from test data (Liquibase changeset 310-login-credentials-test-data.sql)
|
||||
private static final UUID TEST_CONTEXT_HSADMIN_PROD_UUID = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final UUID TEST_CONTEXT_SSH_INTERNAL_UUID = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
private static final UUID TEST_CONTEXT_MATRIX_INTERNAL_UUID = UUID.fromString("33333333-3333-3333-3333-333333333333");
|
||||
// existing UUIDs from test data (Liquibase changeset 310-login-profile-test-data.sql)
|
||||
private static final UUID TEST_SCOPE_HSADMIN_PROD_UUID = UUID.fromString("11111111-1111-1111-1111-111111111111");
|
||||
private static final UUID TEST_SCOPE_SSH_INTERNAL_UUID = UUID.fromString("22222222-2222-2222-2222-222222222222");
|
||||
private static final UUID TEST_SCOPE_MATRIX_INTERNAL_UUID = UUID.fromString("33333333-3333-3333-3333-333333333333");
|
||||
|
||||
private static final String SUPERUSER_ALEX_SUBJECT_NAME = "superuser-alex@hostsharing.net";
|
||||
private static final String TEST_USER_SUBJECT_NAME = "selfregistered-test-user@hostsharing.org";
|
||||
@@ -39,12 +39,12 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
HttpServletRequest request;
|
||||
|
||||
@Autowired
|
||||
private HsCredentialsContextRealRepository loginContextRepository;
|
||||
private HsProfileScopeRealRepository scopeRepository;
|
||||
|
||||
@Test
|
||||
public void historizationIsAvailable() {
|
||||
// given
|
||||
final String nativeQuerySql = "select * from hs_accounts.context_hv";
|
||||
final String nativeQuerySql = "select * from hs_accounts.scope_hv";
|
||||
|
||||
// when
|
||||
historicalContext(Timestamp.from(ZonedDateTime.now().minusDays(1).toInstant()));
|
||||
@@ -53,7 +53,7 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
|
||||
// then
|
||||
assertThat(rowsBefore)
|
||||
.as("hs_accounts.context_hv only contain no rows for a timestamp before test data creation")
|
||||
.as("hs_accounts.scope_hv only contain no rows for a timestamp before test data creation")
|
||||
.hasSize(0);
|
||||
|
||||
// and when
|
||||
@@ -63,7 +63,7 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
|
||||
// then
|
||||
assertThat(rowsAfter)
|
||||
.as("hs_accounts.context_hv should now contain the test-data rows for the current timestamp")
|
||||
.as("hs_accounts.scope_hv should now contain the test-data rows for the current timestamp")
|
||||
.hasSize(7);
|
||||
}
|
||||
|
||||
@@ -72,14 +72,14 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
context(TEST_USER_SUBJECT_NAME);
|
||||
|
||||
// when
|
||||
final var allContexts = loginContextRepository.findAll();
|
||||
final var allScopes = scopeRepository.findAll();
|
||||
|
||||
// then
|
||||
assertThat(allContexts)
|
||||
assertThat(allScopes)
|
||||
.isNotNull()
|
||||
.hasSizeGreaterThanOrEqualTo(3) // Expect at least the 3 from assumed test data
|
||||
.extracting(HsCredentialsContext::getUuid)
|
||||
.contains(TEST_CONTEXT_HSADMIN_PROD_UUID, TEST_CONTEXT_SSH_INTERNAL_UUID, TEST_CONTEXT_MATRIX_INTERNAL_UUID);
|
||||
.extracting(HsProfileScope::getUuid)
|
||||
.contains(TEST_SCOPE_HSADMIN_PROD_UUID, TEST_SCOPE_SSH_INTERNAL_UUID, TEST_SCOPE_MATRIX_INTERNAL_UUID);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -87,11 +87,11 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
context(TEST_USER_SUBJECT_NAME);
|
||||
|
||||
// when
|
||||
final var foundEntityOptional = loginContextRepository.findByUuid(TEST_CONTEXT_HSADMIN_PROD_UUID);
|
||||
final var foundEntityOptional = scopeRepository.findByUuid(TEST_SCOPE_HSADMIN_PROD_UUID);
|
||||
|
||||
// then
|
||||
assertThat(foundEntityOptional).isPresent();
|
||||
assertThat(foundEntityOptional).map(Object::toString).contains("loginContext(HSADMIN:prod:NP-ONLY:PUBLIC)");
|
||||
assertThat(foundEntityOptional).map(Object::toString).contains("scope(HSADMIN:prod:NP-ONLY:PUBLIC)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,11 +99,11 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
context(TEST_USER_SUBJECT_NAME);
|
||||
|
||||
// when
|
||||
final var foundEntityOptional = loginContextRepository.findByTypeAndQualifier("SSH", "internal");
|
||||
final var foundEntityOptional = scopeRepository.findByTypeAndQualifier("SSH", "internal");
|
||||
|
||||
// then
|
||||
assertThat(foundEntityOptional).isPresent();
|
||||
assertThat(foundEntityOptional).map(Object::toString).contains("loginContext(SSH:internal:NP-ONLY:INTERNAL)");
|
||||
assertThat(foundEntityOptional).map(Object::toString).contains("scope(SSH:internal:NP-ONLY:INTERNAL)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -114,7 +114,7 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
final var nonExistentQualifier = "non-existent-qualifier";
|
||||
|
||||
// when
|
||||
final var foundEntityOptional = loginContextRepository.findByTypeAndQualifier(
|
||||
final var foundEntityOptional = scopeRepository.findByTypeAndQualifier(
|
||||
"HSADMIN", nonExistentQualifier);
|
||||
|
||||
// then
|
||||
@@ -122,19 +122,19 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSaveNewLoginContext() {
|
||||
void shouldSaveNewScope() {
|
||||
context(SUPERUSER_ALEX_SUBJECT_NAME);
|
||||
|
||||
// given
|
||||
final var newQualifier = "test@example.social";
|
||||
final var newType = "MASTODON";
|
||||
final var newContext = HsCredentialsContextRealEntity.builder()
|
||||
final var newScope = HsProfileScopeRealEntity.builder()
|
||||
.type(newType)
|
||||
.qualifier(newQualifier)
|
||||
.build();
|
||||
|
||||
// when
|
||||
final var savedEntity = loginContextRepository.save(newContext);
|
||||
final var savedEntity = scopeRepository.save(newScope);
|
||||
em.flush();
|
||||
em.clear();
|
||||
|
||||
@@ -145,7 +145,7 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
|
||||
// Fetch again using the generated UUID to confirm persistence
|
||||
context(TEST_USER_SUBJECT_NAME); // Re-set context if needed after clear
|
||||
final var foundEntityOptional = loginContextRepository.findByUuid(generatedUuid);
|
||||
final var foundEntityOptional = scopeRepository.findByUuid(generatedUuid);
|
||||
assertThat(foundEntityOptional).isPresent();
|
||||
final var foundEntity = foundEntityOptional.get();
|
||||
assertThat(foundEntity.getUuid()).isEqualTo(generatedUuid);
|
||||
@@ -154,21 +154,21 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPreventUpdateOfExistingLoginContext() {
|
||||
void shouldPreventUpdateOfExistingScope() {
|
||||
context(TEST_USER_SUBJECT_NAME);
|
||||
|
||||
// given an existing entity from test data
|
||||
final var entityToUpdateOptional = loginContextRepository.findByUuid(TEST_CONTEXT_MATRIX_INTERNAL_UUID);
|
||||
final var entityToUpdateOptional = scopeRepository.findByUuid(TEST_SCOPE_MATRIX_INTERNAL_UUID);
|
||||
assertThat(entityToUpdateOptional)
|
||||
.withFailMessage("Could not find existing LoginContext with UUID %s. Ensure test data exists.",
|
||||
TEST_CONTEXT_MATRIX_INTERNAL_UUID)
|
||||
.withFailMessage("Could not find existing Scope with UUID %s. Ensure test data exists.",
|
||||
TEST_SCOPE_MATRIX_INTERNAL_UUID)
|
||||
.isPresent();
|
||||
final var entityToUpdate = entityToUpdateOptional.get();
|
||||
|
||||
// when
|
||||
entityToUpdate.setQualifier("updated");
|
||||
final var exception = catchThrowable( () -> {
|
||||
loginContextRepository.save(entityToUpdate);
|
||||
scopeRepository.save(entityToUpdate);
|
||||
em.flush();
|
||||
});
|
||||
|
||||
@@ -176,6 +176,6 @@ class HsCredentialsContextRealRepositoryIntegrationTest extends ContextBasedTest
|
||||
assertThat(exception)
|
||||
.isInstanceOf(PersistenceException.class)
|
||||
.hasCauseInstanceOf(PSQLException.class)
|
||||
.hasMessageContaining("ERROR: Updates to hs_accounts.context are not allowed.");
|
||||
.hasMessageContaining("ERROR: Updates to hs_accounts.scope are not allowed.");
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
package net.hostsharing.hsadminng.hs.accounts.scenarios;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.accounts.generated.api.v1.model.ContextResource;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
public abstract class BaseCredentialsUseCase<T extends UseCase<?>> extends UseCase<T> {
|
||||
|
||||
public BaseCredentialsUseCase(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
protected ContextResource[] fetchContextResourcesByDescriptorPairs(final String descriptPairsVarName) {
|
||||
final var requestedContexts = ScenarioTest.getTypedVariable("contexts", Pair[].class);
|
||||
final var existingContextsJson = withTitle("Fetch Available Account Contexts", () ->
|
||||
httpGet("/api/hs/accounts/contexts").expecting(OK).expecting(JSON)
|
||||
).getResponse().body();
|
||||
final var existingContexts = objectMapper.readValue(existingContextsJson, ContextResource[].class);
|
||||
return Arrays.stream(requestedContexts)
|
||||
.map(pair -> Arrays.stream(existingContexts)
|
||||
.filter(context -> context.getType().equals(pair.getLeft())
|
||||
&& context.getQualifier().equals(pair.getRight()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"No matching context found for type=" + pair.getLeft()
|
||||
+ " and qualifier=" + pair.getRight()))
|
||||
)
|
||||
.toArray(ContextResource[]::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package net.hostsharing.hsadminng.hs.accounts.scenarios;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.accounts.generated.api.v1.model.ScopeResource;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.ScenarioTest;
|
||||
import net.hostsharing.hsadminng.hs.scenarios.UseCase;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
public abstract class BaseProfileUseCase<T extends UseCase<?>> extends UseCase<T> {
|
||||
|
||||
public BaseProfileUseCase(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
protected ScopeResource[] fetchScopeResourcesByDescriptorPairs(final String descriptPairsVarName) {
|
||||
final var requestedScopes = ScenarioTest.getTypedVariable("scopes", Pair[].class);
|
||||
final var existingScopesJson = withTitle("Fetch Available Account Scopes", () ->
|
||||
httpGet("/api/hs/accounts/scopes").expecting(OK).expecting(JSON)
|
||||
).getResponse().body();
|
||||
final var existingScopes = objectMapper.readValue(existingScopesJson, ScopeResource[].class);
|
||||
return Arrays.stream(requestedScopes)
|
||||
.map(pair -> Arrays.stream(existingScopes)
|
||||
.filter(scope -> scope.getType().equals(pair.getLeft())
|
||||
&& scope.getQualifier().equals(pair.getRight()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException(
|
||||
"No matching scope found for type=" + pair.getLeft()
|
||||
+ " and qualifier=" + pair.getRight()))
|
||||
)
|
||||
.toArray(ScopeResource[]::new);
|
||||
}
|
||||
}
|
||||
+13
-15
@@ -8,12 +8,12 @@ import org.springframework.http.HttpStatus;
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
public class CreateCredentials extends BaseCredentialsUseCase<CreateCredentials> {
|
||||
public class CreateProfile extends BaseProfileUseCase<CreateProfile> {
|
||||
|
||||
public CreateCredentials(final ScenarioTest testSuite) {
|
||||
public CreateProfile(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
|
||||
introduction("A set of credentials contains the login data for an RBAC subject.");
|
||||
introduction("A set of profile contains the login data for an RBAC subject.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -26,12 +26,12 @@ public class CreateCredentials extends BaseCredentialsUseCase<CreateCredentials>
|
||||
"In real situations we have more precise measures to find the related person."
|
||||
);
|
||||
|
||||
given("resolvedContexts",
|
||||
fetchContextResourcesByDescriptorPairs("contexts")
|
||||
given("resolvedScopes",
|
||||
fetchScopeResourcesByDescriptorPairs("scopes")
|
||||
);
|
||||
|
||||
return obtain("newCredentials", () ->
|
||||
httpPost("/api/hs/accounts/credentials", usingJsonBody("""
|
||||
return obtain("newProfile", () ->
|
||||
httpPost("/api/hs/accounts/profiles", usingJsonBody("""
|
||||
{
|
||||
"person.uuid": ${Person: %{personGivenName} %{personFamilyName}},
|
||||
"nickname": ${nickname},
|
||||
@@ -40,10 +40,9 @@ public class CreateCredentials extends BaseCredentialsUseCase<CreateCredentials>
|
||||
"emailAddress": ${emailAddress},
|
||||
"phonePassword": ${phonePassword},
|
||||
"smsNumber": ${smsNumber},
|
||||
"onboardingToken": ${onboardingToken},
|
||||
"globalUid": %{globalUid},
|
||||
"globalGid": %{globalGid},
|
||||
"contexts": @{resolvedContexts}
|
||||
"scopes": @{resolvedScopes}
|
||||
}
|
||||
"""))
|
||||
.expecting(HttpStatus.CREATED).expecting(ContentType.JSON)
|
||||
@@ -51,16 +50,15 @@ public class CreateCredentials extends BaseCredentialsUseCase<CreateCredentials>
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verify(final UseCase<CreateCredentials>.HttpResponse response) {
|
||||
protected void verify(final UseCase<CreateProfile>.HttpResponse response) {
|
||||
verify(
|
||||
"Verify the New Credentials",
|
||||
() -> httpGet("/api/hs/accounts/credentials/%{newCredentials}")
|
||||
"Verify the new Profile",
|
||||
() -> httpGet("/api/hs/accounts/profiles/%{newProfile}")
|
||||
.expecting(OK).expecting(JSON),
|
||||
path("uuid").contains("%{newCredentials}"),
|
||||
path("uuid").contains("%{newProfile}"),
|
||||
path("nickname").contains("%{nickname}"),
|
||||
path("person.uuid").contains("%{Person: %{personGivenName} %{personFamilyName}}"),
|
||||
path("totpSecrets").contains("@{totpSecrets}"),
|
||||
path("onboardingToken").contains("%{onboardingToken}")
|
||||
path("totpSecrets").contains("@{totpSecrets}")
|
||||
);
|
||||
}
|
||||
}
|
||||
+14
-15
@@ -14,7 +14,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInfo;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
|
||||
class CredentialsScenarioTests extends ScenarioTest {
|
||||
class ProfileScenarioTests extends ScenarioTest {
|
||||
|
||||
@SneakyThrows
|
||||
@BeforeEach
|
||||
@@ -61,19 +61,19 @@ class CredentialsScenarioTests extends ScenarioTest {
|
||||
@Nested
|
||||
@Order(30)
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
class CredentialScenarios {
|
||||
class ProfileScenarios {
|
||||
|
||||
@Test
|
||||
@Order(1010)
|
||||
@Produces(explicitly = "Credentials@hsadmin: firby-susan", implicitly = { "Person: Susan Firby" })
|
||||
void shouldCreateInitialCredentialsForExistingNaturalPerson() {
|
||||
new CreateCredentials(scenarioTest)
|
||||
@Produces(explicitly = "Profile: firby-susan", implicitly = { "Person: Susan Firby" })
|
||||
void shouldCreateInitialProfileForExistingNaturalPerson() {
|
||||
new CreateProfile(scenarioTest)
|
||||
// to find a specific existing person
|
||||
.given("personFamilyName", "Firby")
|
||||
.given("personGivenName", "Susan")
|
||||
// a login name, to be stored in the new RBAC subject
|
||||
.given("nickname", "firby-susan")
|
||||
// initial credentials
|
||||
// initial profile
|
||||
.given("active", true)
|
||||
.given("totpSecrets", Array.of("initialSecret"))
|
||||
.given("emailAddress", "susan.firby@example.com")
|
||||
@@ -82,29 +82,28 @@ class CredentialsScenarioTests extends ScenarioTest {
|
||||
.given("globalUid", 21011)
|
||||
.given("globalGid", 21011)
|
||||
.given(
|
||||
"contexts", Array.of(
|
||||
"scopes", Array.of(
|
||||
Pair.of("HSADMIN", "prod")
|
||||
))
|
||||
.given("onboardingToken", "fake-unboarding-token")
|
||||
.doRun()
|
||||
.keep();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(1020)
|
||||
@Requires("Credentials@hsadmin: firby-susan")
|
||||
void shouldUpdateCredentials() {
|
||||
new UpdateCredentials(scenarioTest)
|
||||
// the credentials to update
|
||||
.given("credentialsUuid", "%{Credentials@hsadmin: firby-susan}")
|
||||
// updated credentials
|
||||
@Requires("Profile: firby-susan")
|
||||
void shouldUpdateProfile() {
|
||||
new UpdateProfile(scenarioTest)
|
||||
// the profile to update
|
||||
.given("profileUuid", "%{Profile: firby-susan}")
|
||||
// updated profile
|
||||
.given("active", false)
|
||||
.given("totpSecrets", Array.of("initialSecret", "additionalSecret"))
|
||||
.given("emailAddress", "susan.firby@example.org")
|
||||
.given("phonePassword", "securePass987")
|
||||
.given("smsNumber", "+49987654321")
|
||||
.given(
|
||||
"contexts", Array.of(
|
||||
"scopes", Array.of(
|
||||
Pair.of("HSADMIN", "prod"),
|
||||
Pair.of("SSH", "internal")
|
||||
))
|
||||
+12
-12
@@ -8,30 +8,30 @@ import org.springframework.http.HttpStatus;
|
||||
import static io.restassured.http.ContentType.JSON;
|
||||
import static org.springframework.http.HttpStatus.OK;
|
||||
|
||||
public class UpdateCredentials extends BaseCredentialsUseCase<UpdateCredentials> {
|
||||
public class UpdateProfile extends BaseProfileUseCase<UpdateProfile> {
|
||||
|
||||
public UpdateCredentials(final ScenarioTest testSuite) {
|
||||
public UpdateProfile(final ScenarioTest testSuite) {
|
||||
super(testSuite);
|
||||
|
||||
introduction("A set of credentials contains the login data for an RBAC subject.");
|
||||
introduction("A set of profile contains the login data for an RBAC subject.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpResponse run() {
|
||||
|
||||
given("resolvedContexts",
|
||||
fetchContextResourcesByDescriptorPairs("contexts")
|
||||
given("resolvedScopes",
|
||||
fetchScopeResourcesByDescriptorPairs("scopes")
|
||||
);
|
||||
|
||||
withTitle("Patch the Changes to the existing Credentials", () ->
|
||||
httpPatch("/api/hs/accounts/credentials/%{credentialsUuid}", usingJsonBody("""
|
||||
withTitle("Patch the Changes to the existing Profile", () ->
|
||||
httpPatch("/api/hs/accounts/profiles/%{profileUuid}", usingJsonBody("""
|
||||
{
|
||||
"active": %{active},
|
||||
"totpSecrets": @{totpSecrets},
|
||||
"emailAddress": ${emailAddress},
|
||||
"phonePassword": ${phonePassword},
|
||||
"smsNumber": ${smsNumber},
|
||||
"contexts": @{resolvedContexts}
|
||||
"scopes": @{resolvedScopes}
|
||||
}
|
||||
"""))
|
||||
.reportWithResponse().expecting(HttpStatus.OK).expecting(ContentType.JSON)
|
||||
@@ -43,12 +43,12 @@ public class UpdateCredentials extends BaseCredentialsUseCase<UpdateCredentials>
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void verify(final UseCase<UpdateCredentials>.HttpResponse response) {
|
||||
protected void verify(final UseCase<UpdateProfile>.HttpResponse response) {
|
||||
verify(
|
||||
"Verify the Patched Credentials",
|
||||
() -> httpGet("/api/hs/accounts/credentials/%{credentialsUuid}")
|
||||
"Verify the Patched Profile",
|
||||
() -> httpGet("/api/hs/accounts/profiles/%{profileUuid}")
|
||||
.expecting(OK).expecting(JSON),
|
||||
path("uuid").contains("%{newCredentials}"),
|
||||
path("uuid").contains("%{newProfile}"),
|
||||
path("nickname").contains("%{nickname}"),
|
||||
path("totpSecrets").contains("%{totpSecrets}")
|
||||
);
|
||||
+2
-3
@@ -7,7 +7,6 @@ import net.hostsharing.hsadminng.rbac.context.Context;
|
||||
import net.hostsharing.hsadminng.rbac.test.ContextBasedTestWithCleanup;
|
||||
import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.json.JSONException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
@@ -31,12 +30,12 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
@Transactional
|
||||
@Tag("officeIntegrationTest")
|
||||
@SpringBootTest(
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||
classes = HsadminNgApplication.class)
|
||||
@ActiveProfiles("fake-jwt")
|
||||
@Transactional
|
||||
class HsOfficeBankAccountControllerAcceptanceTest extends ContextBasedTestWithCleanup {
|
||||
|
||||
@LocalServerPort
|
||||
@@ -58,7 +57,7 @@ class HsOfficeBankAccountControllerAcceptanceTest extends ContextBasedTestWithCl
|
||||
class GetListOfBankAccounts {
|
||||
|
||||
@Test
|
||||
void globalAdmin_withoutAssumedRoles_canViewAllBankAccounts_ifNoCriteriaGiven() throws JSONException {
|
||||
void globalAdmin_withoutAssumedRoles_canViewAllBankAccounts_ifNoCriteriaGiven() {
|
||||
|
||||
RestAssured // @formatter:off
|
||||
.given()
|
||||
|
||||
Reference in New Issue
Block a user