diff --git a/.tc-environment b/.tc-environment index 665a2123..063792cd 100644 --- a/.tc-environment +++ b/.tc-environment @@ -5,5 +5,9 @@ export HSADMINNG_POSTGRES_ADMIN_USERNAME=admin export HSADMINNG_SUPERUSER=import-superuser@hostsharing.net export HSADMINNG_OFFICE_DATA_SQL_FILE export HSADMINNG_JWT_TOKEN_URL=http://localhost:8080/fake-jwt/token +export HSADMINNG_JWT_CLIENT_ID=hsscript.ng +export HSADMINNG_JWT_CLIENT_SECRET= +export HSADMINNG_JWT_USERNAME=superuser-alex@hostsharing.net +export HSADMINNG_JWT_PASSWORD=password export LANG=en_US.UTF-8 diff --git a/.unset-environment b/.unset-environment index 4fc29b63..aec5ceb3 100644 --- a/.unset-environment +++ b/.unset-environment @@ -8,7 +8,8 @@ unset HSADMINNG_OFFICE_DATA_SQL_FILE unset HSADMINNG_JWT_ISSUER unset HSADMINNG_JWT_JWKS_URL +unset HSADMINNG_JWT_TOKEN_URL +unset HSADMINNG_JWT_CLIENT_ID +unset HSADMINNG_JWT_CLIENT_SECRET unset HSADMINNG_JWT_USERNAME unset HSADMINNG_JWT_PASSWORD -unset HSADMINNG_JWT_TOKEN_URL - diff --git a/bin/jwt-curl b/bin/jwt-curl index 3caf5993..86f268a0 100755 --- a/bin/jwt-curl +++ b/bin/jwt-curl @@ -102,12 +102,12 @@ function jwtLogin() { # OAuth2 Resource Owner Password Credentials Grant (public client) trace "+ curl --fail-with-body --show-error -X POST \ -H 'Content-Type: application/x-www-form-urlencoded' \ - -d \"grant_type=password&username=$HSADMINNG_JWT_USERNAME&password=$HSADMINNG_JWT_PASSWORD_DISPLAY\" \ + -d \"grant_type=password&client_id=$HSADMINNG_JWT_CLIENT_ID&client_secret=$HSADMINNG_JWT_CLIENT_SECRET&username=$HSADMINNG_JWT_USERNAME&password=$HSADMINNG_JWT_PASSWORD_DISPLAY\" \ $HSADMINNG_JWT_TOKEN_URL -o ~/.jwt-token.response" JWT_RESPONSE=$(curl --fail-with-body --show-error -X POST \ -H 'Content-Type: application/x-www-form-urlencoded' \ - -d "grant_type=password&username=$HSADMINNG_JWT_USERNAME&password=$HSADMINNG_JWT_PASSWORD" \ + -d "grant_type=password&client_id=$HSADMINNG_JWT_CLIENT_ID&client_secret=$HSADMINNG_JWT_CLIENT_SECRET&username=$HSADMINNG_JWT_USERNAME&password=$HSADMINNG_JWT_PASSWORD_DISPLAY" \ $HSADMINNG_JWT_TOKEN_URL 2>&1 | tee ~/.jwt-token.response) # Extract access token from JSON response @@ -198,6 +198,8 @@ case "${1,,}" in "env") ## prints all related HSADMINNG_JWT_... environment variables; use '--show-password' to show the password as well # example: jwt-curl -show-password env echo "export HSADMINNG_JWT_TOKEN_URL=$HSADMINNG_JWT_TOKEN_URL" + echo "export HSADMINNG_JWT_CLIENT_ID=$HSADMINNG_JWT_CLIENT_ID" + echo "export HSADMINNG_JWT_CLIENT_SECRET=$HSADMINNG_JWT_CLIENT_SECRET" echo "export HSADMINNG_JWT_USERNAME=$HSADMINNG_JWT_USERNAME" if [ "$HSADMINNG_JWT_SHOW_PASSWORD" == "yes" ]; then echo "export HSADMINNG_JWT_PASSWORD=$HSADMINNG_JWT_PASSWORD" diff --git a/doc/ideas/login-credentials-data-model.mermaid b/doc/ideas/login-credentials-data-model.mermaid index 9bcf775a..0d7e0f43 100644 --- a/doc/ideas/login-credentials-data-model.mermaid +++ b/doc/ideas/login-credentials-data-model.mermaid @@ -8,14 +8,11 @@ classDiagram Credentials "1..n" --o "1" CredentialsContextMapping class Credentials{ - +totpSecret: text - +phonePassword: text +emailAdress: text +smsNumber: text -active: bool [r/w] -globalUid: int [w/o] -globalGid: int [w/o] - -onboardingToken: text [w/o] } class CredentialsContext{ diff --git a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsController.java b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsController.java index 15687dd0..d834a91e 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsController.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsController.java @@ -1,7 +1,5 @@ package net.hostsharing.hsadminng.hs.accounts; -import java.time.LocalDateTime; -import java.time.ZoneOffset; import java.util.List; import java.util.UUID; import java.util.function.BiConsumer; @@ -37,7 +35,6 @@ import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBui import jakarta.persistence.EntityNotFoundException; import jakarta.validation.ValidationException; -import static java.util.Optional.ofNullable; import static java.util.Optional.of; @RestController @@ -191,22 +188,6 @@ public class HsCredentialsController implements CredentialsApi { return ResponseEntity.ok(result); } - @Override - @Transactional - @Timed("app.credentials.credentials.credentialsUsed") - public ResponseEntity credentialsUsed(final UUID credentialsUuid) { - context.define(); - - val current = credentialsRepo.findByUuid(credentialsUuid).orElseThrow(); - - current.setOnboardingToken(null); - current.setLastUsed(LocalDateTime.now()); - - val saved = credentialsRepo.save(current); - val mapped = mapper.map(saved, CredentialsResource.class, ENTITY_TO_RESOURCE_POSTMAPPER); - return ResponseEntity.ok(mapped); - } - private void validateOnCreate(final HsCredentialsEntity newCredentialsEntity) { validateReferencedPersonToBeRepresentedByLoginUserPerson(newCredentialsEntity); validateNormalUsersOnlyAccessPublicContexts(newCredentialsEntity); @@ -324,8 +305,6 @@ public class HsCredentialsController implements CredentialsApi { } final BiConsumer ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> { - ofNullable(entity.getLastUsed()).ifPresent( - dt -> resource.setLastUsed(dt.atOffset(ZoneOffset.UTC))); of(entity.getSubject()).ifPresent( subject -> resource.setNickname(subject.getName()) ); diff --git a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntity.java b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntity.java index aa7411fb..64ba669b 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntity.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntity.java @@ -9,9 +9,7 @@ import net.hostsharing.hsadminng.repr.Stringify; import net.hostsharing.hsadminng.repr.Stringifyable; // import net.hostsharing.hsadminng.rbac.RbacSubjectEntity; // Assuming RbacSubjectEntity exists for the FK relationship -import java.time.LocalDateTime; import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.UUID; @@ -31,8 +29,6 @@ public class HsCredentialsEntity implements BaseEntity, Str protected static Stringify stringify = stringify(HsCredentialsEntity.class, "credentials") .withProp(HsCredentialsEntity::isActive) .withProp(HsCredentialsEntity::getEmailAddress) - .withProp(HsCredentialsEntity::getTotpSecrets) - .withProp(HsCredentialsEntity::getPhonePassword) .withProp(HsCredentialsEntity::getSmsNumber) .quotedValues(false); @@ -51,9 +47,6 @@ public class HsCredentialsEntity implements BaseEntity, Str @Version private int version; - @Column - private LocalDateTime lastUsed; - @Column private boolean active; @@ -63,15 +56,6 @@ public class HsCredentialsEntity implements BaseEntity, Str @Column private Integer globalGid; - @Column - private String onboardingToken; - - @Column - private List totpSecrets; - - @Column - private String phonePassword; - @Column private String emailAddress; diff --git a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntityPatcher.java b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntityPatcher.java index 3e424e47..af84bc26 100644 --- a/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntityPatcher.java +++ b/src/main/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntityPatcher.java @@ -4,7 +4,6 @@ import net.hostsharing.hsadminng.accounts.generated.api.v1.model.CredentialsPatc import net.hostsharing.hsadminng.mapper.EntityPatcher; import net.hostsharing.hsadminng.mapper.OptionalFromJson; -import java.util.Optional; public class HsCredentialsEntityPatcher implements EntityPatcher { @@ -23,12 +22,8 @@ public class HsCredentialsEntityPatcher implements EntityPatcher { - 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()))); - // @formatter:on - } - } - // Helper methods private HsOfficePersonRealEntity givenLegalPerson(final String executingSubjectName) { return jpaAttempt.transacted(() -> { diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntityPatcherUnitTest.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntityPatcherUnitTest.java index 2f1f69fd..b6d3550b 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntityPatcherUnitTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/HsCredentialsEntityPatcherUnitTest.java @@ -33,15 +33,11 @@ class HsCredentialsEntityPatcherUnitTest extends PatchUnitTestBase< private static final Boolean INITIAL_ACTIVE = true; private static final String INITIAL_EMAIL_ADDRESS = "initial@example.com"; - private static final List 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 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(); @@ -102,9 +98,7 @@ class HsCredentialsEntityPatcherUnitTest extends PatchUnitTestBase< 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; @@ -137,25 +131,12 @@ class HsCredentialsEntityPatcherUnitTest extends PatchUnitTestBase< 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, diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateCredentials.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateCredentials.java index 0e7191c8..a9b4e9e7 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateCredentials.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CreateCredentials.java @@ -36,11 +36,8 @@ public class CreateCredentials extends BaseCredentialsUseCase "person.uuid": ${Person: %{personGivenName} %{personFamilyName}}, "nickname": ${nickname}, "active": %{active}, - "totpSecrets": @{totpSecrets}, "emailAddress": ${emailAddress}, - "phonePassword": ${phonePassword}, "smsNumber": ${smsNumber}, - "onboardingToken": ${onboardingToken}, "globalUid": %{globalUid}, "globalGid": %{globalGid}, "contexts": @{resolvedContexts} @@ -58,9 +55,7 @@ public class CreateCredentials extends BaseCredentialsUseCase .expecting(OK).expecting(JSON), path("uuid").contains("%{newCredentials}"), path("nickname").contains("%{nickname}"), - path("person.uuid").contains("%{Person: %{personGivenName} %{personFamilyName}}"), - path("totpSecrets").contains("@{totpSecrets}"), - path("onboardingToken").contains("%{onboardingToken}") + path("person.uuid").contains("%{Person: %{personGivenName} %{personFamilyName}}") ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CredentialsScenarioTests.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CredentialsScenarioTests.java index 5cfd7665..65cfe8f9 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CredentialsScenarioTests.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/CredentialsScenarioTests.java @@ -75,9 +75,7 @@ class CredentialsScenarioTests extends ScenarioTest { .given("nickname", "firby-susan") // initial credentials .given("active", true) - .given("totpSecrets", Array.of("initialSecret")) .given("emailAddress", "susan.firby@example.com") - .given("phonePassword", "securePass123") .given("smsNumber", "+49123456789") .given("globalUid", 21011) .given("globalGid", 21011) @@ -85,7 +83,6 @@ class CredentialsScenarioTests extends ScenarioTest { "contexts", Array.of( Pair.of("HSADMIN", "prod") )) - .given("onboardingToken", "fake-unboarding-token") .doRun() .keep(); } @@ -99,9 +96,7 @@ class CredentialsScenarioTests extends ScenarioTest { .given("credentialsUuid", "%{Credentials@hsadmin: firby-susan}") // updated credentials .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( diff --git a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/UpdateCredentials.java b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/UpdateCredentials.java index 4714bc1e..d665e6c8 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/UpdateCredentials.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/accounts/scenarios/UpdateCredentials.java @@ -27,16 +27,12 @@ public class UpdateCredentials extends BaseCredentialsUseCase httpPatch("/api/hs/accounts/credentials/%{credentialsUuid}", usingJsonBody(""" { "active": %{active}, - "totpSecrets": @{totpSecrets}, - "emailAddress": ${emailAddress}, - "phonePassword": ${phonePassword}, "smsNumber": ${smsNumber}, "contexts": @{resolvedContexts} } """)) .reportWithResponse().expecting(HttpStatus.OK).expecting(ContentType.JSON) .extractValue("nickname", "nickname") - .extractValue("totpSecrets", "totpSecrets") ); return null; @@ -49,8 +45,7 @@ public class UpdateCredentials extends BaseCredentialsUseCase () -> httpGet("/api/hs/accounts/credentials/%{credentialsUuid}") .expecting(OK).expecting(JSON), path("uuid").contains("%{newCredentials}"), - path("nickname").contains("%{nickname}"), - path("totpSecrets").contains("%{totpSecrets}") + path("nickname").contains("%{nickname}") ); } } diff --git a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerAcceptanceTest.java b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerAcceptanceTest.java index ad3496c2..911603a3 100644 --- a/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerAcceptanceTest.java +++ b/src/test/java/net/hostsharing/hsadminng/hs/office/bankaccount/HsOfficeBankAccountControllerAcceptanceTest.java @@ -31,12 +31,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