feature/add-jenkinsfile #114

Merged
hsh-michaelhoennig merged 5 commits from feature/add-jenkinsfile into master 2024-10-14 10:36:58 +02:00
8 changed files with 181 additions and 12 deletions
Showing only changes of commit 2776d9935a - Show all commits

51
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,51 @@
pipeline {
agent {
dockerfile {
filename 'etc/jenkinsAgent.Dockerfile'
// additionalBuildArgs ...
args '--network=bridge --user root -v $PWD:$PWD -v /var/run/docker.sock:/var/run/docker.sock --group-add 984'
reuseNode true
}
}
environment {
DOCKER_HOST = 'unix:///var/run/docker.sock'
HSADMINNG_POSTGRES_ADMIN_USERNAME = 'admin'
HSADMINNG_POSTGRES_RESTRICTED_USERNAME = 'restricted'
HSADMINNG_MIGRATION_DATA_PATH = 'migration'
}
triggers {
pollSCM('H/1 * * * *')
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage ('Compile & Test') {
steps {
sh './gradlew clean check --no-daemon -x pitest -x dependencyCheckAnalyze'
}
}
}
post {
always {
// archive test results
junit 'build/test-results/test/*.xml'
// archive the JaCoCo coverage report in XML and HTML format
jacoco(execPattern: '**/jacoco.exec',
classPattern: 'build/classes/java/main',
sourcePattern: 'src/main/java',
exclusionPattern: '')
// cleanup workspace
cleanWs()
}
}
}

View File

@ -0,0 +1,10 @@
FROM eclipse-temurin:21-jdk
RUN apt-get update && \
apt-get install -y bind9-utils && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# RUN mkdir /opt/app
# COPY japp.jar /opt
# CMD ["java", "-jar", "/opt/app/japp.jar"]

View File

