diff --git a/Jenkinsfile b/Jenkinsfile index 75ff9a330f970e0ac309847e5206523235a2eceb..b11447de08cd0645e6f427af44fff71823e7e53d 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -56,10 +56,10 @@ pipeline { } configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { sh "mvn -s $MAVEN_SETTINGS versions:set -DnewVersion=${JAR_TAG} -DprocessAllModules=true" - + } } - } + } stage('Build Administration') { steps { @@ -72,7 +72,7 @@ pipeline { } } } - + stage('Deploy to Nexus'){ steps { script { @@ -112,7 +112,7 @@ pipeline { IMAGE_TAG = buildVersionName() FAILED_STAGE=env.STAGE_NAME } - + configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { withCredentials([usernamePassword(credentialsId: 'jenkins-nexus-login', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) { sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS -Dspring-boot.build-image.publish=true -DskipTests -Dmaven.wagon.http.retryHandler.count=3 $BUILD_PROFILE -Ddocker.publishRegistry.username=${USER} -Ddocker.publishRegistry.password=${PASSWORD} -Dbuild.number=$BUILD_NUMBER spring-boot:build-image -Dspring-boot.build-image.imageName=docker.ozg-sh.de/administration:${IMAGE_TAG}' @@ -120,7 +120,7 @@ pipeline { } } } - + stage('Test, build and deploy Helm Chart') { steps { script { @@ -191,7 +191,7 @@ pipeline { } } } - + post { always{ junit testResults: '**/target/surefire-reports/*.xml', skipPublishingChecks: true @@ -226,7 +226,7 @@ String getHelmRepository(){ } String validateBranchName(branchName) { - int maxLength = 30 + int maxLength = 20 if (branchName.length() > maxLength) { String originalBranchName = branchName branchName = branchName.substring(0, maxLength) @@ -291,7 +291,7 @@ String getRoom() { return "!oWZpUGTFsxkJIYNfYg:matrix.ozg-sh.de" } else { return "!iQPAvQIiRwRpNOszjw:matrix.ozg-sh.de" - } + } } Void configureGit() { diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml index 54775001fee442756defcbb005b29ebc6102c263..58ff6b8aeeff343f7af013e6c3ff8e22c31d326b 100644 --- a/src/main/helm/templates/deployment.yaml +++ b/src/main/helm/templates/deployment.yaml @@ -123,6 +123,10 @@ spec: - name: ozgcloud_feature_organisationsEinheiten value: {{ .Values.ozgcloud.feature.organisationsEinheiten | quote }} {{- end }} + {{- if eq ((.Values.ozgcloud).feature).organisationsEinheiten "true" }} + - name: ozgcloud_organisationeinheit_zufisearchuri + value: {{ required "ozgcloud.organisationEinheit.zufiSearchUri must be set if feature organisationsEinheiten is activated" ((.Values.ozgcloud).organisationEinheit).zufiSearchUri}} + {{- end }} envFrom: {{- if (.Values.database).useExternal }} - secretRef: diff --git a/src/main/helm/values.yaml b/src/main/helm/values.yaml index 6d9f58a3cdce518e86f0bca90594972ee87f2fd9..0fc8fa9bd51f4ff7d8cef1a9bc028fd0e948948b 100644 --- a/src/main/helm/values.yaml +++ b/src/main/helm/values.yaml @@ -34,7 +34,7 @@ ozgcloud: user: administrationApiUser zufiManager: - address: zufi-server.zufi:9090 + address: dns:///zufi-server.zufi:9090 image: repo: docker.ozg-sh.de @@ -42,7 +42,7 @@ image: tag: latest # [default: latest] database: - databaseName: "administration-database" - secretName: "ozg-mongodb-admin-administration-user" - tls: - secretName: "ozg-mongodb-tls-cert" + databaseName: "administration-database" + secretName: "ozg-mongodb-admin-administration-user" + tls: + secretName: "ozg-mongodb-tls-cert" diff --git a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationEinheitProperties.java b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationEinheitProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..552ed8074437fbead4c365d191588319177d3d61 --- /dev/null +++ b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationEinheitProperties.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.organisationseinheit; + +import jakarta.validation.constraints.NotBlank; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import de.ozgcloud.admin.common.FeatureToggleProperties; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Configuration +@ConfigurationProperties(prefix = OrganisationEinheitProperties.ORGANISATION_EINHEIT_PROPERTIES_PREFIX) +@ConditionalOnProperty(prefix = FeatureToggleProperties.FEATURE_TOGGLE_PREFIX, name = "organisations-einheiten", havingValue = "true") +class OrganisationEinheitProperties { + + static final String ORGANISATION_EINHEIT_PROPERTIES_PREFIX = "ozgcloud.organisation-einheit"; + + /* + * Uri pointing to the endpoint in Alfa that searches for Organisationseinheiten + * in Zufi + */ + @NotBlank + private String zufiSearchUri; +} diff --git a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessor.java b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessor.java index 1cb98fd649ac08564bb8f9da24e5419c0e930016..8e613dda4bb29b3382e1ed2ecf10fa245b64f79a 100644 --- a/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessor.java +++ b/src/main/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessor.java @@ -25,25 +25,42 @@ package de.ozgcloud.admin.organisationseinheit; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; +import java.util.List; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; import org.springframework.hateoas.server.RepresentationModelProcessor; import org.springframework.stereotype.Component; import de.ozgcloud.admin.Root; import de.ozgcloud.admin.common.FeatureToggleProperties; +import de.ozgcloud.admin.common.user.CurrentUserService; +import de.ozgcloud.admin.common.user.UserRole; import lombok.RequiredArgsConstructor; @Component @RequiredArgsConstructor +@ConditionalOnProperty(prefix = FeatureToggleProperties.FEATURE_TOGGLE_PREFIX, name = "organisations-einheiten", havingValue = "true") class OrganisationsEinheitRootProcessor implements RepresentationModelProcessor<EntityModel<Root>> { - static final String REL_ORGANISATIONS_EINHEITEN = "organisationsEinheiten"; + static final String REL_ORGANISATION_EINHEITEN = "organisationsEinheiten"; + static final String REL_SEARCH_ORGANISATION_EINHEIT = "searchOrganisationEinheit"; - private final FeatureToggleProperties featureToggleProperties; + private final OrganisationEinheitProperties organisationsEinheitProperties; + private final CurrentUserService currentUserService; @Override public EntityModel<Root> process(EntityModel<Root> model) { - return model.addIf(featureToggleProperties.isOrganisationsEinheiten(), - () -> linkTo(methodOn(OrganisationsEinheitController.class).getAll()).withRel(REL_ORGANISATIONS_EINHEITEN)); + return model.addAllIf(currentUserService.hasRole(UserRole.ADMIN_ADMIN), + () -> List.of(buildGetAllOrganisationEinheitenLink(), buildSearchOrganisationEinheitLink())); + } + + private Link buildGetAllOrganisationEinheitenLink() { + return linkTo(methodOn(OrganisationsEinheitController.class).getAll()).withRel(REL_ORGANISATION_EINHEITEN); + } + + private Link buildSearchOrganisationEinheitLink() { + return Link.of(organisationsEinheitProperties.getZufiSearchUri(), REL_SEARCH_ORGANISATION_EINHEIT); } } diff --git a/src/test/helm/deployment_features_test.yaml b/src/test/helm/deployment_features_test.yaml index 9e02c58d077d05b1f50bc15e8a701a36e6c4ac6f..d1d562160ef83483e3d37c7f939865b7baac18e2 100644 --- a/src/test/helm/deployment_features_test.yaml +++ b/src/test/helm/deployment_features_test.yaml @@ -60,6 +60,8 @@ tests: postfach: "true" benutzerRollen: "true" organisationsEinheiten: "true" + organisationEinheit: + zufiSearchUri: alfa/zufi/search/endpoint?searchBy={searchBy} asserts: - contains: path: spec.template.spec.containers[0].env @@ -69,7 +71,7 @@ tests: - contains: path: spec.template.spec.containers[0].env content: - name: ozgcloud_feature_benutzerRollen + name: ozgcloud_feature_benutzerRollen value: "true" - contains: path: spec.template.spec.containers[0].env @@ -98,4 +100,4 @@ tests: path: spec.template.spec.containers[0].env content: name: ozgcloud_feature_organisationsEinheiten - value: "false" \ No newline at end of file + value: "false" diff --git a/src/test/helm/deployment_zufi_test.yaml b/src/test/helm/deployment_zufi_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e342ff4be91b397f84da64cf8606fc8d99f6e082 --- /dev/null +++ b/src/test/helm/deployment_zufi_test.yaml @@ -0,0 +1,83 @@ +# +# Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: test zufi search property +release: + name: administration + namespace: sh-helm-test +templates: + - templates/deployment.yaml +set: + ozgcloud: + bundesland: sh + bezeichner: helm + sso: + serverUrl: https://sso.company.local + imagePullSecret: image-pull-secret +tests: + - it: should not contain zufi search uri if organisationseinheit feature toggle not set + set: + ozgcloud: + feature: + organisationsEinheiten: "false" + organisationEinheit: + zufiSearchUri: alfa/zufi/search/endpoint?searchBy={searchBy} + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_organisationeinheit_zufisearchuri + any: true + - it: should not contain zufi search uri if organisationseinheit feature toggle is disabled + set: + ozgcloud: + organisationEinheit: + zufiSearchUri: alfa/zufi/search/endpoint?searchBy={searchBy} + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_organisationeinheit_zufisearchuri + any: true + - it: should set zufi search uri + set: + ozgcloud: + feature: + organisationsEinheiten: "true" + organisationEinheit: + zufiSearchUri: alfa/zufi/search/endpoint?searchBy={searchBy} + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_organisationeinheit_zufisearchuri + value: alfa/zufi/search/endpoint?searchBy={searchBy} + - it: should fail if zufi search uri is not set + set: + ozgcloud: + feature: + organisationsEinheiten: "true" + asserts: + - failedTemplate: + errorMessage: "ozgcloud.organisationEinheit.zufiSearchUri must be set if feature organisationsEinheiten is activated" diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorITCase.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..c1cbf97a6669f18cd7f6f22b8bd94a4b126e69bb --- /dev/null +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorITCase.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.admin.organisationseinheit; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; + +import de.ozgcloud.common.test.ITCase; + +class OrganisationsEinheitRootProcessorITCase { + + @Nested + @SpringBootTest(properties = { + "ozgcloud.feature.organisations-einheiten=true", + "ozgcloud.organisation-einheit.zufi-search-url=foo" + }) + + @ITCase + class TestFeatureEnabled { + + @Autowired + private ApplicationContext applicationContext; + + @Test + void shouldHaveOrganisationsEinheitRootProcessorBean() { + assertDoesNotThrow(() -> applicationContext.getBean(OrganisationsEinheitRootProcessor.class)); + } + } + + @Nested + @SpringBootTest(properties = { "ozgcloud.feature.organisations-einheiten=false", "ozgcloud.organisation-einheit.zufi-search-url=foo" }) + @ITCase + class TestFeatureDisabled { + + @Autowired + private ApplicationContext applicationContext; + + @Test + void shouldHaveOrganisationsEinheitRootProcessorBean() { + assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean(OrganisationsEinheitRootProcessor.class)); + } + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorTest.java b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorTest.java index f147d68e37d851e374f7376a2e84c34374c1f271..47323d43651ecfe9be484f7b6c625e8fa68292a2 100644 --- a/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorTest.java +++ b/src/test/java/de/ozgcloud/admin/organisationseinheit/OrganisationsEinheitRootProcessorTest.java @@ -24,8 +24,10 @@ package de.ozgcloud.admin.organisationseinheit; import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; @@ -33,61 +35,122 @@ import org.mockito.Mock; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.admin.Root; import de.ozgcloud.admin.RootTestFactory; -import de.ozgcloud.admin.common.FeatureToggleProperties; +import de.ozgcloud.admin.common.user.CurrentUserService; +import de.ozgcloud.admin.common.user.UserRole; class OrganisationsEinheitRootProcessorTest { @InjectMocks private OrganisationsEinheitRootProcessor organisationsEinheitRootProcessor; @Mock - private FeatureToggleProperties featureToggleProperties; + private OrganisationEinheitProperties organisationsEinheitProperties; + @Mock + private CurrentUserService currentUserService; @Nested class TestProcess { - @Nested - class OrganisationsEinheitenLinkRelation { + private final String zufiSearchUri = LoremIpsum.getInstance().getUrl() + "?searchBy={searchBy}"; - @Test - void shouldExistsIfFeatureEnabled() { - givenFeatureIsEnabled(); + @Test + void shouldCheckUserRole() { + processModel(); - var model = organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create())); + verify(currentUserService).hasRole(UserRole.ADMIN_ADMIN); + } + + @Nested + class TestOnWrongUserRole { - assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATIONS_EINHEITEN)).isNotEmpty(); + @BeforeEach + void givenHasWrongRole() { + when(currentUserService.hasRole(anyString())).thenReturn(false); } @Test - void shouldNotExistIfFeatureDisabled() { - givenFeatureIsDisabled(); + void shouldNotAddAnyLinks() { + var model = processModel(); - var model = organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create())); + assertThat(model.getLinks()).isEmpty(); + } + } + + @Nested + class TestOnAdminRole { - assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATIONS_EINHEITEN)).isEmpty(); + @BeforeEach + void givenHasAdminRole() { + when(currentUserService.hasRole(anyString())).thenReturn(true); + when(organisationsEinheitProperties.getZufiSearchUri()).thenReturn(zufiSearchUri); } - @Test - void shouldHaveHref() { - givenFeatureIsEnabled(); + @Nested + class TestOrganisationsEinheitenLinkRelation { - var model = organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create())); + @Test + void shouldExist() { + var model = processModel(); - assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATIONS_EINHEITEN)) - .get() - .extracting(Link::getHref) - .isEqualTo(OrganisationsEinheitController.PATH); - } + assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATION_EINHEITEN)).isNotEmpty(); + } - private void givenFeatureIsEnabled() { - when(featureToggleProperties.isOrganisationsEinheiten()).thenReturn(true); + @Test + void shouldHaveHref() { + var model = processModel(); + + assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_ORGANISATION_EINHEITEN)) + .get() + .extracting(Link::getHref) + .isEqualTo(OrganisationsEinheitController.PATH); + } } - private void givenFeatureIsDisabled() { - when(featureToggleProperties.isOrganisationsEinheiten()).thenReturn(false); + @Nested + class TestSearchOrganisationsEinheitenLink { + + @Test + void shouldExistsIfFeatureEnabled() { + var model = processModel(); + + assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_SEARCH_ORGANISATION_EINHEIT)).isNotEmpty(); + } + + @Test + void shouldGetZufiSearchUri() { + processModel(); + + verify(organisationsEinheitProperties).getZufiSearchUri(); + + } + + @Test + void shouldHaveHref() { + var model = processModel(); + + assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_SEARCH_ORGANISATION_EINHEIT)) + .get() + .extracting(Link::getHref) + .isEqualTo(zufiSearchUri); + } + + @Test + void shouldBeTemplated() { + var model = processModel(); + + assertThat(model.getLink(OrganisationsEinheitRootProcessor.REL_SEARCH_ORGANISATION_EINHEIT)) + .get() + .extracting(Link::isTemplated) + .isEqualTo(true); + } } } + private EntityModel<Root> processModel() { + return organisationsEinheitRootProcessor.process(EntityModel.of(RootTestFactory.create())); + } } - } \ No newline at end of file