@ -37,7 +37,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
private HsOfficePersonRepository holderRepo; private HsOfficePersonRepository holderRepo;
@Autowired @Autowired
private HsOfficeContactRealRepository contactrealRepo; private HsOfficeContactRealRepository realContactRepo;
@PersistenceContext @PersistenceContext
private EntityManager em; private EntityManager em;
@ -47,12 +47,20 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
public ResponseEntity<List<HsOfficeRelationResource>> listRelations( public ResponseEntity<List<HsOfficeRelationResource>> listRelations(
final String currentSubject, final String currentSubject,
final String assumedRoles, final String assumedRoles,
final UUID personUuid, UUID personUuid,
final HsOfficeRelationTypeResource relationType) { HsOfficeRelationTypeResource relationType,
String personData,
String contactData) {
context.define(currentSubject, assumedRoles); context.define(currentSubject, assumedRoles);
final var entities = relationRbacRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid, final var entities =
relationType == null ? null : HsOfficeRelationType.valueOf(relationType.name())); ( personData == null && contactData == null )
? relationRbacRepo.findRelationRelatedToPersonUuidAndRelationType(personUuid,
relationType == null ? null : HsOfficeRelationType.valueOf(relationType.name()))
: relationRbacRepo.findRelationRelatedToPersonUuidRelationTypePersonAndContactData(
personUuid,
relationType == null ? null : HsOfficeRelationType.valueOf(relationType.name()),
forLike(personData), forLike(contactData));
final var resources = mapper.mapList(entities, HsOfficeRelationResource.class, final var resources = mapper.mapList(entities, HsOfficeRelationResource.class,
RELATION_ENTITY_TO_RESOURCE_POSTMAPPER); RELATION_ENTITY_TO_RESOURCE_POSTMAPPER);
@ -77,7 +85,7 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
entityToSave.setHolder(holderRepo.findByUuid(body.getHolderUuid()).orElseThrow( entityToSave.setHolder(holderRepo.findByUuid(body.getHolderUuid()).orElseThrow(
() -> new NoSuchElementException("cannot find Person by holderUuid: " + body.getHolderUuid()) () -> new NoSuchElementException("cannot find Person by holderUuid: " + body.getHolderUuid())
)); ));
entityToSave.setContact(contactrealRepo.findByUuid(body.getContactUuid()).orElseThrow( entityToSave.setContact(realContactRepo.findByUuid(body.getContactUuid()).orElseThrow(
() -> new NoSuchElementException("cannot find Contact by contactUuid: " + body.getContactUuid()) () -> new NoSuchElementException("cannot find Contact by contactUuid: " + body.getContactUuid())
)); ));
@ -144,6 +152,9 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
return ResponseEntity.ok(mapped); return ResponseEntity.ok(mapped);
} }
private String forLike(final String text) {
return text == null ? null : ("%" + text.toLowerCase() + "%");
}
final BiConsumer<HsOfficeRelationRbacEntity, HsOfficeRelationResource> RELATION_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> { final BiConsumer<HsOfficeRelationRbacEntity, HsOfficeRelationResource> RELATION_ENTITY_TO_RESOURCE_POSTMAPPER = (entity, resource) -> {
resource.setAnchor(mapper.map(entity.getAnchor(), HsOfficePersonResource.class)); resource.setAnchor(mapper.map(entity.getAnchor(), HsOfficePersonResource.class));

View File

@ -29,6 +29,36 @@ public interface HsOfficeRelationRbacRepository extends Repository<HsOfficeRelat
""", nativeQuery = true) """, nativeQuery = true)
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType); List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidAndRelationTypeString(@NotNull UUID personUuid, String relationType);
/**
* Finds relations by a conjunction of optional criteria, including anchorPerson, holderPerson and contact data.
*
* @param personUuid the optional UUID of the anchorPerson or holderPerson
* @param relationType the type of the relation
* @param personData a lower-case string to match the persons tradeName, familyName or givenName (use '%' for wildcard)
* @param contactData a lower-case string to match the contacts caption, postalAddress, emailAddresses or phoneNumbers (use '%' for wildcard)
* @return a list of (accessible) relations which match all given criteria
*/
@Query(value = """
SELECT rel FROM HsOfficeRelationRbacEntity AS rel
WHERE (:relationType IS NULL OR CAST(rel.type AS String) = CAST(:relationType AS String))
AND ( :personUuid IS NULL OR
rel.anchor.uuid = :personUuid OR rel.holder.uuid = :personUuid )
AND ( :personData IS NULL OR
lower(rel.anchor.tradeName) LIKE :personData OR lower(rel.holder.tradeName) LIKE :personData OR
lower(rel.anchor.familyName) LIKE :personData OR lower(rel.holder.familyName) LIKE :personData OR
lower(rel.anchor.givenName) LIKE :personData OR lower(rel.holder.givenName) LIKE :personData )
AND ( :contactData IS NULL OR
lower(rel.contact.caption) LIKE :contactData OR
lower(rel.contact.postalAddress) LIKE :contactData OR
lower(CAST(rel.contact.emailAddresses AS String)) LIKE :contactData OR
lower(CAST(rel.contact.phoneNumbers AS String)) LIKE :contactData )
""")
List<HsOfficeRelationRbacEntity> findRelationRelatedToPersonUuidRelationTypePersonAndContactData(
UUID personUuid,
HsOfficeRelationType relationType,
String personData,
String contactData);
HsOfficeRelationRbacEntity save(final HsOfficeRelationRbacEntity entity); HsOfficeRelationRbacEntity save(final HsOfficeRelationRbacEntity entity);
long count(); long count();

View File

@ -1,6 +1,8 @@
get: get:
summary: Returns a list of (optionally filtered) person relations for a given person. summary: Returns a list of (optionally filtered) person relations for a given person.
description: Returns the list of (optionally filtered) person relations of a given person and which are visible to the current subject or any of it's assumed roles. description:
Returns the list of (optionally filtered) person relations of a given person and which are visible to the current subject or any of it's assumed roles.
To match data, all given query parameters must be fulfilled ('and' / logical conjunction).
tags: tags:
- hs-office-relations - hs-office-relations
operationId: listRelations operationId: listRelations
@ -9,7 +11,7 @@ get:
- $ref: 'auth.yaml#/components/parameters/assumedRoles' - $ref: 'auth.yaml#/components/parameters/assumedRoles'
- name: personUuid - name: personUuid
in: query in: query
required: true required: false
schema: schema:
type: string type: string
format: uuid format: uuid
@ -20,6 +22,18 @@ get:
schema: schema:
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationType' $ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationType'
description: Prefix of name properties from holder or contact to filter the results. description: Prefix of name properties from holder or contact to filter the results.
- name: personData
in: query
required: false
schema:
type: string
description: 'Data from any of these text field in the anchor or holder person: tradeName, familyName, givenName'
- name: contactData
in: query
required: false
schema:
type: string
description: 'Data from any of these text field in the contact: caption, postalAddress, emailAddresses, phoneNumbers'
responses: responses:
"200": "200":
description: OK description: OK

View File

@ -176,6 +176,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
// given // given
final var givenEntity = validEntityBuilder().build(); final var givenEntity = validEntityBuilder().build();
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
Dns.fakeResultForDomain(givenEntity.getIdentifier(), Dns.Result.fromRecords());
// when // when
final var errors = validator.validateContext(givenEntity); final var errors = validator.validateContext(givenEntity);
@ -317,6 +318,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
)) ))
.build(); .build();
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
Dns.fakeResultForDomain("example.org", Dns.Result.fromRecords());
// when // when
final var errors = validator.validateContext(givenEntity); final var errors = validator.validateContext(givenEntity);
@ -340,6 +342,7 @@ class HsDomainDnsSetupHostingAssetValidatorUnitTest {
)) ))
.build(); .build();
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType()); final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
Dns.fakeResultForDomain("example.org", Dns.Result.fromRecords());
// when // when
final var zonefileErrors = new ArrayList<String>(); final var zonefileErrors = new ArrayList<String>();

View File

@ -9,7 +9,6 @@ import net.hostsharing.hsadminng.context.Context;
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationTypeResource; import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationTypeResource;
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository; import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRepository;
import net.hostsharing.hsadminng.rbac.test.JpaAttempt; import net.hostsharing.hsadminng.rbac.test.JpaAttempt;
import org.json.JSONException;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -55,7 +54,7 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
class ListRelations { class ListRelations {
@Test @Test
void globalAdmin_withoutAssumedRoles_canViewAllRelationsOfGivenPersonAndType() throws JSONException { void globalAdmin_withoutAssumedRoles_canViewAllRelationsOfGivenPersonAndType() {
// given // given
context.define("superuser-alex@hostsharing.net"); context.define("superuser-alex@hostsharing.net");
@ -113,7 +112,7 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
} }
@Test @Test
void personAdmin_canViewAllRelationsOfGivenRelatedPersonAndAnyType() throws JSONException { void personAdmin_canViewAllRelationsOfGivenRelatedPersonAndAnyType() {
// given // given
context.define("contact-admin@firstcontact.example.com"); context.define("contact-admin@firstcontact.example.com");
@ -125,7 +124,7 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
.port(port) .port(port)
.when() .when()
.get("http://localhost/api/hs/office/relations?personUuid=%s" .get("http://localhost/api/hs/office/relations?personUuid=%s"
.formatted(givenPerson.getUuid(), HsOfficeRelationTypeResource.PARTNER)) .formatted(givenPerson.getUuid()))
.then().log().all().assertThat() .then().log().all().assertThat()
.statusCode(200) .statusCode(200)
.contentType("application/json") .contentType("application/json")
@ -169,6 +168,50 @@ class HsOfficeRelationControllerAcceptanceTest extends ContextBasedTestWithClean
""")); """));
// @formatter:on // @formatter:on
} }
@Test
void globalAdmin_canViewAllRelationsWithGivenContactData() {
// given
context.define("superuser-alex@hostsharing.net");
RestAssured // @formatter:off
.given()
.header("current-subject", "superuser-alex@hostsharing.net")
.port(port)
.when()
.get("http://localhost/api/hs/office/relations?personData=firby&contactData=Contact-Admin@FirstContact.Example.COM")
.then().log().all().assertThat()
.statusCode(200)
.contentType("application/json")
.body("", lenientlyEquals("""
[
{
"anchor": {
"personType": "LEGAL_PERSON",
"tradeName": "First GmbH"
},
"holder": {
"personType": "NATURAL_PERSON",
"givenName": "Susan",
"familyName": "Firby"
},
"type": "REPRESENTATIVE",
"contact": {
"caption": "first contact",
"postalAddress": "Vorname Nachname\\nStraße Hnr\\nPLZ Stadt",
"emailAddresses": {
"main": "contact-admin@firstcontact.example.com"
},
"phoneNumbers": {
"phone_office": "+49 123 1234567"
}
}
}
]
"""));
// @formatter:on
}
} }
@Nested @Nested

View File

@ -33,3 +33,10 @@ logging:
level: level:
liquibase: WARN liquibase: WARN
net.ttddyy.dsproxy.listener: DEBUG # HOWTO: log meaningful SQL statements net.ttddyy.dsproxy.listener: DEBUG # HOWTO: log meaningful SQL statements
org.testcontainers: DEBUG
com.github.dockerjava: DEBUG
testcontainers:
network:
mode: host