Compare commits
10 Commits
16252334b7
...
4994bac101
Author | SHA1 | Date | |
---|---|---|---|
|
4994bac101 | ||
|
eb9edf1cb1 | ||
|
5ca0638319 | ||
|
a2b81f009b | ||
|
e3b11972e5 | ||
|
f8fda06beb | ||
|
a0635960a5 | ||
|
ddd96654ef | ||
|
abafd64813 | ||
|
a1a753e00a |
21
.aliases
21
.aliases
@ -1,4 +1,4 @@
|
||||
# For using the alias gw-importOfficeData or gw-importHostingAssets,
|
||||
# For using the alias gw-importHostingAssets,
|
||||
# copy the file .tc-environment to .environment (ignored by git)
|
||||
# and amend them according to your external DB.
|
||||
|
||||
@ -71,7 +71,6 @@ function importLegacyData() {
|
||||
./gradlew $target --rerun
|
||||
fi
|
||||
}
|
||||
alias gw-importOfficeData='importLegacyData importOfficeData'
|
||||
alias gw-importHostingAssets='importLegacyData importHostingAssets'
|
||||
|
||||
alias podman-start='systemctl --user enable --now podman.socket && systemctl --user status podman.socket && ls -la /run/user/$UID/podman/podman.sock'
|
||||
@ -92,8 +91,8 @@ alias fp='grep -r '@Accepts' src | sed -e 's/^.*@/@/g' | sort -u | wc -l'
|
||||
alias gw-spotless='./gradlew spotlessApply -x pitest -x test -x :processResources'
|
||||
alias gw-check='. .aliases; . .tc-environment; gw test check -x pitest'
|
||||
|
||||
# HOWTO: run all 'normal' tests (no scenario+import-tests): `gw-test`
|
||||
# You can also mention specific targets: `gw-test importOfficeData`.
|
||||
# HOWTO: run all 'normal' tests (by default without scenario+import-tests): `gw-test`
|
||||
# You can also mention specific targets: `gw-test importHostingAssets`, in that case only these tests are executed.
|
||||
# This will always use the environment from `.tc-environment`.
|
||||
#
|
||||
# HOWTO: re-run tests even if no changed can be detected: `gw-test --rerun`
|
||||
@ -110,20 +109,22 @@ function _gwTest1() {
|
||||
echo "RUNNING gw $@"
|
||||
printf -- '-%0.s' {1..80}; echo
|
||||
./gradlew "$@"
|
||||
local buildResultCode=$?
|
||||
printf -- '-%0.s' {1..80}; echo
|
||||
echo "DONE gw $@"
|
||||
return $buildResultCode
|
||||
}
|
||||
function _gwTest() {
|
||||
. .aliases;
|
||||
. .tc-environment;
|
||||
rm /tmp/gwTest.tmp
|
||||
. .aliases
|
||||
. .tc-environment
|
||||
rm -f /tmp/gwTest.tmp
|
||||
if [ "$1" == "--all" ]; then
|
||||
shift # to remove the --all from $@
|
||||
# delierately in separate gradlew-calls to avoid Testcontains-PostgreSQL problem spillover
|
||||
time (_gwTest1 unitTest "$@" &&
|
||||
_gwTest1 officeIntegrationTest bookingIntegrationTest hostingIntegrationTest "$@" &&
|
||||
_gwTest1 scenarioTest "$@" &&
|
||||
_gwTest1 importOfficeData importHostingAssets "$@");
|
||||
_gwTest1 importHostingAssets "$@");
|
||||
elif [ $# -eq 0 ] || [[ $1 == -* ]]; then
|
||||
time _gwTest1 test "$@";
|
||||
else
|
||||
@ -137,7 +138,7 @@ alias howto=bin/howto
|
||||
alias cas-curl=bin/cas-curl
|
||||
|
||||
# etc/docker-compose.yml limits CPUs+MEM and includes a PostgreSQL config for analysing slow queries
|
||||
alias gw-importOfficeData-in-docker-compose='
|
||||
alias gw-importHostingAssets-in-docker-compose='
|
||||
docker-compose -f etc/docker-compose.yml down &&
|
||||
docker-compose -f etc/docker-compose.yml up -d && sleep 10 &&
|
||||
time gw-importHostingAssets'
|
||||
@ -147,6 +148,6 @@ if [ ! -f .environment ]; then
|
||||
fi
|
||||
source .environment
|
||||
|
||||
alias scenario-reports-upload='./gradlew scenarioTest convertMarkdownToHtml && ssh hsh03-hsngdev@h50.hostsharing.net "rm -f doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office/*.html" && scp build/doc/scenarios/*.html hsh03-hsngdev@h50.hostsharing.net:doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office'
|
||||
alias scenario-reports-upload='./gradlew scenarioTest convertMarkdownToHtml && ssh hsh03-hsngdev@hsh03.hostsharing.net "rm -f doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office/*.html" && scp build/doc/scenarios/*.html hsh03-hsngdev@hsh03.hostsharing.net:doms/hsngdev.hs-example.de/htdocs-ssl/scenarios/office'
|
||||
alias scenario-reports-open='open https://hsngdev.hs-example.de/scenarios/office'
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
<entry key="HSADMINNG_POSTGRES_ADMIN_USERNAME" value="postgres" />
|
||||
<entry key="HSADMINNG_POSTGRES_JDBC_URL" value="jdbc:postgresql://localhost:5432/postgres" />
|
||||
<entry key="HSADMINNG_POSTGRES_RESTRICTED_USERNAME" value="restricted" />
|
||||
<entry key="HSADMINNG_MIGRATION_DATA_PATH" value="migration" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
|
@ -3,9 +3,9 @@
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="HSADMINNG_MIGRATION_DATA_PATH" value="migration" />
|
||||
<entry key="HSADMINNG_POSTGRES_ADMIN_USERNAME" value="admin" />
|
||||
<entry key="HSADMINNG_POSTGRES_RESTRICTED_USERNAME" value="restricted" />
|
||||
<entry key="HSADMINNG_SUPERUSER" value="import-superuser@hostsharing.net" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
|
@ -1,103 +0,0 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ImportOfficeData" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="HSADMINNG_MIGRATION_DATA_PATH" value="migration" />
|
||||
<entry key="HSADMINNG_POSTGRES_ADMIN_USERNAME" value="admin" />
|
||||
<entry key="HSADMINNG_POSTGRES_RESTRICTED_USERNAME" value="restricted" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":importOfficeData" />
|
||||
<option value="--tests" />
|
||||
<option value=""net.hostsharing.hsadminng.hs.migration.ImportOfficeData"" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<EXTENSION ID="com.intellij.execution.ExternalSystemRunConfigurationJavaExtension">
|
||||
<extension name="coverage" sample_coverage="false" />
|
||||
</EXTENSION>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>true</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration default="false" name="ImportOfficeData" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="HSADMINNG_MIGRATION_DATA_PATH" value="migration" />
|
||||
<entry key="HSADMINNG_POSTGRES_ADMIN_USERNAME" value="admin" />
|
||||
<entry key="HSADMINNG_POSTGRES_RESTRICTED_USERNAME" value="restricted" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":importOfficeData" />
|
||||
<option value="--tests" />
|
||||
<option value=""net.hostsharing.hsadminng.hs.office.migration.ImportOfficeData"" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<EXTENSION ID="com.intellij.execution.ExternalSystemRunConfigurationJavaExtension">
|
||||
<extension name="coverage" sample_coverage="false" />
|
||||
</EXTENSION>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>true</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration default="false" name="ImportOfficeData" type="GradleRunConfiguration" factoryName="Gradle">
|
||||
<ExternalSystemSettings>
|
||||
<option name="env">
|
||||
<map>
|
||||
<entry key="HSADMINNG_POSTGRES_ADMIN_USERNAME" value="admin" />
|
||||
<entry key="HSADMINNG_POSTGRES_RESTRICTED_USERNAME" value="restricted" />
|
||||
</map>
|
||||
</option>
|
||||
<option name="executionName" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="externalSystemIdString" value="GRADLE" />
|
||||
<option name="scriptParameters" value="" />
|
||||
<option name="taskDescriptions">
|
||||
<list />
|
||||
</option>
|
||||
<option name="taskNames">
|
||||
<list>
|
||||
<option value=":importOfficeData" />
|
||||
<option value="--tests" />
|
||||
<option value=""net.hostsharing.hsadminng.hs.migration.ImportOfficeData"" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="vmOptions" />
|
||||
</ExternalSystemSettings>
|
||||
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
|
||||
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
|
||||
<EXTENSION ID="com.intellij.execution.ExternalSystemRunConfigurationJavaExtension">
|
||||
<extension name="coverage" sample_coverage="false" />
|
||||
</EXTENSION>
|
||||
<DebugAllEnabled>false</DebugAllEnabled>
|
||||
<RunAsTest>true</RunAsTest>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
@ -1,7 +1,8 @@
|
||||
unset HSADMINNG_POSTGRES_JDBC_URL # dynamically set, different for normal tests and imports
|
||||
export HSADMINNG_POSTGRES_ADMIN_USERNAME=admin
|
||||
export HSADMINNG_POSTGRES_ADMIN_PASSWORD=
|
||||
source .unset-environment
|
||||
|
||||
export HSADMINNG_POSTGRES_RESTRICTED_USERNAME=restricted
|
||||
export HSADMINNG_SUPERUSER=superuser-alex@hostsharing.net
|
||||
export HSADMINNG_MIGRATION_DATA_PATH=migration
|
||||
export HSADMINNG_POSTGRES_ADMIN_USERNAME=admin
|
||||
export HSADMINNG_SUPERUSER=import-superuser@hostsharing.net
|
||||
export HSADMINNG_CAS_SERVER=
|
||||
|
||||
export LANG=en_US.UTF-8
|
||||
|
@ -4,4 +4,6 @@ unset HSADMINNG_POSTGRES_ADMIN_PASSWORD
|
||||
unset HSADMINNG_POSTGRES_RESTRICTED_USERNAME
|
||||
unset HSADMINNG_SUPERUSER
|
||||
unset HSADMINNG_MIGRATION_DATA_PATH
|
||||
unset HSADMINNG_OFFICE_DATA_SQL_FILE
|
||||
unset HSADMINNG_CAS_SERVER=
|
||||
|
||||
|
4
Jenkinsfile
vendored
4
Jenkinsfile
vendored
@ -55,9 +55,9 @@ pipeline {
|
||||
sh './gradlew bookingIntegrationTest hostingIntegrationTest --no-daemon'
|
||||
}
|
||||
}
|
||||
stage('Import-Tests') {
|
||||
stage('Test-Imports') {
|
||||
steps {
|
||||
sh './gradlew importOfficeData importHostingAssets --no-daemon'
|
||||
sh './gradlew importHostingAssets --no-daemon'
|
||||
}
|
||||
}
|
||||
stage ('Scenario-Tests') {
|
||||
|
50
README.md
50
README.md
@ -91,17 +91,15 @@ Next, compile and run the application on `localhost:8080` and the management ser
|
||||
export HSADMINNG_CAS_SERVER=
|
||||
|
||||
# this runs the application with test-data and all modules:
|
||||
gw bootRun --args='--spring.profiles.active=dev,complete,test-data'
|
||||
gw bootRun --args='--spring.profiles.active=dev,fakeCasAuthenticator,complete,test-data'
|
||||
|
||||
The meaning of these profiles is:
|
||||
|
||||
- **dev**: the PostgreSQL users are created via Liquibase
|
||||
- **fakeCasAuthenticator**: The username is simply taken from whatever is after "Bearer " in the "Authorization" header.
|
||||
- **complete**: all modules are started
|
||||
- **test-data**: some test data inserted
|
||||
|
||||
Running just `gw bootRun` would just run the *office* module, not insert any test-data and
|
||||
require the PostgreSQL users created in the database (see env-vars in `.aliases`).
|
||||
|
||||
Now we can access the REST API, e.g. using curl:
|
||||
|
||||
# the following command should reply with "pong":
|
||||
@ -109,19 +107,19 @@ Now we can access the REST API, e.g. using curl:
|
||||
|
||||
# the following command should return a JSON array with just all customers:
|
||||
curl -f -s\
|
||||
-H 'current-subject: superuser-alex@hostsharing.net' \
|
||||
-H 'Authorization: Bearer superuser-alex@hostsharing.net' \
|
||||
http://localhost:8080/api/test/customers \
|
||||
| jq # just if `jq` is installed, to prettyprint the output
|
||||
|
||||
# the following command should return a JSON array with just all packages visible for the admin of the customer yyy:
|
||||
curl -f -s\
|
||||
-H 'current-subject: superuser-alex@hostsharing.net' -H 'assumed-roles: rbactest.customer#yyy:ADMIN' \
|
||||
-H 'Authorization: Bearer superuser-alex@hostsharing.net' -H 'assumed-roles: rbactest.customer#yyy:ADMIN' \
|
||||
http://localhost:8080/api/test/packages \
|
||||
| jq
|
||||
|
||||
# add a new customer
|
||||
curl -f -s\
|
||||
-H 'current-subject: superuser-alex@hostsharing.net' -H "Content-Type: application/json" \
|
||||
-H 'Authorization: Bearer superuser-alex@hostsharing.net' -H "Content-Type: application/json" \
|
||||
-d '{ "prefix":"ttt", "reference":80001, "adminUserName":"admin@ttt.example.com" }' \
|
||||
-X POST http://localhost:8080/api/test/customers \
|
||||
| jq
|
||||
@ -132,9 +130,18 @@ Also try for example 'admin@xxx.example.com' or 'unknown@example.org'.
|
||||
|
||||
If you want a formatted JSON output, you can pipe the result to `jq` or similar.
|
||||
|
||||
And to see the full, currently implemented, API, open http://localhost:8081/actuator/swagger-ui/index.html (uses management-port and thus bypasses authentication).
|
||||
And to see the full, currently implemented, API, open http://localhost:8080/swagger-ui/index.html).
|
||||
For a locally running app without CAS-authentication (export HSADMINNG_CAS_SERVER=''),
|
||||
authorize using the name of the subject (e.g. "superuser-alex@hostsharing.net" in case of test-data).
|
||||
Otherwise, use a valid CAS-ticket.
|
||||
|
||||
If you still need to install some of these tools, find some hints in the next chapters.
|
||||
If you want to run the application with real CAS-Authentication:
|
||||
|
||||
# set the CAS-SERVER-Root, also see `bin/cas-curl`.
|
||||
export HSADMINNG_CAS_SERVER=https://login.hostsharing.net # or whatever your CAS-Server-URL you want to use
|
||||
|
||||
# run the application against the real CAS authenticator
|
||||
gw bootRun --args='--spring.profiles.active=dev,realCasAuthenticator,complete,test-data'
|
||||
|
||||
|
||||
### PostgreSQL Server
|
||||
@ -656,7 +663,7 @@ howto
|
||||
Add `--args='--spring.profiles.active=...` with the wanted profile selector:
|
||||
|
||||
```sh
|
||||
gw bootRun --args='--spring.profiles.active=external-db,only -office,without-test-data'
|
||||
gw bootRun --args='--spring.profiles.active=external-db,only-office,without-test-data'
|
||||
```
|
||||
|
||||
These profiles mean:
|
||||
@ -666,6 +673,29 @@ These profiles mean:
|
||||
- **without-test-data**: no test-data is inserted
|
||||
|
||||
|
||||
### How to Run the Application in a Debugger
|
||||
|
||||
Add `' --debug-jvm` to the command line:
|
||||
|
||||
|
||||
```sh
|
||||
gw bootRun ... --debug-jvm
|
||||
```
|
||||
|
||||
At the very beginning, the application is going to wait for a debugger with a message like this:
|
||||
|
||||
> Listening for transport dt_socket at address: 5005
|
||||
|
||||
As soon as a debugger connects to that port, the application will continue to run.
|
||||
|
||||
In IntelliJ IDEA you need a 'Remote JVM Debug' run configuration like this:
|
||||
|
||||

|
||||
|
||||
Now, to attach IntelliJ IDEA as a debugger, you just need to run that config in debug mode.
|
||||
If it's selected, just hit the *bug*-symbol next to it.
|
||||
|
||||
|
||||
### How to Do a Clean Run of the Application
|
||||
|
||||
If you frequently need to run with a fresh database and a clean build, you can use this:
|
||||
|
12
bin/cas-curl
12
bin/cas-curl
@ -131,6 +131,15 @@ function casTicket() {
|
||||
echo $HSADMINNG_CAS_TICKET
|
||||
}
|
||||
|
||||
function casTgt() {
|
||||
HSADMINNG_CAS_TGT=$(<~/.cas-login-tgt)
|
||||
if [[ -z "$HSADMINNG_CAS_TGT" ]]; then
|
||||
echo "ERROR: cannot get CAS ticket granting ticket for $HSADMINNG_CAS_USERNAME" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "CAS-TGT: $HSADMINNG_CAS_TGT"
|
||||
}
|
||||
|
||||
function casValidate() {
|
||||
HSADMINNG_CAS_TICKET=`casTicket`
|
||||
|
||||
@ -191,6 +200,9 @@ case "${1,,}" in
|
||||
"unassume") ## do not assume any particular role anymore, use the plain user as RBAC subject
|
||||
rm ~/.cas-curl-assume
|
||||
;;
|
||||
"tgt") ## prints the current ticket granting ticket
|
||||
casTgt
|
||||
;;
|
||||
"validate") ## validates current ticket granting ticket and prints currently logged in user
|
||||
casValidate
|
||||
;;
|
||||
|
@ -31,7 +31,7 @@ def search_keywords_in_files(keywords):
|
||||
sys.exit(1)
|
||||
|
||||
# Allowed comment symbols
|
||||
comment_symbols = {"//", "#", ";"}
|
||||
comment_symbols = {"//", "#", "##", "###", "####", "#####", ";"}
|
||||
|
||||
for root, dirs, files in os.walk("."):
|
||||
# Ausschließen bestimmter Verzeichnisse
|
||||
|
22
build.gradle
22
build.gradle
@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.4.1'
|
||||
id 'org.springframework.boot' version '3.4.2'
|
||||
id 'io.spring.dependency-management' version '1.1.7' // manages implicit dependencies
|
||||
id 'io.openapiprocessor.openapi-processor' version '2023.2' // generates Controller-interface and resources from API-spec
|
||||
id 'com.github.jk1.dependency-license-report' version '2.9' // checks dependency-license compatibility
|
||||
@ -66,8 +66,8 @@ dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-actuator'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.5'
|
||||
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.10.0'
|
||||
implementation 'org.springdoc:springdoc-openapi:2.8.3'
|
||||
implementation 'org.postgresql:postgresql'
|
||||
implementation 'org.liquibase:liquibase-core'
|
||||
implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.9.0'
|
||||
@ -77,7 +77,6 @@ dependencies {
|
||||
implementation 'net.java.dev.jna:jna:5.16.0'
|
||||
implementation 'org.modelmapper:modelmapper:3.2.2'
|
||||
implementation 'org.iban4j:iban4j:3.2.10-RELEASE'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.3'
|
||||
implementation 'org.reflections:reflections:0.10.2'
|
||||
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
@ -263,7 +262,7 @@ test {
|
||||
'net.hostsharing.hsadminng.**.generated.**',
|
||||
]
|
||||
useJUnitPlatform {
|
||||
excludeTags 'importOfficeData', 'importHostingAssets', 'scenarioTest'
|
||||
excludeTags 'importHostingAssets', 'scenarioTest'
|
||||
}
|
||||
}
|
||||
|
||||
@ -338,7 +337,7 @@ jacocoTestCoverageVerification {
|
||||
// HOWTO: run all unit-tests which don't need a database: gw-test unitTest
|
||||
tasks.register('unitTest', Test) {
|
||||
useJUnitPlatform {
|
||||
excludeTags 'importOfficeData', 'importHostingAssets', 'scenarioTest', 'generalIntegrationTest',
|
||||
excludeTags 'importHostingAssets', 'scenarioTest', 'generalIntegrationTest',
|
||||
'officeIntegrationTest', 'bookingIntegrationTest', 'hostingIntegrationTest'
|
||||
}
|
||||
|
||||
@ -396,17 +395,6 @@ tasks.register('hostingIntegrationTest', Test) {
|
||||
mustRunAfter spotlessJava
|
||||
}
|
||||
|
||||
tasks.register('importOfficeData', Test) {
|
||||
useJUnitPlatform {
|
||||
includeTags 'importOfficeData'
|
||||
}
|
||||
|
||||
group 'verification'
|
||||
description 'run the import jobs as tests'
|
||||
|
||||
mustRunAfter spotlessJava
|
||||
}
|
||||
|
||||
tasks.register('importHostingAssets', Test) {
|
||||
useJUnitPlatform {
|
||||
includeTags 'importHostingAssets'
|
||||
@ -439,7 +427,7 @@ pitest {
|
||||
]
|
||||
|
||||
targetTests = ['net.hostsharing.hsadminng.**.*UnitTest', 'net.hostsharing.hsadminng.**.*RestTest']
|
||||
excludedTestClasses = ['**AcceptanceTest*', '**IntegrationTest*', '**ImportOfficeData', '**ImportHostingAssets']
|
||||
excludedTestClasses = ['**AcceptanceTest*', '**IntegrationTest*', '**ImportHostingAssets']
|
||||
|
||||
pitestVersion = '1.17.0'
|
||||
junit5PluginVersion = '1.1.0'
|
||||
|
BIN
doc/.images/intellij-idea-jvm-debug-run-config.png
Normal file
BIN
doc/.images/intellij-idea-jvm-debug-run-config.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
124
doc/adr/2025-02-27-exchanging-the-partner-person.de.md
Normal file
124
doc/adr/2025-02-27-exchanging-the-partner-person.de.md
Normal file
@ -0,0 +1,124 @@
|
||||
# Änderung eines Geschäftspartners oder Rechnungsempfängers (Debitor)
|
||||
|
||||
**Status:**
|
||||
- [x] vorgeschlagen von (Michael Hönnig)
|
||||
- [ ] akzeptiert von (...)
|
||||
- [ ] abgelehnt von (...)
|
||||
- [ ] ersetzt durch (ersetzende ADR)
|
||||
|
||||
## Kontext und Problemstellung
|
||||
|
||||
Im vorgegebenen Datenmodell von Geschäftspartnern und Rechnungsempfängern (Debitoren), das auch fachliche Rollen wie Repräsentant, technische Ansprechpartner oder Mailinglisten-Subscriptions umfasst, stellt sich die Frage, wie eine Änderung der Geschäftspartner-Person effizient und konsistent umgesetzt werden kann.
|
||||
Diese fachlichen Rollen hängen jeweils an der Partner-Person.
|
||||
|
||||
Ein konkretes Beispiel hierfür ist die Änderung von einer natürlichen Person, die verstorben ist, zu deren Erbengemeinschaft.
|
||||
**Hierbei zeigte sich, dass die API-Bedienung durch die Vielzahl neu zu erstellender Objekte und deren Verknüpfungen komplex und fehleranfällig ist. Zudem lassen sich nicht alle Änderung in einer einzigen Transaktion durchführen, was zu Inkonsistenzen führen kann.“**
|
||||
|
||||
Angepasst werden müssen:
|
||||
|
||||
1. alle Relations mit der alten Partner-Person:
|
||||
- die PARTNER-Relation
|
||||
- die DEBITOR-Relations (ggf. mehrere)
|
||||
- die OPERATIONS-Relations (ggf. mehrere)
|
||||
- die SUBSCRIBER-Relations (ggf. mehrere)
|
||||
- die REPRESENTATIVE-Relations (ggf. mehrere)
|
||||
- etc.
|
||||
2. Die PARTNER-Relation hat die Besonderheit, dass sie vom Partner referenziert wird und daher auch dort ausgetauscht werden muss.
|
||||
3. Die DEBITOR-Relation hat die Besonderheit, dass sie vom Debitor referenziert wird und daher auch dort ausgetauscht werden muss.
|
||||
|
||||
Daher sollen möglichst viele dieser *Neuverdrahtungen* im Backend gemacht werden.
|
||||
Und dafür braucht es dann eine zentrale Stelle, an der die Kaskade ausgelöst wird.
|
||||
|
||||
Derzeit gibt es drei mögliche Varianten, diese Änderung dynamisch umzusetzen, die jeweils unterschiedliche Auswirkungen auf Aufwände, API und Zugriffsrechte haben.
|
||||
|
||||
### Technischer Hintergrund
|
||||
|
||||
Zum Zeitpunkt der Erstellung dieses ADR existieren folgende relevante Entitäten:
|
||||
- **Person**: Natürliche oder juristische Person (Name, Firma, Anrede etc.)
|
||||
- **Contact**: Kontaktdaten einer fachlichen Rolle
|
||||
- **Relation**: Mit einem Typ (z.B. PARTNER, DEBITOR, REPRESENTATIVE) und Kontaktdaten versehene Beziehung von einer Person (Holder) zu einer anderen (Anchor)
|
||||
- **Partner**: Sind quasi Zusatzdaten einer PARTNER-Relation (derzeit nur die Partnernummer), welche eine Partner-Person mit der Hostsharing-Person verknüpft
|
||||
- **Debitor**: Sind quasi Zusatzdaten einer DEBITOR-Relation, welche eine Debitor-Person mit einer Partner-Person verknüpft
|
||||
|
||||
Zugriffsrechte werden über ein hierarchisches, dynamisches RBAC-System gesteuert, bei dem der **OWNER** einer Entitäten-Instanz alle Rechte hat, **ADMIN** definierte Spalten aktualisieren darf, **AGENT** Verknüpfungen anlegen kann, und **TENANT**, **GUEST** sowie **REFERRER** nur Lesezugriff haben.
|
||||
Partner und Debitor nutzen dabei die RBAC-Rollen der zugehörigen Relations.
|
||||
|
||||
## In Betracht gezogene Varianten
|
||||
|
||||
* **1. Relations ersetzen:** Austausch der PARTNER-/DEBITOR-/OPERATIONS-/...-Relations gegen eine neue Relation für die neue Partner-Person (z.B. Erbengemeinschaft) als neuen Holder als PATCH auf /api/hs/office/partners/UUID
|
||||
* **2. Relations direkt aktualisieren:** Änderung der Holder-Referenz in der bestehenden PARTNER-Relation auf die neue Partner-Person (z.B. Erbengemeinschaft) als PATCH auf /api/hs/office/relations/UUID
|
||||
* **3. Relations via Partner aktualisieren:** Änderung der Partner-Person in die PARTNER-Relation als PATCH auf /api/hs/office/partners/UUID
|
||||
|
||||
### Variante 1: Relations ersetzen
|
||||
|
||||
Der Austausch der Partner- (und Debitor-) Person erfolgt über das Erstellen einer neuen PARTNER- bzw. DEBITOR-Relation, im Partner bzw. Debitor wird dann die Referenz auf die alte PARTNER- bzw. DEBITOR-Relation gegen die neue ausgetauscht.
|
||||
|
||||
#### Vorteile
|
||||
|
||||
- **Beibehaltung der API:** Dieses Verhalten ist bereits implementiert und benötigt keinen großen Umbau an der API, sondern nur eine Erweiterung um das Austauschen weiterer Relations.
|
||||
- **UPDATE-Permission für AGENT:** Es wäre möglich, der AGENT-Rolle einer Relation UPDATE-Rechte an der Relation zu geben, weil nur die unkritische Contact-Referenz änderbar wäre.
|
||||
- **Kongruenz von Fachlichkeit+API**: Fachlich handelt es sich um den Austausch der Partner-Person, dazu passend wäre der Endpunkt, allerdings wird in dieser Variante nicht direkt die Partner-Person ausgetauscht, sondern eine neue PARTNER-Relation mit der neuen Partner-Person eingesetzt.
|
||||
|
||||
#### Nachteile
|
||||
|
||||
- **Verlust expliziter GRANTs:** Gibt es explizite GRANTs an der PARTNER-Relation, gehen diese verloren, da die Relation ausgetauscht wird. Die Übernahme dieser expliziten Grants erfordert also einen zusätzlichen Implementationsaufwand.
|
||||
- **Divergenz zwischen Fachlichkeit und API:** Fachlich handelt es sich um den Austausch der Partner-Person, würde aber eine neue PARTNER-Relation dieser Person in den Partner eingesetzt werden. Das erfordert ein höheres Verständnis des Datenmodells.
|
||||
- **Keine Anwendbarkeit auf abhängige Relations:** Beim Aktualisieren der abhängigen Relations (z.B. Representative, Operational- und Billing-Kontakt sowie der Mailinglisten-Subscriptions) stehen wir wieder vor dem Ausgangsproblem und müssten jeweils neue Relations erzeugen und die alten Relations löschen, was dann wieder zum Verlust expliziter GRANTs führt.
|
||||
- **Performance bei vielen abhängigen Relations:** die abhängigen Relations können nur über Loops, nicht aber durch direkt SQL UPDATEs ausgetauscht werden, was zu einer schlechteren Performance führt
|
||||
|
||||
### Variante 2: Relations direkt aktualisieren
|
||||
|
||||
Die bestehende PARTNER-Relation bliebe erhalten, und der Holder wird von der verstorbenen Person auf die Erbengemeinschaft geändert.
|
||||
|
||||
#### Vorteile
|
||||
|
||||
- **Anwendbarkeit auf Partner- und Debitor-Person:** Der Code wäre an einer generischen Stelle, welche dann Partner- und Debitor-Person austauschbar machen würde
|
||||
- **Einheitlichkeit/Generizität der API:** Die REST-API für Änderungen gehört dann einheitlich zum Relation-Endpunkt, was der bestehenden Handhabung von Contact-Änderungen entspricht.
|
||||
|
||||
#### Nachteile
|
||||
|
||||
- **UPDATE Permission für Relation-AGENT wäre kritisch:** Der Relation-AGENT darf nicht das Recht bekommen, den Holder auszutauschen. Da es keine Spalten-spezifischen Update-Rechte gibt, könnte dieser auch den Contact nicht mehr austauschen. Derzeit ist das allerdings auch noch nicht so implementiert.
|
||||
- **Umbau der API:** Der Austausch einer Partner-Person würde vom Partner-Endpunkt (/api/hs/office/partner) zur Relation (/api/hs/office/partner) wandern, was ein größerer Umbau, auch bei den Tests wäre.
|
||||
- **Divergenz von Fachlichkeit und API**: Fachlich handelt es sich um den Austausch der Partner-Person, aber man würde die Person nicht am Partner selbst austauschen, sondern an der PARTNER-Relation.
|
||||
|
||||
### Variante 3: Relations via Partner aktualisieren
|
||||
|
||||
Der Austausch der Partner- (bzw. Debitor-) Person würde weiterhin beim Partner bzw. Debitor erfolgen, jedoch würde die Personen-Referenz direkt in der bestehenden Partner- (bzw. Debitor-) Relation umgesetzt werden, statt eine neue Relation mit der neuen Partner- (bzw. Debitor) Person einzusetzen. Die direkt wie auch abhängige Relations könnten also einfach per SQL UPDATE aktualisiert werden.
|
||||
|
||||
#### Vorteile
|
||||
|
||||
- **Beibehaltung der API:** Der Endpunkt /api/hs/office/partners/UUID bliebe erhalten, wenn auch lokal ein Umbau auf Person-Update statt Relation-Update erfolgen müsste, Anpassungen in Verwendungen dieser API, z.B. in Tests, wären allerdings wenig aufwändig und das Risiko für weitere Aufwände recht gering.
|
||||
- **UPDATE-Permission für AGENT:** Es wäre möglich, der AGENT-Rolle einer Relation UPDATE-Rechte an der Relation zu geben, aber eine Aktualisierung über die REST-Controller nur an kontrollierten Stellen zuzulassen.
|
||||
- **Kongruenz von Fachlichkeit+API**: Fachlich handelt es sich um den Austausch der Partner-Person, was auch in dieser Variante technisch abgebildet würde, wenn auch eine Ebene tiefer im JSON, nämlich in der Partner-Relation.
|
||||
|
||||
#### Nachteile
|
||||
|
||||
Nennenswerte Nachteile wurden nicht identifiziert, allenfalls ist es etwas schräge, dass die RBAC-Rechte an den Relations ein UPDATE zulassen, was aber an der API nur für bestimmte Relations (ggf. kontrolliert) erreichbar wäre.
|
||||
|
||||
|
||||
## Entscheidung und Ergebnis
|
||||
|
||||
**Entscheidung:** 3. Relations via Partner aktualisieren
|
||||
|
||||
**Begründung:**
|
||||
- die Fachlichkeit wird an der API gut abgebildet (PATCH der Partner-Person auf /api/hs/office/partners/UUID)
|
||||
- der Aufwand ist relativ gering (vieles ist mit SQL UPDATEs machbar)
|
||||
- die UPDATE Permission dürfte an Relation-AGENT granted werden, ohne damit Schindluder getrieben werden kann (weil das an der API verhindert werden kann)
|
||||
|
||||
| Kriterium \ Relations ... | 1. ersetzen | 2. direkt aktualisieren | 3. via Partner aktualisieren |
|
||||
|-----------------------------------------------|------------:|------------------------:|-----------------------------:|
|
||||
| **Technische und Aufwands-Kriterien** | | | |
|
||||
| Beibehaltung der API vs. Umbau (inkl. Risiko) | +2 | -2 | +1 |
|
||||
| Anwendbarkeit auf Partner- und Debitor-Person | | +1 | |
|
||||
| Anwendbarkeit auf abhängige Relations | -3 | | |
|
||||
| Performance bei vielen abhängigen Relations | -1 | | |
|
||||
| Aufwand für explizite Grants | -1 | | |
|
||||
| **Zwischenergebnis** | **-3** | **-1** | **+1** |
|
||||
| | | | |
|
||||
| **Fachliche Kriterien** | | | |
|
||||
| Kongruenz von Fachlichkeit+API | +1 | -1 | +1 |
|
||||
| Einheitlichkeit/Generizität der API | | +1 | |
|
||||
| UPDATE Permission für Relation-AGENT möglich | +1 | | +1 |
|
||||
| **Zwischenergebnis** | **+2** | **0** | **+2** |
|
||||
| | | | |
|
||||
| **Endergebnis** | **-1** | **-1** | **+3** |
|
124
doc/adr/2025-02-27-exchanging-the-partner-person.en.md
Normal file
124
doc/adr/2025-02-27-exchanging-the-partner-person.en.md
Normal file
@ -0,0 +1,124 @@
|
||||
# Changing a Business Partner or Invoice Recipient (Debitor)
|
||||
|
||||
**Status:**
|
||||
- [x] Proposed by (Michael Hönnig)
|
||||
- [ ] Accepted by (...)
|
||||
- [ ] Rejected by (...)
|
||||
- [ ] Replaced by (replacing ADR)
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
In the given data model of business partners and invoice recipients (debitors), which also includes business roles such as representative, technical contacts, or mailing list subscriptions, the question arises of how to efficiently and consistently implement a change of the business partner person. These business roles are each linked to the partner person.
|
||||
|
||||
A concrete example is changing from a natural person who has passed away to their heir community.
|
||||
**It has been shown that handling the API is complex and error-prone due to the large number of newly created objects and their links. Additionally, not all changes can be carried out in a single transaction, which can lead to inconsistencies.**
|
||||
|
||||
The following elements must be updated:
|
||||
|
||||
1. All relations with the old partner person:
|
||||
- The PARTNER relation
|
||||
- The DEBITOR relations (possibly multiple)
|
||||
- The OPERATIONS relations (possibly multiple)
|
||||
- The SUBSCRIBER relations (possibly multiple)
|
||||
- The REPRESENTATIVE relations (possibly multiple)
|
||||
- etc.
|
||||
2. The PARTNER relation has the peculiarity that it is referenced by the partner and therefore must also be replaced there.
|
||||
3. The DEBITOR relation has the peculiarity that it is referenced by the debitor and therefore must also be replaced there.
|
||||
|
||||
As a result, as many of these *rewirings* as possible should be done in the backend.
|
||||
A central point is needed to trigger this cascade.
|
||||
|
||||
Currently, there are three possible approaches to implementing this change dynamically, each with different impacts on effort, API, and access rights.
|
||||
|
||||
### Technical Background
|
||||
|
||||
At the time of this ADR's creation, the following relevant entities exist:
|
||||
- **Person**: A natural or legal entity (name, company, salutation, etc.)
|
||||
- **Contact**: Contact data of a business role
|
||||
- **Relation**: A relationship from one person (Holder) to another (Anchor), with a type (e.g., PARTNER, DEBITOR, REPRESENTATIVE) and contact data
|
||||
- **Partner**: Essentially additional data of a PARTNER relation (currently only the partner number), linking a partner person to the Hostsharing person
|
||||
- **Debitor**: Essentially additional data of a DEBITOR relation, linking a debitor person to a partner person
|
||||
|
||||
Access rights are managed through a hierarchical, dynamic RBAC system, where the **OWNER** of an entity instance has all rights, **ADMIN** can update defined fields, **AGENT** can create links, and **TENANT**, **GUEST**, and **REFERRER** have read-only access.
|
||||
Partners and debitors use the RBAC roles of the associated relations.
|
||||
|
||||
## Considered Alternatives
|
||||
|
||||
* **1. Replace Relations:** Replace PARTNER/DEBITOR/OPERATIONS/... relations with a new relation for the new partner person (e.g., heir community) as the new Holder via PATCH on /api/hs/office/partners/UUID
|
||||
* **2. Directly Update Relations:** Change the Holder reference in the existing PARTNER relation to the new partner person (e.g., heir community) via PATCH on /api/hs/office/relations/UUID
|
||||
* **3. Update Relations via Partner:** Change the partner person in the PARTNER relation via PATCH on /api/hs/office/partners/UUID
|
||||
|
||||
### Option 1: Replace Relations
|
||||
|
||||
The exchange of the partner (and debitor) person is done by creating a new PARTNER or DEBITOR relation, and then updating the reference in the partner or debitor to point to the new relation instead of the old one.
|
||||
|
||||
#### Advantages
|
||||
|
||||
- **Preserving the API:** This behavior is already implemented and requires no major API remodelling, only an extension to swap additional relations.
|
||||
- **UPDATE permission for AGENT:** The AGENT role of a relation could be granted UPDATE rights because only the non-critical contact reference would be modifiable.
|
||||
- **Congruence of business logic and API:** Conceptually, this aligns with replacing the partner person, though technically, a new PARTNER relation is created instead of directly replacing the person.
|
||||
|
||||
#### Disadvantages
|
||||
|
||||
- **Loss of explicit GRANTs:** Explicit GRANTs on the PARTNER relation would be lost due to the relation being replaced. Preserving these would require additional implementation effort.
|
||||
- **Mismatch between business logic and API:** The exchange of the partner person would not directly occur at the partner but rather through a new PARTNER relation.
|
||||
- **Not applicable to dependent relations:** Updating dependent relations (e.g., representatives, operational contacts, billing contacts, mailing list subscriptions) would require creating new relations and deleting old ones, again leading to the loss of explicit GRANTs.
|
||||
- **Performance issues with many dependent relations:** Dependent relations can only be exchanged via loops rather than direct SQL UPDATEs, leading to poorer performance.
|
||||
|
||||
### Option 2: Directly Update Relations
|
||||
|
||||
The existing PARTNER relation remains unchanged, and the Holder is switched from the deceased person to the heir community.
|
||||
|
||||
#### Advantages
|
||||
|
||||
- **Applicability to both partner and debitor persons:** This approach would work for both partner and debitor persons at a generic level.
|
||||
- **API uniformity and generality:** REST API changes would belong uniformly to the relation endpoint, consistent with how contact changes are currently handled.
|
||||
|
||||
#### Disadvantages
|
||||
|
||||
- **UPDATE permission for relation-AGENT would be problematic:** The relation-AGENT must not have permission to swap the Holder. Since there are no column-specific update rights, they would also lose the ability to change the Contact.
|
||||
- **API remodelling:** The exchange of a partner person would move from the partner endpoint (/api/hs/office/partner) to the relation endpoint, requiring significant restructuring, including tests.
|
||||
- **Mismatch between business logic and API:** Although conceptually it involves replacing a partner person, technically, the change would occur at the PARTNER relation.
|
||||
|
||||
### Option 3: Update Relations via Partner
|
||||
|
||||
The partner (or debitor) person would still be updated at the partner or debitor level, but instead of creating a new relation, the reference to the person would be updated in the existing PARTNER (or DEBITOR) relation. Dependent relations could be updated efficiently via SQL UPDATE.
|
||||
|
||||
#### Advantages
|
||||
|
||||
- **Preserving the API:** The endpoint /api/hs/office/partners/UUID remains unchanged, requiring only internal adjustments.
|
||||
- **UPDATE permission for AGENT:** AGENT roles could be granted UPDATE rights, while API controls could limit modifications.
|
||||
- **Congruence of business logic and API:** The technical implementation matches the conceptual model of replacing a partner person.
|
||||
|
||||
#### Disadvantages
|
||||
|
||||
No significant drawbacks were identified, other than allowing UPDATE permissions on relations while controlling updates at the API level.
|
||||
|
||||
## Decision and Outcome
|
||||
|
||||
**Decision:** 3. Update Relations via Partner
|
||||
|
||||
**Rationale:**
|
||||
- The API accurately reflects the business logic (PATCH partner person on /api/hs/office/partners/UUID)
|
||||
- The effort required is relatively low (many updates can be done via SQL UPDATEs)
|
||||
- UPDATE permission can be granted to relation-AGENT without security risks (since the API controls access)
|
||||
|
||||
| Criteria \ Relations ... | 1. Replace | 2. Directly Update | 3. Update via Partner |
|
||||
|------------------------------------------------|-----------:|-------------------:|----------------------:|
|
||||
| **Technical and Effort Criteria** | | | |
|
||||
| Preserve API vs. Remodelling (incl. risk) | +2 | -2 | +1 |
|
||||
| Applicability to Partner and Debitor Person | | +1 | |
|
||||
| Applicability to dependent relations | -3 | | |
|
||||
| Performance with many dependent relations | -1 | | |
|
||||
| Effort for explicit grants | -1 | | |
|
||||
| **Intermediate Score** | **-3** | **-1** | **+1** |
|
||||
| | | | |
|
||||
| **Business Criteria** | | | |
|
||||
| Congruence of Business Logic and API | +1 | -1 | +1 |
|
||||
| Uniformity/Generality of the API | | +1 | |
|
||||
| UPDATE Permission for Relation-AGENT possible | +1 | | +1 |
|
||||
| **Intermediate Score** | **+2** | **0** | **+2** |
|
||||
| | | | |
|
||||
| **Final Score** | **-1** | **-1** | **+3** |
|
||||
|
||||
|
@ -108,6 +108,40 @@ der Person des _Subscriber-Contact_ (_Holder_) zur repräsentierten Person (_Anc
|
||||
Zusätzlich wird diese Relation mit dem Kurznamen der abonnierten Mailingliste markiert.
|
||||
|
||||
|
||||
### Coop-Asset-Transactions (Geschäftsguthabens-Transaktionen)
|
||||
|
||||
- positiver Wert => Geschäftsguthaben nehmen zu
|
||||
- negativer Wert => Geschäftsguthaben nehmen ab
|
||||
|
||||
**REVERSAL**: **Korrekturbuchung** einer fehlerhaften Buchung, positiver oder negativer Wert ist möglich
|
||||
|
||||
**DEPOSIT**: **Zahlungseingang** vom Mitglied nach Beteiligung mit Geschäftsanteilen, immer positiver Wert
|
||||
|
||||
**DISBURSAL**: **Zahlungsausgang** an Mitglied nach Kündigung von Geschäftsanteilen, immer negativer Wert
|
||||
|
||||
**TRANSFER**: **Übertragung** von Geschäftsguthaben an ein anderes Mitglied, immer negativer Wert
|
||||
|
||||
**ADOPTION**: **Übernahme** von Geschäftsguthaben von einem anderen Mitglied, immer positiver Wert
|
||||
|
||||
**CLEARING**: **Verrechnung** von Geschäftsguthaben mit Schulden des Mitglieds, immer negativer Wert
|
||||
|
||||
**LOSS**: **Verlust** von Geschäftsguthaben bei Zuweisung Eigenkapitalverlust nach Kündigung von Geschäftsanteilen, immer negativer Wert
|
||||
|
||||
**LIMITATION**: **Verjährung** von Geschäftsguthaben, wenn Auszahlung innerhalb der Frist nicht möglich war.
|
||||
|
||||
|
||||
### Coop-Share-Transactions (Geschäftsanteil-Transaktionen)
|
||||
|
||||
- positiver Wert => Geschäftsanteile nehmen zu
|
||||
- negativer Wert => Geschäftsanteile nehmen ab
|
||||
-
|
||||
**REVERSAL**: **Korrekturbuchung** einer fehlerhaften Buchung, positiver oder negativer Wert ist möglich
|
||||
|
||||
**SUBSCRIPTION**: **Beteiligung** mit Geschäftsanteilen, z.B. durch Beitrittserklärung, immer positiver Wert
|
||||
|
||||
**CANCELLATION**: **Kündigung** von Geschäftsanteilen, z.B. durch Austritt, immer negativer Wert
|
||||
|
||||
|
||||
#### Anchor / Relation-Anchor
|
||||
|
||||
siehe [Relation](#Relation)
|
||||
|
@ -116,7 +116,7 @@ classDiagram
|
||||
+BankAccount refundBankAccount
|
||||
+String defaultPrefix: mei
|
||||
}
|
||||
debitor-MeierGmbH o-- partner-MeierGmbH
|
||||
debitor-MeierGmbH o.. partner-MeierGmbH
|
||||
debitor-MeierGmbH *-- rel-MeierGmbH-Buha
|
||||
|
||||
class contactData-MeierGmbH-Buha {
|
||||
|
@ -1,9 +1,11 @@
|
||||
package net.hostsharing.hsadminng;
|
||||
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@OpenAPIDefinition
|
||||
public class HsadminNgApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -1,39 +0,0 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class AuthenticationFilter implements Filter {
|
||||
|
||||
@Autowired
|
||||
private Authenticator authenticator;
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) {
|
||||
final var httpRequest = (HttpServletRequest) request;
|
||||
final var httpResponse = (HttpServletResponse) response;
|
||||
|
||||
try {
|
||||
final var currentSubject = authenticator.authenticate(httpRequest);
|
||||
|
||||
final var authenticatedRequest = new AuthenticatedHttpServletRequestWrapper(httpRequest);
|
||||
authenticatedRequest.addHeader("current-subject", currentSubject);
|
||||
|
||||
chain.doFilter(authenticatedRequest, response);
|
||||
} catch (final BadCredentialsException exc) {
|
||||
// TODO.impl: should not be necessary if ResponseStatusException worked
|
||||
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
public interface Authenticator {
|
||||
|
||||
String authenticate(final HttpServletRequest httpRequest);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
// Do NOT use @Component (or similar) here, this would register the filter directly.
|
||||
// But we need to register it in the SecurityFilterChain created by WebSecurityConfig.
|
||||
// The bean gets created in net.hostsharing.hsadminng.config.WebSecurityConfig.authenticationFilter.
|
||||
@AllArgsConstructor
|
||||
public class CasAuthenticationFilter extends OncePerRequestFilter {
|
||||
|
||||
private CasAuthenticator authenticator;
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
protected void doFilterInternal(
|
||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) {
|
||||
|
||||
if (request.getHeader("authorization") != null) {
|
||||
final var authenticatedRequest = new AuthenticatedHttpServletRequestWrapper(request);
|
||||
final var currentSubject = authenticator.authenticate(request);
|
||||
final var authentication = new UsernamePasswordAuthenticationToken(currentSubject, null, null);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
filterChain.doFilter(authenticatedRequest, response);
|
||||
} else {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,71 +1,8 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class CasAuthenticator implements Authenticator {
|
||||
public interface CasAuthenticator {
|
||||
|
||||
@Value("${hsadminng.cas.server}")
|
||||
private String casServerUrl;
|
||||
|
||||
@Value("${hsadminng.cas.service}")
|
||||
private String serviceUrl;
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@SneakyThrows
|
||||
@Timed("app.cas.authenticate")
|
||||
public String authenticate(final HttpServletRequest httpRequest) {
|
||||
final var userName = StringUtils.isBlank(casServerUrl)
|
||||
? bypassCurrentSubject(httpRequest)
|
||||
: casValidation(httpRequest);
|
||||
final var authentication = new UsernamePasswordAuthenticationToken(userName, null, null);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
return authentication.getName();
|
||||
}
|
||||
|
||||
private static String bypassCurrentSubject(final HttpServletRequest httpRequest) {
|
||||
final var userName = httpRequest.getHeader("current-subject");
|
||||
System.err.println("CasAuthenticator.bypassCurrentSubject: " + userName);
|
||||
return userName;
|
||||
}
|
||||
|
||||
private String casValidation(final HttpServletRequest httpRequest)
|
||||
throws SAXException, IOException, ParserConfigurationException {
|
||||
|
||||
final var ticket = httpRequest.getHeader("Authorization");
|
||||
final var url = casServerUrl + "/p3/serviceValidate" +
|
||||
"?service=" + serviceUrl +
|
||||
"&ticket=" + ticket;
|
||||
|
||||
System.err.println("CasAuthenticator.casValidation using URL: " + url);
|
||||
|
||||
final var response = restTemplate.getForObject(url, String.class);
|
||||
|
||||
final var doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
.parse(new java.io.ByteArrayInputStream(response.getBytes()));
|
||||
if (doc.getElementsByTagName("cas:authenticationSuccess").getLength() == 0) {
|
||||
// TODO.impl: for unknown reasons, this results in a 403 FORBIDDEN
|
||||
// throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "CAS service ticket could not be validated");
|
||||
System.err.println("CAS service ticket could not be validated");
|
||||
System.err.println("CAS-validation-URL: " + url);
|
||||
System.err.println(response);
|
||||
throw new BadCredentialsException("CAS service ticket could not be validated");
|
||||
}
|
||||
final var userName = doc.getElementsByTagName("cas:user").item(0).getTextContent();
|
||||
System.err.println("CAS-user: " + userName);
|
||||
return userName;
|
||||
}
|
||||
String authenticate(final HttpServletRequest httpRequest);
|
||||
}
|
||||
|
@ -4,11 +4,11 @@ import lombok.SneakyThrows;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
public class FakeAuthenticator implements Authenticator {
|
||||
public class FakeCasAuthenticator implements CasAuthenticator {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public String authenticate(final HttpServletRequest httpRequest) {
|
||||
return httpRequest.getHeader("current-subject");
|
||||
return httpRequest.getHeader("Authorization").replaceAll("^Bearer ", "");
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.TYPE;
|
||||
|
||||
/** Explicitly marks a REST-Controller for not requiring authorization for Swagger UI.
|
||||
*
|
||||
* @see SecurityRequirement
|
||||
*/
|
||||
@Target(TYPE)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
public @interface NoSecurityRequirement {
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
|
||||
// HOWTO add logger
|
||||
@Slf4j
|
||||
public class RealCasAuthenticator implements CasAuthenticator {
|
||||
|
||||
@Value("${hsadminng.cas.server}")
|
||||
private String casServerUrl;
|
||||
|
||||
@Value("${hsadminng.cas.service}")
|
||||
private String serviceUrl;
|
||||
|
||||
private final RestTemplate restTemplate = new RestTemplate();
|
||||
|
||||
@SneakyThrows
|
||||
@Timed("app.cas.authenticate")
|
||||
public String authenticate(final HttpServletRequest httpRequest) {
|
||||
final var ticket = httpRequest.getHeader("authorization").replaceAll("^Bearer ", "");
|
||||
final var serviceTicket = ticket.startsWith("TGT-")
|
||||
? fetchServiceTicket(ticket)
|
||||
: ticket;
|
||||
final var userName = extractUserName(verifyServiceTicket(serviceTicket));
|
||||
// HOWTO log some message for a certain log level (trace, debug, info, warn, error)
|
||||
log.debug("CAS-user: {}", userName);
|
||||
return userName;
|
||||
}
|
||||
|
||||
private String fetchServiceTicket(final String ticketGrantingTicket) {
|
||||
final var tgtUrl = casServerUrl + "/cas/v1/tickets/" + ticketGrantingTicket;
|
||||
|
||||
final var restTemplate = new RestTemplate();
|
||||
final var formData = new LinkedMultiValueMap<String, String>();
|
||||
formData.add("service", serviceUrl);
|
||||
|
||||
return restTemplate.postForObject(tgtUrl, formData, String.class);
|
||||
}
|
||||
|
||||
private Document verifyServiceTicket(final String serviceTicket) throws SAXException, IOException, ParserConfigurationException {
|
||||
if ( !serviceTicket.startsWith("ST-") ) {
|
||||
throwBadCredentialsException("Invalid authorization ticket");
|
||||
}
|
||||
|
||||
final var url = casServerUrl + "/cas/p3/serviceValidate" +
|
||||
"?service=" + serviceUrl +
|
||||
"&ticket=" + serviceTicket;
|
||||
|
||||
final var response = restTemplate.getForObject(url, String.class);
|
||||
|
||||
return DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
.parse(new java.io.ByteArrayInputStream(response.getBytes()));
|
||||
|
||||
}
|
||||
|
||||
private String extractUserName(final Document verification) {
|
||||
|
||||
if (verification.getElementsByTagName("cas:authenticationSuccess").getLength() == 0) {
|
||||
throwBadCredentialsException("CAS service ticket could not be verified");
|
||||
}
|
||||
return verification.getElementsByTagName("cas:user").item(0).getTextContent();
|
||||
}
|
||||
|
||||
private void throwBadCredentialsException(final String message) {
|
||||
throw new BadCredentialsException(message);
|
||||
}
|
||||
}
|
@ -1,36 +1,71 @@
|
||||
package net.hostsharing.hsadminng.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
|
||||
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityScheme;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationFilter;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
// TODO.impl: securitySchemes should work in OpenAPI yaml, but the Spring templates seem not to support it
|
||||
@SecurityScheme(type = SecuritySchemeType.HTTP, name = "casTicket", scheme = "bearer", bearerFormat = "CAS ticket", description = "CAS ticket", in = SecuritySchemeIn.HEADER)
|
||||
public class WebSecurityConfig {
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private CasAuthenticationFilter authenticationFilter;
|
||||
|
||||
@Bean
|
||||
@Profile("!test")
|
||||
public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception {
|
||||
return http
|
||||
.authorizeHttpRequests(authorize -> authorize
|
||||
.requestMatchers("/api/**").permitAll() // TODO.impl: implement authentication
|
||||
.requestMatchers("/swagger-ui/**").permitAll()
|
||||
.requestMatchers("/v3/api-docs/**").permitAll()
|
||||
.requestMatchers("/actuator/**").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
.requestMatchers(
|
||||
"/swagger-ui/**",
|
||||
"/v3/api-docs/**",
|
||||
"/actuator/**",
|
||||
"/api/hs/hosting/asset-types/**"
|
||||
).permitAll()
|
||||
.requestMatchers("/api/**").authenticated()
|
||||
.anyRequest().denyAll()
|
||||
)
|
||||
.addFilterBefore(authenticationFilter, AuthenticationFilter.class)
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.exceptionHandling(exception -> exception
|
||||
.authenticationEntryPoint((request, response, authException) ->
|
||||
// For unknown reasons Spring security returns 403 FORBIDDEN for a BadCredentialsException.
|
||||
// But it should return 401 UNAUTHORIZED.
|
||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED)
|
||||
)
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Profile("!test")
|
||||
public Authenticator casServiceTicketValidator() {
|
||||
return new CasAuthenticator();
|
||||
@Profile("realCasAuthenticator")
|
||||
public CasAuthenticator realCasServiceTicketValidator() {
|
||||
return new RealCasAuthenticator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Profile("fakeCasAuthenticator")
|
||||
public CasAuthenticator fakeCasServiceTicketValidator() {
|
||||
return new FakeCasAuthenticator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CasAuthenticationFilter authenticationFilter(final CasAuthenticator authenticator) {
|
||||
return new CasAuthenticationFilter(authenticator);
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
@ -47,6 +48,12 @@ public class Context {
|
||||
define(toTask(request), toCurl(request), currentSubject, assumedRoles);
|
||||
}
|
||||
|
||||
@Transactional(propagation = MANDATORY)
|
||||
public void assumeRoles(final String assumedRoles) {
|
||||
final var currentSubject = SecurityContextHolder.getContext().getAuthentication().getName();
|
||||
define(toTask(request), toCurl(request), currentSubject, assumedRoles);
|
||||
}
|
||||
|
||||
@Transactional(propagation = MANDATORY)
|
||||
public void define(
|
||||
final String currentTask,
|
||||
|
@ -21,12 +21,12 @@ public class HttpServletRequestWithCachedBody extends HttpServletRequestWrapper
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
public ServletInputStream getInputStream() {
|
||||
return new HttpServletRequestBodyCache(this.cachedBody);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader() throws IOException {
|
||||
public BufferedReader getReader() {
|
||||
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
|
||||
return new BufferedReader(new InputStreamReader(byteArrayInputStream));
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import java.util.regex.Pattern;
|
||||
import static net.hostsharing.hsadminng.errors.CustomErrorResponse.*;
|
||||
|
||||
@ControllerAdvice
|
||||
// HOWTO handle exceptions to produce specific http error codes and sensible error messages
|
||||
public class RestResponseEntityExceptionHandler
|
||||
extends ResponseEntityExceptionHandler {
|
||||
|
||||
|
@ -121,10 +121,4 @@ public final class HashGenerator {
|
||||
}
|
||||
return withSalt(stringBuilder.toString());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(
|
||||
HashGenerator.using(Algorithm.LINUX_YESCRYPT).withRandomSalt().hash("my plaintext domain transfer passphrase")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.booking.item;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingItemsApi;
|
||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.model.HsBookingItemInsertResource;
|
||||
@ -32,6 +33,7 @@ import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateR
|
||||
|
||||
@RestController
|
||||
@Profile("!only-office")
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsBookingItemController implements HsBookingItemsApi {
|
||||
|
||||
@Autowired
|
||||
@ -56,10 +58,9 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.bookingItems.api.getListOfBookingItemsByProjectUuid")
|
||||
public ResponseEntity<List<HsBookingItemResource>> getListOfBookingItemsByProjectUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID projectUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = bookingItemRepo.findAllByProjectUuid(projectUuid);
|
||||
|
||||
@ -71,11 +72,10 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
||||
@Transactional
|
||||
@Timed("app.bookingItems.api.postNewBookingItem")
|
||||
public ResponseEntity<HsBookingItemResource> postNewBookingItem(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsBookingItemInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entityToSave = mapper.map(body, HsBookingItemRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||
final var saveProcessor = new BookingItemEntitySaveProcessor(em, entityToSave);
|
||||
@ -101,11 +101,10 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.bookingItems.api.getSingleBookingItemByUuid")
|
||||
public ResponseEntity<HsBookingItemResource> getSingleBookingItemByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID bookingItemUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = bookingItemRepo.findByUuid(bookingItemUuid);
|
||||
result.ifPresent(entity -> em.detach(entity)); // prevent further LAZY-loading
|
||||
@ -119,10 +118,9 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
||||
@Transactional
|
||||
@Timed("app.bookingItems.api.deleteBookingIemByUuid")
|
||||
public ResponseEntity<Void> deleteBookingIemByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID bookingItemUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = bookingItemRepo.deleteByUuid(bookingItemUuid);
|
||||
return result == 0
|
||||
@ -134,12 +132,11 @@ public class HsBookingItemController implements HsBookingItemsApi {
|
||||
@Transactional
|
||||
@Timed("app.bookingItems.api.patchBookingItem")
|
||||
public ResponseEntity<HsBookingItemResource> patchBookingItem(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID bookingItemUuid,
|
||||
final HsBookingItemPatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = bookingItemRepo.findByUuid(bookingItemUuid).orElseThrow();
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.booking.project;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.booking.debitor.HsBookingDebitorRepository;
|
||||
import net.hostsharing.hsadminng.hs.booking.generated.api.v1.api.HsBookingProjectsApi;
|
||||
@ -22,6 +23,7 @@ import java.util.function.BiConsumer;
|
||||
|
||||
@RestController
|
||||
@Profile("!only-office")
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsBookingProjectController implements HsBookingProjectsApi {
|
||||
|
||||
@Autowired
|
||||
@ -40,10 +42,9 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.bookingProjects.api.getListOfBookingProjectsByDebitorUuid")
|
||||
public ResponseEntity<List<HsBookingProjectResource>> getListOfBookingProjectsByDebitorUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID debitorUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = bookingProjectRepo.findAllByDebitorUuid(debitorUuid);
|
||||
|
||||
@ -55,11 +56,10 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
|
||||
@Transactional
|
||||
@Timed("app.bookingProjects.api.postNewBookingProject")
|
||||
public ResponseEntity<HsBookingProjectResource> postNewBookingProject(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsBookingProjectInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entityToSave = mapper.map(body, HsBookingProjectRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||
|
||||
@ -78,11 +78,10 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.bookingProjects.api.getBookingProjectByUuid")
|
||||
public ResponseEntity<HsBookingProjectResource> getBookingProjectByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID bookingProjectUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = bookingProjectRepo.findByUuid(bookingProjectUuid);
|
||||
return result
|
||||
@ -95,10 +94,9 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
|
||||
@Transactional
|
||||
@Timed("app.bookingProjects.api.deleteBookingIemByUuid")
|
||||
public ResponseEntity<Void> deleteBookingIemByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID bookingProjectUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = bookingProjectRepo.deleteByUuid(bookingProjectUuid);
|
||||
return result == 0
|
||||
@ -110,12 +108,11 @@ public class HsBookingProjectController implements HsBookingProjectsApi {
|
||||
@Transactional
|
||||
@Timed("app.bookingProjects.api.patchBookingProject")
|
||||
public ResponseEntity<HsBookingProjectResource> patchBookingProject(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID bookingProjectUuid,
|
||||
final HsBookingProjectPatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = bookingProjectRepo.findByUuid(bookingProjectUuid).orElseThrow();
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.hosting.asset;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRealRepository;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntitySaveProcessor;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry;
|
||||
@ -29,6 +30,7 @@ import java.util.function.BiConsumer;
|
||||
|
||||
@RestController
|
||||
@Profile("!only-office")
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsHostingAssetController implements HsHostingAssetsApi {
|
||||
|
||||
@Autowired
|
||||
@ -53,12 +55,11 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.hosting.assets.api.getListOfHostingAssets")
|
||||
public ResponseEntity<List<HsHostingAssetResource>> getListOfHostingAssets(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID debitorUuid,
|
||||
final UUID parentAssetUuid,
|
||||
final HsHostingAssetTypeResource type) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = rbacAssetRepo.findAllByCriteria(debitorUuid, parentAssetUuid, HsHostingAssetType.of(type));
|
||||
|
||||
@ -71,11 +72,10 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
||||
@Transactional
|
||||
@Timed("app.hosting.assets.api.postNewHostingAsset")
|
||||
public ResponseEntity<HsHostingAssetResource> postNewHostingAsset(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsHostingAssetInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entity = mapper.map(body, HsHostingAssetRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||
|
||||
@ -100,11 +100,10 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.hosting.assets.api.getSingleHostingAssetByUuid")
|
||||
public ResponseEntity<HsHostingAssetResource> getSingleHostingAssetByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID assetUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = rbacAssetRepo.findByUuid(assetUuid);
|
||||
return result
|
||||
@ -117,10 +116,9 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
||||
@Transactional
|
||||
@Timed("app.hosting.assets.api.deleteHostingAssetByUuid")
|
||||
public ResponseEntity<Void> deleteHostingAssetByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID assetUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = rbacAssetRepo.deleteByUuid(assetUuid);
|
||||
return result == 0
|
||||
@ -132,12 +130,11 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
||||
@Transactional
|
||||
@Timed("app.hosting.assets.api.patchHostingAsset")
|
||||
public ResponseEntity<HsHostingAssetResource> patchHostingAsset(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID assetUuid,
|
||||
final HsHostingAssetPatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entity = rbacAssetRepo.findByUuid(assetUuid).orElseThrow();
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.hosting.asset;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import net.hostsharing.hsadminng.config.NoSecurityRequirement;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.validators.HostingAssetEntityValidatorRegistry;
|
||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.api.HsHostingAssetPropsApi;
|
||||
import net.hostsharing.hsadminng.hs.hosting.generated.api.v1.model.HsHostingAssetTypeResource;
|
||||
@ -14,6 +15,7 @@ import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@Profile("!only-office")
|
||||
@NoSecurityRequirement
|
||||
public class HsHostingAssetPropsController implements HsHostingAssetPropsApi {
|
||||
|
||||
@Override
|
||||
|
@ -125,10 +125,4 @@ public class Dns {
|
||||
return Result.fromException(exception);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
final var result = new Dns("example.org").fetchRecordsOfType("TXT");
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.bankaccount;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeBankAccountsApi;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeBankAccountInsertResource;
|
||||
@ -18,7 +19,7 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
|
||||
|
||||
@Autowired
|
||||
@ -34,10 +35,9 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.bankAccounts.api.patchDebitor")
|
||||
public ResponseEntity<List<HsOfficeBankAccountResource>> getListOfBankAccounts(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final String holder) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = bankAccountRepo.findByOptionalHolderLike(holder);
|
||||
|
||||
@ -49,11 +49,10 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.bankAccounts.api.postNewBankAccount")
|
||||
public ResponseEntity<HsOfficeBankAccountResource> postNewBankAccount(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeBankAccountInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
IbanUtil.validate(body.getIban());
|
||||
BicUtil.validate(body.getBic());
|
||||
@ -76,11 +75,10 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.bankAccounts.api.getSingleBankAccountByUuid")
|
||||
public ResponseEntity<HsOfficeBankAccountResource> getSingleBankAccountByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID bankAccountUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = bankAccountRepo.findByUuid(bankAccountUuid);
|
||||
if (result.isEmpty()) {
|
||||
@ -93,10 +91,9 @@ public class HsOfficeBankAccountController implements HsOfficeBankAccountsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.bankAccounts.api.deleteBankAccountByUuid")
|
||||
public ResponseEntity<Void> deleteBankAccountByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID BankAccountUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = bankAccountRepo.deleteByUuid(BankAccountUuid);
|
||||
if (result == 0) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.contact;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeContactsApi;
|
||||
@ -13,15 +14,14 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static net.hostsharing.hsadminng.errors.Validate.validate;
|
||||
import static net.hostsharing.hsadminng.mapper.KeyValueMap.from;
|
||||
|
||||
@RestController
|
||||
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficeContactController implements HsOfficeContactsApi {
|
||||
|
||||
@Autowired
|
||||
@ -30,18 +30,28 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
||||
@Autowired
|
||||
private StrictMapper mapper;
|
||||
|
||||
@Autowired
|
||||
private HsOfficeContactFromResourceConverter<HsOfficeContactRbacEntity> contactFromResourceConverter;
|
||||
|
||||
@Autowired
|
||||
private HsOfficeContactRbacRepository contactRepo;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// HOWTO: add a ModelMapper converter for a generic entity class to a ModelMapper to be used in a certain context
|
||||
// This @PostConstruct could be implemented in the converter, but only without generics.
|
||||
// But this converter is for HsOfficeContactRbacEntity and HsOfficeContactRealEntity.
|
||||
mapper.addConverter(contactFromResourceConverter, HsOfficeContactInsertResource.class, HsOfficeContactRbacEntity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.contacts.api.getListOfContacts")
|
||||
public ResponseEntity<List<HsOfficeContactResource>> getListOfContacts(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final String caption,
|
||||
final String emailAddress) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
validate("caption, emailAddress").atMaxOne(caption, emailAddress);
|
||||
final var entities = emailAddress != null
|
||||
@ -56,13 +66,12 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.contacts.api.postNewContact")
|
||||
public ResponseEntity<HsOfficeContactResource> postNewContact(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeContactInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entityToSave = mapper.map(body, HsOfficeContactRbacEntity.class, RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||
final var entityToSave = mapper.map(body, HsOfficeContactRbacEntity.class);
|
||||
|
||||
final var saved = contactRepo.save(entityToSave);
|
||||
|
||||
@ -79,11 +88,10 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.contacts.api.getSingleContactByUuid")
|
||||
public ResponseEntity<HsOfficeContactResource> getSingleContactByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID contactUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = contactRepo.findByUuid(contactUuid);
|
||||
if (result.isEmpty()) {
|
||||
@ -96,10 +104,9 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.contacts.api.deleteContactByUuid")
|
||||
public ResponseEntity<Void> deleteContactByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID contactUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = contactRepo.deleteByUuid(contactUuid);
|
||||
if (result == 0) {
|
||||
@ -113,12 +120,11 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.contacts.api.patchContact")
|
||||
public ResponseEntity<HsOfficeContactResource> patchContact(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID contactUuid,
|
||||
final HsOfficeContactPatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = contactRepo.findByUuid(contactUuid).orElseThrow();
|
||||
|
||||
@ -128,11 +134,4 @@ public class HsOfficeContactController implements HsOfficeContactsApi {
|
||||
final var mapped = mapper.map(saved, HsOfficeContactResource.class);
|
||||
return ResponseEntity.ok(mapped);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final BiConsumer<HsOfficeContactInsertResource, HsOfficeContactRbacEntity> RESOURCE_TO_ENTITY_POSTMAPPER = (resource, entity) -> {
|
||||
entity.putPostalAddress(from(resource.getPostalAddress()));
|
||||
entity.putEmailAddresses(from(resource.getEmailAddresses()));
|
||||
entity.putPhoneNumbers(from(resource.getPhoneNumbers()));
|
||||
};
|
||||
}
|
||||
|
@ -0,0 +1,27 @@
|
||||
package net.hostsharing.hsadminng.hs.office.contact;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactInsertResource;
|
||||
import org.modelmapper.Converter;
|
||||
import org.modelmapper.spi.MappingContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import static net.hostsharing.hsadminng.mapper.KeyValueMap.from;
|
||||
|
||||
// HOWTO: implement a ModelMapper converter which converts from a (JSON) resource instance to a generic entity instance (RBAC vs. REAL)
|
||||
@Component
|
||||
public class HsOfficeContactFromResourceConverter<E extends HsOfficeContact>
|
||||
implements Converter<HsOfficeContactInsertResource, E> {
|
||||
|
||||
@Override
|
||||
@SneakyThrows
|
||||
public E convert(final MappingContext<HsOfficeContactInsertResource, E> context) {
|
||||
final var resource = context.getSource();
|
||||
final var entity = context.getDestinationType().getDeclaredConstructor().newInstance();
|
||||
entity.setCaption(resource.getCaption());
|
||||
entity.putPostalAddress(from(resource.getPostalAddress()));
|
||||
entity.putEmailAddresses(from(resource.getEmailAddresses()));
|
||||
entity.putPhoneNumbers(from(resource.getPhoneNumbers()));
|
||||
return entity;
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.coopassets;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopAssetsApi;
|
||||
@ -37,6 +38,7 @@ import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOffic
|
||||
import static net.hostsharing.hsadminng.lambda.WithNonNull.withNonNull;
|
||||
|
||||
@RestController
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAssetsApi {
|
||||
|
||||
@Autowired
|
||||
@ -58,12 +60,11 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.coopAssets.api.getListOfCoopAssets")
|
||||
public ResponseEntity<List<HsOfficeCoopAssetsTransactionResource>> getListOfCoopAssets(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID membershipUuid,
|
||||
final @DateTimeFormat(iso = ISO.DATE) LocalDate fromValueDate,
|
||||
final @DateTimeFormat(iso = ISO.DATE) LocalDate toValueDate) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = coopAssetsTransactionRepo.findCoopAssetsTransactionByOptionalMembershipUuidAndDateRange(
|
||||
membershipUuid,
|
||||
@ -81,11 +82,10 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
@Transactional
|
||||
@Timed("app.office.coopAssets.api.postNewCoopAssetTransaction")
|
||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> postNewCoopAssetTransaction(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeCoopAssetsTransactionInsertResource requestBody) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
validate(requestBody);
|
||||
|
||||
final var entityToSave = mapper.map(
|
||||
@ -107,9 +107,9 @@ public class HsOfficeCoopAssetsTransactionController implements HsOfficeCoopAsse
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.coopAssets.api.getSingleCoopAssetTransactionByUuid")
|
||||
public ResponseEntity<HsOfficeCoopAssetsTransactionResource> getSingleCoopAssetTransactionByUuid(
|
||||
final String currentSubject, final String assumedRoles, final UUID assetTransactionUuid) {
|
||||
final String assumedRoles, final UUID assetTransactionUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = coopAssetsTransactionRepo.findByUuid(assetTransactionUuid);
|
||||
if (result.isEmpty()) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.coopshares;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.errors.MultiValidationException;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeCoopSharesApi;
|
||||
@ -27,6 +28,7 @@ import static net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOffic
|
||||
import static net.hostsharing.hsadminng.hs.validation.UuidResolver.resolve;
|
||||
|
||||
@RestController
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopSharesApi {
|
||||
|
||||
@Autowired
|
||||
@ -45,12 +47,11 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.coopShares.api.getListOfCoopShares")
|
||||
public ResponseEntity<List<HsOfficeCoopSharesTransactionResource>> getListOfCoopShares(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID membershipUuid,
|
||||
final @DateTimeFormat(iso = ISO.DATE) LocalDate fromValueDate,
|
||||
final @DateTimeFormat(iso = ISO.DATE) LocalDate toValueDate) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = coopSharesTransactionRepo.findCoopSharesTransactionByOptionalMembershipUuidAndDateRange(
|
||||
membershipUuid,
|
||||
@ -68,11 +69,10 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
||||
@Transactional
|
||||
@Timed("app.office.coopShares.repo.postNewCoopSharesTransaction")
|
||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> postNewCoopSharesTransaction(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeCoopSharesTransactionInsertResource requestBody) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
validate(requestBody);
|
||||
|
||||
final var entityToSave = mapper.map(
|
||||
@ -95,9 +95,9 @@ public class HsOfficeCoopSharesTransactionController implements HsOfficeCoopShar
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.coopShares.repo.getSingleCoopShareTransactionByUuid")
|
||||
public ResponseEntity<HsOfficeCoopSharesTransactionResource> getSingleCoopShareTransactionByUuid(
|
||||
final String currentSubject, final String assumedRoles, final UUID shareTransactionUuid) {
|
||||
final String assumedRoles, final UUID shareTransactionUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = coopSharesTransactionRepo.findByUuid(shareTransactionUuid);
|
||||
if (result.isEmpty()) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.debitor;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealRepository;
|
||||
@ -32,7 +33,7 @@ import static net.hostsharing.hsadminng.hs.validation.UuidResolver.resolve;
|
||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
||||
|
||||
@RestController
|
||||
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
|
||||
@Autowired
|
||||
@ -63,12 +64,11 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.debitors.api.getListOfDebitors")
|
||||
public ResponseEntity<List<HsOfficeDebitorResource>> getListOfDebitors(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final String name,
|
||||
final UUID partnerUuid,
|
||||
final String partnerNumber) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = partnerNumber != null
|
||||
? debitorRepo.findDebitorsByPartnerNumber(cropTag("P-", partnerNumber))
|
||||
@ -84,11 +84,10 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.debitors.api.postNewDebitor")
|
||||
public ResponseEntity<HsOfficeDebitorResource> postNewDebitor(
|
||||
String currentSubject,
|
||||
String assumedRoles,
|
||||
HsOfficeDebitorInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
Validate.isTrue(
|
||||
body.getDebitorRel() == null || body.getDebitorRelUuid() == null,
|
||||
@ -117,11 +116,10 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.debitors.api.getSingleDebitorByUuid")
|
||||
public ResponseEntity<HsOfficeDebitorResource> getSingleDebitorByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID debitorUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = debitorRepo.findByUuid(debitorUuid);
|
||||
if (result.isEmpty()) {
|
||||
@ -134,11 +132,10 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.debitors.api.getSingleDebitorByDebitorNumber")
|
||||
public ResponseEntity<HsOfficeDebitorResource> getSingleDebitorByDebitorNumber(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final Integer debitorNumber) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = debitorRepo.findDebitorByDebitorNumber(debitorNumber);
|
||||
if (result.isEmpty()) {
|
||||
@ -151,10 +148,9 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.debitors.api.deleteDebitorByUuid")
|
||||
public ResponseEntity<Void> deleteDebitorByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID debitorUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = debitorRepo.deleteByUuid(debitorUuid);
|
||||
if (result == 0) {
|
||||
@ -168,12 +164,11 @@ public class HsOfficeDebitorController implements HsOfficeDebitorsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.debitors.api.patchDebitor")
|
||||
public ResponseEntity<HsOfficeDebitorResource> patchDebitor(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID debitorUuid,
|
||||
final HsOfficeDebitorPatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = debitorRepo.findByUuid(debitorUuid).orElseThrow().reload(em);
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.membership;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficeMembershipsApi;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeMembershipInsertResource;
|
||||
@ -24,6 +25,7 @@ import static net.hostsharing.hsadminng.errors.Validate.validate;
|
||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
||||
|
||||
@RestController
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
|
||||
@Autowired
|
||||
@ -42,11 +44,10 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.membership.api.getListOfMemberships")
|
||||
public ResponseEntity<List<HsOfficeMembershipResource>> getListOfMemberships(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID partnerUuid,
|
||||
final String partnerNumber) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
validate("partnerUuid, partnerNumber").atMaxOne(partnerUuid, partnerNumber);
|
||||
|
||||
@ -67,11 +68,10 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.membership.api.postNewMembership")
|
||||
public ResponseEntity<HsOfficeMembershipResource> postNewMembership(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeMembershipInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entityToSave = mapper.map(body, HsOfficeMembershipEntity.class, SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||
|
||||
@ -92,11 +92,10 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.membership.api.getSingleMembershipByUuid")
|
||||
public ResponseEntity<HsOfficeMembershipResource> getSingleMembershipByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID membershipUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = membershipRepo.findByUuid(membershipUuid);
|
||||
if (result.isEmpty()) {
|
||||
@ -111,11 +110,10 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.membership.api.getSingleMembershipByMembershipNumber")
|
||||
public ResponseEntity<HsOfficeMembershipResource> getSingleMembershipByMembershipNumber(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final Integer membershipNumber) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = membershipRepo.findMembershipByMemberNumber(membershipNumber);
|
||||
if (result.isEmpty()) {
|
||||
@ -130,10 +128,9 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.membership.api.deleteMembershipByUuid")
|
||||
public ResponseEntity<Void> deleteMembershipByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID membershipUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = membershipRepo.deleteByUuid(membershipUuid);
|
||||
if (result == 0) {
|
||||
@ -147,12 +144,11 @@ public class HsOfficeMembershipController implements HsOfficeMembershipsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.membership.api.patchMembership")
|
||||
public ResponseEntity<HsOfficeMembershipResource> patchMembership(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID membershipUuid,
|
||||
final HsOfficeMembershipPatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = membershipRepo.findByUuid(membershipUuid).orElseThrow();
|
||||
|
||||
|
@ -1,10 +1,13 @@
|
||||
package net.hostsharing.hsadminng.hs.office.partner;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.errors.ReferenceNotFoundException;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactFromResourceConverter;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePartnersApi;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeContactInsertResource;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerInsertResource;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerResource;
|
||||
@ -22,6 +25,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.PersistenceContext;
|
||||
import java.util.List;
|
||||
@ -32,7 +36,7 @@ import static net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationType.
|
||||
import static net.hostsharing.hsadminng.repr.TaggedNumber.cropTag;
|
||||
|
||||
@RestController
|
||||
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
|
||||
@Autowired
|
||||
@ -42,24 +46,31 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
private StrictMapper mapper;
|
||||
|
||||
@Autowired
|
||||
private HsOfficePartnerRbacRepository partnerRepo;
|
||||
private HsOfficeContactFromResourceConverter<HsOfficeContactRealEntity> contactFromResourceConverter;
|
||||
|
||||
@Autowired
|
||||
private HsOfficeRelationRealRepository relationRepo;
|
||||
private HsOfficePartnerRbacRepository rbacPartnerRepo;
|
||||
|
||||
@Autowired
|
||||
private HsOfficeRelationRealRepository realRelationRepo;
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
mapper.addConverter(contactFromResourceConverter, HsOfficeContactInsertResource.class, HsOfficeContactRealEntity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.partners.api.getListOfPartners")
|
||||
public ResponseEntity<List<HsOfficePartnerResource>> getListOfPartners(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final String name) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = partnerRepo.findPartnerByOptionalNameLike(name);
|
||||
final var entities = rbacPartnerRepo.findPartnerByOptionalNameLike(name);
|
||||
|
||||
final var resources = mapper.mapList(entities, HsOfficePartnerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.ok(resources);
|
||||
@ -69,15 +80,14 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
@Transactional
|
||||
@Timed("app.office.partners.api.postNewPartner")
|
||||
public ResponseEntity<HsOfficePartnerResource> postNewPartner(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficePartnerInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entityToSave = createPartnerEntity(body);
|
||||
|
||||
final var saved = partnerRepo.save(entityToSave);
|
||||
final var saved = rbacPartnerRepo.save(entityToSave);
|
||||
|
||||
final var uri =
|
||||
MvcUriComponentsBuilder.fromController(getClass())
|
||||
@ -92,13 +102,12 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.partners.api.getSinglePartnerByUuid")
|
||||
public ResponseEntity<HsOfficePartnerResource> getSinglePartnerByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID partnerUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = partnerRepo.findByUuid(partnerUuid);
|
||||
final var result = rbacPartnerRepo.findByUuid(partnerUuid);
|
||||
if (result.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
@ -110,13 +119,12 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.partners.api.getSinglePartnerByPartnerNumber")
|
||||
public ResponseEntity<HsOfficePartnerResource> getSinglePartnerByPartnerNumber(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final Integer partnerNumber) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = partnerRepo.findPartnerByPartnerNumber(partnerNumber);
|
||||
final var result = rbacPartnerRepo.findPartnerByPartnerNumber(partnerNumber);
|
||||
if (result.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
@ -128,17 +136,16 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
@Transactional
|
||||
@Timed("app.office.partners.api.deletePartnerByUuid")
|
||||
public ResponseEntity<Void> deletePartnerByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID partnerUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var partnerToDelete = partnerRepo.findByUuid(partnerUuid);
|
||||
final var partnerToDelete = rbacPartnerRepo.findByUuid(partnerUuid);
|
||||
if (partnerToDelete.isEmpty()) {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
|
||||
if (partnerRepo.deleteByUuid(partnerUuid) != 1) {
|
||||
if (rbacPartnerRepo.deleteByUuid(partnerUuid) != 1) {
|
||||
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
|
||||
}
|
||||
|
||||
@ -149,29 +156,61 @@ public class HsOfficePartnerController implements HsOfficePartnersApi {
|
||||
@Transactional
|
||||
@Timed("app.office.partners.api.patchPartner")
|
||||
public ResponseEntity<HsOfficePartnerResource> patchPartner(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID partnerUuid,
|
||||
final HsOfficePartnerPatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = partnerRepo.findByUuid(partnerUuid).orElseThrow();
|
||||
final var previousPartnerRel = current.getPartnerRel();
|
||||
final var current = rbacPartnerRepo.findByUuid(partnerUuid).orElseThrow();
|
||||
final var previousPartnerPerson = current.getPartnerRel().getHolder();
|
||||
|
||||
new HsOfficePartnerEntityPatcher(em, current).apply(body);
|
||||
new HsOfficePartnerEntityPatcher(mapper, em, current).apply(body);
|
||||
|
||||
final var saved = partnerRepo.save(current);
|
||||
optionallyCreateExPartnerRelation(saved, previousPartnerRel);
|
||||
final var saved = rbacPartnerRepo.save(current);
|
||||
optionallyCreateExPartnerRelation(saved, previousPartnerPerson);
|
||||
optionallyUpdateRelatedRelations(saved, previousPartnerPerson);
|
||||
|
||||
final var mapped = mapper.map(saved, HsOfficePartnerResource.class, ENTITY_TO_RESOURCE_POSTMAPPER);
|
||||
return ResponseEntity.ok(mapped);
|
||||
}
|
||||
|
||||
private void optionallyCreateExPartnerRelation(final HsOfficePartnerRbacEntity saved, final HsOfficeRelationRealEntity previousPartnerRel) {
|
||||
if (!saved.getPartnerRel().getUuid().equals(previousPartnerRel.getUuid())) {
|
||||
// TODO.impl: we also need to use the new partner-person as the anchor
|
||||
relationRepo.save(previousPartnerRel.toBuilder().uuid(null).type(EX_PARTNER).build());
|
||||
private void optionallyCreateExPartnerRelation(final HsOfficePartnerRbacEntity saved, final HsOfficePersonRealEntity previousPartnerPerson) {
|
||||
|
||||
final var partnerPersonHasChanged = !saved.getPartnerRel().getHolder().getUuid().equals(previousPartnerPerson.getUuid());
|
||||
if (partnerPersonHasChanged) {
|
||||
realRelationRepo.save(saved.getPartnerRel().toBuilder()
|
||||
.uuid(null)
|
||||
.type(EX_PARTNER)
|
||||
.anchor(saved.getPartnerRel().getHolder())
|
||||
.holder(previousPartnerPerson)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
|
||||
private void optionallyUpdateRelatedRelations(final HsOfficePartnerRbacEntity saved, final HsOfficePersonRealEntity previousPartnerPerson) {
|
||||
final var partnerPersonHasChanged = !saved.getPartnerRel().getHolder().getUuid().equals(previousPartnerPerson.getUuid());
|
||||
if (partnerPersonHasChanged) {
|
||||
// self-debitors of the old partner-person become self-debitors of the new partner person
|
||||
em.createNativeQuery("""
|
||||
UPDATE hs_office.relation
|
||||
SET holderUuid = :newPartnerPersonUuid
|
||||
WHERE type = 'DEBITOR' AND
|
||||
holderUuid = :oldPartnerPersonUuid AND anchorUuid = :oldPartnerPersonUuid
|
||||
""")
|
||||
.setParameter("oldPartnerPersonUuid", previousPartnerPerson.getUuid())
|
||||
.setParameter("newPartnerPersonUuid", saved.getPartnerRel().getHolder().getUuid())
|
||||
.executeUpdate();
|
||||
|
||||
// re-anchor all relations from the old partner person to the new partner persion
|
||||
em.createNativeQuery("""
|
||||
UPDATE hs_office.relation
|
||||
SET anchorUuid = :newPartnerPersonUuid
|
||||
WHERE anchorUuid = :oldPartnerPersonUuid
|
||||
""")
|
||||
.setParameter("oldPartnerPersonUuid", previousPartnerPerson.getUuid())
|
||||
.setParameter("newPartnerPersonUuid", saved.getPartnerRel().getHolder().getUuid())
|
||||
.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,35 +1,36 @@
|
||||
package net.hostsharing.hsadminng.hs.office.partner;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficePartnerPatchResource;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.relation.HsOfficeRelationPatcher;
|
||||
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
class HsOfficePartnerEntityPatcher implements EntityPatcher<HsOfficePartnerPatchResource> {
|
||||
|
||||
private final StrictMapper mapper;
|
||||
private final EntityManager em;
|
||||
private final HsOfficePartnerRbacEntity entity;
|
||||
|
||||
HsOfficePartnerEntityPatcher(
|
||||
final StrictMapper mapper,
|
||||
final EntityManager em,
|
||||
final HsOfficePartnerRbacEntity entity) {
|
||||
this.mapper = mapper;
|
||||
this.em = em;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(final HsOfficePartnerPatchResource resource) {
|
||||
OptionalFromJson.of(resource.getPartnerRelUuid()).ifPresent(newValue -> {
|
||||
verifyNotNull(newValue, "partnerRel");
|
||||
entity.setPartnerRel(em.getReference(HsOfficeRelationRealEntity.class, newValue));
|
||||
});
|
||||
|
||||
new HsOfficePartnerDetailsEntityPatcher(em, entity.getDetails()).apply(resource.getDetails());
|
||||
if (resource.getPartnerRel() != null) {
|
||||
new HsOfficeRelationPatcher(mapper, em, entity.getPartnerRel()).apply(resource.getPartnerRel());
|
||||
}
|
||||
|
||||
private void verifyNotNull(final Object newValue, final String propertyName) {
|
||||
if (newValue == null) {
|
||||
throw new IllegalArgumentException("property '" + propertyName + "' must not be null");
|
||||
if (resource.getDetails() != null) {
|
||||
new HsOfficePartnerDetailsEntityPatcher(em, entity.getDetails()).apply(resource.getDetails());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.person;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.api.HsOfficePersonsApi;
|
||||
@ -17,7 +18,7 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficePersonController implements HsOfficePersonsApi {
|
||||
|
||||
@Autowired
|
||||
@ -33,10 +34,9 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.persons.api.getListOfPersons")
|
||||
public ResponseEntity<List<HsOfficePersonResource>> getListOfPersons(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final String name) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = personRepo.findPersonByOptionalNameLike(name);
|
||||
|
||||
@ -48,11 +48,10 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.persons.api.postNewPerson")
|
||||
public ResponseEntity<HsOfficePersonResource> postNewPerson(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficePersonInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entityToSave = mapper.map(body, HsOfficePersonRbacEntity.class);
|
||||
|
||||
@ -71,11 +70,10 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.persons.api.getSinglePersonByUuid")
|
||||
public ResponseEntity<HsOfficePersonResource> getSinglePersonByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID personUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = personRepo.findByUuid(personUuid);
|
||||
if (result.isEmpty()) {
|
||||
@ -88,10 +86,9 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.persons.api.deletePersonByUuid")
|
||||
public ResponseEntity<Void> deletePersonByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID personUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = personRepo.deleteByUuid(personUuid);
|
||||
if (result == 0) {
|
||||
@ -105,12 +102,11 @@ public class HsOfficePersonController implements HsOfficePersonsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.persons.api.patchPerson")
|
||||
public ResponseEntity<HsOfficePersonResource> patchPerson(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID personUuid,
|
||||
final HsOfficePersonPatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = personRepo.findByUuid(personUuid).orElseThrow();
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.relation;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.errors.Validate;
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||
@ -26,6 +27,7 @@ import java.util.function.BiConsumer;
|
||||
import static net.hostsharing.hsadminng.mapper.KeyValueMap.from;
|
||||
|
||||
@RestController
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
||||
|
||||
@Autowired
|
||||
@ -50,14 +52,13 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.relations.api.getListOfRelations")
|
||||
public ResponseEntity<List<HsOfficeRelationResource>> getListOfRelations(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID personUuid,
|
||||
final HsOfficeRelationTypeResource relationType,
|
||||
final String mark,
|
||||
final String personData,
|
||||
final String contactData) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final List<HsOfficeRelationRbacEntity> entities =
|
||||
rbacRelationRepo.findRelationRelatedToPersonUuidRelationTypeMarkPersonAndContactData(
|
||||
@ -74,11 +75,10 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.relations.api.postNewRelation")
|
||||
public ResponseEntity<HsOfficeRelationResource> postNewRelation(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeRelationInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entityToSave = new HsOfficeRelationRbacEntity();
|
||||
entityToSave.setType(HsOfficeRelationType.valueOf(body.getType()));
|
||||
@ -126,11 +126,10 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.relations.api.getSingleRelationByUuid")
|
||||
public ResponseEntity<HsOfficeRelationResource> getSingleRelationByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID relationUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = rbacRelationRepo.findByUuid(relationUuid);
|
||||
if (result.isEmpty()) {
|
||||
@ -143,10 +142,9 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
||||
@Transactional
|
||||
@Timed("apprelations.api..deleteRelationByUuid")
|
||||
public ResponseEntity<Void> deleteRelationByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID relationUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = rbacRelationRepo.deleteByUuid(relationUuid);
|
||||
if (result == 0) {
|
||||
@ -160,16 +158,15 @@ public class HsOfficeRelationController implements HsOfficeRelationsApi {
|
||||
@Transactional
|
||||
@Timed("app.office.relations.api.patchRelation")
|
||||
public ResponseEntity<HsOfficeRelationResource> patchRelation(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID relationUuid,
|
||||
final HsOfficeRelationPatchResource body) {
|
||||
final HsOfficeRelationContactPatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = rbacRelationRepo.findByUuid(relationUuid).orElseThrow();
|
||||
|
||||
new HsOfficeRelationEntityPatcher(em, current).apply(body);
|
||||
new HsOfficeRelationEntityContactPatcher(em, current).apply(body);
|
||||
|
||||
final var saved = rbacRelationRepo.save(current);
|
||||
final var mapped = mapper.map(saved, HsOfficeRelationResource.class);
|
||||
|
@ -1,25 +1,25 @@
|
||||
package net.hostsharing.hsadminng.hs.office.relation;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationPatchResource;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationContactPatchResource;
|
||||
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import java.util.UUID;
|
||||
|
||||
class HsOfficeRelationEntityPatcher implements EntityPatcher<HsOfficeRelationPatchResource> {
|
||||
public class HsOfficeRelationEntityContactPatcher implements EntityPatcher<HsOfficeRelationContactPatchResource> {
|
||||
|
||||
private final EntityManager em;
|
||||
private final HsOfficeRelation entity;
|
||||
|
||||
HsOfficeRelationEntityPatcher(final EntityManager em, final HsOfficeRelation entity) {
|
||||
public HsOfficeRelationEntityContactPatcher(final EntityManager em, final HsOfficeRelation entity) {
|
||||
this.em = em;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(final HsOfficeRelationPatchResource resource) {
|
||||
public void apply(final HsOfficeRelationContactPatchResource resource) {
|
||||
OptionalFromJson.of(resource.getContactUuid()).ifPresent(newValue -> {
|
||||
verifyNotNull(newValue, "contact");
|
||||
entity.setContact(em.getReference(HsOfficeContactRealEntity.class, newValue));
|
@ -0,0 +1,50 @@
|
||||
package net.hostsharing.hsadminng.hs.office.relation;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactRealEntity;
|
||||
import net.hostsharing.hsadminng.hs.office.generated.api.v1.model.HsOfficeRelationPatchResource;
|
||||
import net.hostsharing.hsadminng.hs.office.person.HsOfficePersonRealEntity;
|
||||
import net.hostsharing.hsadminng.mapper.EntityPatcher;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.validation.ValidationException;
|
||||
|
||||
public class HsOfficeRelationPatcher implements EntityPatcher<HsOfficeRelationPatchResource> {
|
||||
|
||||
private final StrictMapper mapper;
|
||||
private final EntityManager em;
|
||||
private final HsOfficeRelation entity;
|
||||
|
||||
public HsOfficeRelationPatcher(final StrictMapper mapper, final EntityManager em, final HsOfficeRelation entity) {
|
||||
this.mapper = mapper;
|
||||
this.em = em;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(final HsOfficeRelationPatchResource resource) {
|
||||
if (resource.getHolder() != null && resource.getHolderUuid() != null) {
|
||||
throw new ValidationException("either \"holder\" or \"holder.uuid\" can be given, not both");
|
||||
} else {
|
||||
if (resource.getHolder() != null) {
|
||||
final var newHolder = mapper.map(resource.getHolder(), HsOfficePersonRealEntity.class);
|
||||
em.persist(newHolder);
|
||||
entity.setHolder(newHolder);
|
||||
} else if (resource.getHolderUuid() != null) {
|
||||
entity.setHolder(em.getReference(HsOfficePersonRealEntity.class, resource.getHolderUuid().get()));
|
||||
}
|
||||
}
|
||||
|
||||
if (resource.getContact() != null && resource.getContactUuid() != null) {
|
||||
throw new ValidationException("either \"contact\" or \"contact.uuid\" can be given, not both");
|
||||
} else {
|
||||
if (resource.getContact() != null) {
|
||||
final var newContact = mapper.map(resource.getContact(), HsOfficeContactRealEntity.class);
|
||||
em.persist(newContact);
|
||||
entity.setContact(newContact);
|
||||
} else if (resource.getContactUuid() != null) {
|
||||
entity.setContact(em.getReference(HsOfficeContactRealEntity.class, resource.getContactUuid().get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ public class HsOfficeRelationRbacEntity extends HsOfficeRelation {
|
||||
"""))
|
||||
.withRestrictedViewOrderBy(SQL.expression(
|
||||
"(select idName from hs_office.person_iv p where p.uuid = target.holderUuid)"))
|
||||
.withUpdatableColumns("contactUuid")
|
||||
.withUpdatableColumns("anchorUuid", "holderUuid", "contactUuid") // BEWARE: additional checks at API-level
|
||||
.importEntityAlias("anchorPerson", HsOfficePersonRbacEntity.class, usingDefaultCase(),
|
||||
dependsOnColumn("anchorUuid"),
|
||||
directlyFetchedByDependsOnColumn(),
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.office.sepamandate;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.hs.office.bankaccount.HsOfficeBankAccountRepository;
|
||||
import net.hostsharing.hsadminng.hs.office.debitor.HsOfficeDebitorRepository;
|
||||
@ -26,7 +27,7 @@ import java.util.function.BiConsumer;
|
||||
import static net.hostsharing.hsadminng.mapper.PostgresDateRange.toPostgresDateRange;
|
||||
|
||||
@RestController
|
||||
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
||||
|
||||
@Autowired
|
||||
@ -51,10 +52,9 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.sepaMandates.api.getListOfSepaMandates")
|
||||
public ResponseEntity<List<HsOfficeSepaMandateResource>> getListOfSepaMandates(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final String iban) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entities = sepaMandateRepo.findSepaMandateByOptionalIban(iban);
|
||||
|
||||
@ -67,11 +67,10 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
||||
@Transactional
|
||||
@Timed("app.office.sepaMandates.api.postNewSepaMandate")
|
||||
public ResponseEntity<HsOfficeSepaMandateResource> postNewSepaMandate(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final HsOfficeSepaMandateInsertResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var entityToSave = mapper.map(body, HsOfficeSepaMandateEntity.class, SEPA_MANDATE_RESOURCE_TO_ENTITY_POSTMAPPER);
|
||||
|
||||
@ -91,11 +90,10 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.office.sepaMandates.api.getSingleSepaMandateByUuid")
|
||||
public ResponseEntity<HsOfficeSepaMandateResource> getSingleSepaMandateByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID sepaMandateUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = sepaMandateRepo.findByUuid(sepaMandateUuid);
|
||||
if (result.isEmpty()) {
|
||||
@ -109,10 +107,9 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
||||
@Transactional
|
||||
@Timed("app.office.sepaMandates.api.deleteSepaMandateByUuid")
|
||||
public ResponseEntity<Void> deleteSepaMandateByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID sepaMandateUuid) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = sepaMandateRepo.deleteByUuid(sepaMandateUuid);
|
||||
if (result == 0) {
|
||||
@ -126,12 +123,11 @@ public class HsOfficeSepaMandateController implements HsOfficeSepaMandatesApi {
|
||||
@Transactional
|
||||
@Timed("app.office.sepaMandates.api.patchSepaMandate")
|
||||
public ResponseEntity<HsOfficeSepaMandateResource> patchSepaMandate(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID sepaMandateUuid,
|
||||
final HsOfficeSepaMandatePatchResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = sepaMandateRepo.findByUuid(sepaMandateUuid).orElseThrow();
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
package net.hostsharing.hsadminng.ping;
|
||||
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
@Controller
|
||||
public class PingController {
|
||||
@ -14,9 +14,8 @@ public class PingController {
|
||||
@ResponseBody
|
||||
@RequestMapping(value = "/api/ping", method = RequestMethod.GET)
|
||||
public String ping(
|
||||
@RequestHeader(name = "current-subject") @NotNull String currentSubject,
|
||||
@RequestHeader(name = "assumed-roles", required = false) String assumedRoles
|
||||
) {
|
||||
return "pong " + currentSubject + "\n";
|
||||
return "pong " + SecurityContextHolder.getContext().getAuthentication().getName() + "\n";
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ class RbacRbacSystemRebuildGenerator {
|
||||
void generateTo(final StringWriter plPgSql) {
|
||||
plPgSql.writeLn("""
|
||||
-- ============================================================================
|
||||
--changeset RbacRbacSystemRebuildGenerator:${liquibaseTagPrefix}-rbac-rebuild endDelimiter:--//
|
||||
--changeset RbacRbacSystemRebuildGenerator:${liquibaseTagPrefix}-rbac-rebuild runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
-- HOWTO: Rebuild RBAC-system for table ${rawTableName} after changing its RBAC specification.
|
||||
|
@ -19,7 +19,7 @@ public class RbacRestrictedViewGenerator {
|
||||
void generateTo(final StringWriter plPgSql) {
|
||||
plPgSql.writeLn("""
|
||||
-- ============================================================================
|
||||
--changeset RbacRestrictedViewGenerator:${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW endDelimiter:--//
|
||||
--changeset RbacRestrictedViewGenerator:${liquibaseTagPrefix}-rbac-RESTRICTED-VIEW runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call rbac.generateRbacRestrictedView('${rawTableName}',
|
||||
$orderBy$
|
||||
|
@ -1261,16 +1261,12 @@ public class RbacSpec {
|
||||
m -> isStatic(m.getModifiers()) && m.getName().equals("main")
|
||||
)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (mainMethod != null) {
|
||||
.orElseThrow(() -> new RuntimeException("no main method in: " + c.getName() + " => cannot generate RBAC rules"));
|
||||
try {
|
||||
mainMethod.invoke(null, new Object[] { null });
|
||||
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
System.err.println("WARNING: no main method in: " + c.getName() + " => no RBAC rules generated");
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<Class<? extends BaseEntity>> findRbacEntityClasses(String packageName) {
|
||||
|
@ -202,6 +202,5 @@ public class RbacViewMermaidFlowchartGenerator {
|
||||
.replace("%{flowchart}", flowchart.toString())
|
||||
.replace("%{case}", forCase == null ? "" : " " + forCase),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
System.out.println("Markdown-File: " + path.toAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,5 @@ public class RbacViewPostgresGenerator {
|
||||
toString(),
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING);
|
||||
System.out.println(outputPath.toAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
private void generateHeader(final StringWriter plPgSql, final String triggerType) {
|
||||
plPgSql.writeLn("""
|
||||
-- ============================================================================
|
||||
--changeset RolesGrantsAndPermissionsGenerator:${liquibaseTagPrefix}-rbac-${triggerType}-trigger endDelimiter:--//
|
||||
--changeset RolesGrantsAndPermissionsGenerator:${liquibaseTagPrefix}-rbac-${triggerType}-trigger runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
""",
|
||||
with("liquibaseTagPrefix", liquibaseTagPrefix),
|
||||
@ -358,9 +358,6 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
}
|
||||
|
||||
private String roleRef(final PostgresTriggerReference rootRefVar, final RbacSpec.RbacRoleDefinition roleDef) {
|
||||
if (roleDef == null) {
|
||||
System.out.println("null");
|
||||
}
|
||||
if (roleDef.getEntityAlias().isGlobal()) {
|
||||
return "rbac.global_ADMIN()";
|
||||
}
|
||||
@ -523,12 +520,11 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger build_rbac_system_after_insert_tg
|
||||
create or replace trigger build_rbac_system_after_insert_tg
|
||||
after insert on ${rawTableQualifiedName}
|
||||
for each row
|
||||
execute procedure ${rawTableQualifiedName}_build_rbac_system_after_insert_tf();
|
||||
"""
|
||||
.replace("${schemaPrefix}", schemaPrefix(qualifiedRawTableName))
|
||||
.replace("${rawTableQualifiedName}", qualifiedRawTableName)
|
||||
);
|
||||
|
||||
@ -558,7 +554,7 @@ class RolesGrantsAndPermissionsGenerator {
|
||||
return NEW;
|
||||
end; $$;
|
||||
|
||||
create trigger update_rbac_system_after_update_tg
|
||||
create or replace trigger update_rbac_system_after_update_tg
|
||||
after update on ${rawTableQualifiedName}
|
||||
for each row
|
||||
execute procedure ${rawTableQualifiedName}_update_rbac_system_after_update_tf();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.rbac.grant;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacGrantsApi;
|
||||
@ -17,6 +18,7 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class RbacGrantController implements RbacGrantsApi {
|
||||
|
||||
@Autowired
|
||||
@ -35,12 +37,11 @@ public class RbacGrantController implements RbacGrantsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.rbac.grants.api.getListOfGrantsByUuid")
|
||||
public ResponseEntity<RbacGrantResource> getListOfGrantsByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID grantedRoleUuid,
|
||||
final UUID granteeSubjectUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var id = new RbacGrantId(granteeSubjectUuid, grantedRoleUuid);
|
||||
final var result = rbacGrantRepository.findById(id);
|
||||
@ -54,10 +55,9 @@ public class RbacGrantController implements RbacGrantsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.rbac.grants.api.getListOfSubjectGrants")
|
||||
public ResponseEntity<List<RbacGrantResource>> getListOfSubjectGrants(
|
||||
final String currentSubject,
|
||||
final String assumedRoles) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
return ResponseEntity.ok(mapper.mapList(rbacGrantRepository.findAll(), RbacGrantResource.class));
|
||||
}
|
||||
@ -66,11 +66,10 @@ public class RbacGrantController implements RbacGrantsApi {
|
||||
@Transactional
|
||||
@Timed("app.rbac.grants.api.postNewRoleGrantToSubject")
|
||||
public ResponseEntity<RbacGrantResource> postNewRoleGrantToSubject(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final RbacGrantResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var granted = rbacGrantRepository.save(mapper.map(body, RbacGrantEntity.class));
|
||||
em.flush();
|
||||
@ -88,12 +87,11 @@ public class RbacGrantController implements RbacGrantsApi {
|
||||
@Transactional
|
||||
@Timed("app.rbac.grants.api.deleteRoleGrantFromSubject")
|
||||
public ResponseEntity<Void> deleteRoleGrantFromSubject(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID grantedRoleUuid,
|
||||
final UUID granteeSubjectUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
rbacGrantRepository.deleteByRbacGrantId(new RbacGrantId(granteeSubjectUuid, grantedRoleUuid));
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.rbac.role;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacRolesApi;
|
||||
@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class RbacRoleController implements RbacRolesApi {
|
||||
|
||||
@Autowired
|
||||
@ -28,10 +30,9 @@ public class RbacRoleController implements RbacRolesApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.rbac.roles.api.getListOfRoles")
|
||||
public ResponseEntity<List<RbacRoleResource>> getListOfRoles(
|
||||
final String currentSubject,
|
||||
final String assumedRoles) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final List<RbacRoleEntity> result = rbacRoleRepository.findAll();
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.rbac.subject;
|
||||
|
||||
import io.micrometer.core.annotation.Timed;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
import net.hostsharing.hsadminng.rbac.generated.api.v1.api.RbacSubjectsApi;
|
||||
@ -16,6 +17,7 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class RbacSubjectController implements RbacSubjectsApi {
|
||||
|
||||
@Autowired
|
||||
@ -42,7 +44,7 @@ public class RbacSubjectController implements RbacSubjectsApi {
|
||||
rbacSubjectRepository.create(saved);
|
||||
final var uri =
|
||||
MvcUriComponentsBuilder.fromController(getClass())
|
||||
.path("/api/rbac.yaml/users/{id}")
|
||||
.path("/api/rbac/subjects/{id}")
|
||||
.buildAndExpand(saved.getUuid())
|
||||
.toUri();
|
||||
return ResponseEntity.created(uri).body(mapper.map(saved, RbacSubjectResource.class));
|
||||
@ -52,11 +54,10 @@ public class RbacSubjectController implements RbacSubjectsApi {
|
||||
@Transactional
|
||||
@Timed("app.rbac.subjects.api.deleteSubjectByUuid")
|
||||
public ResponseEntity<Void> deleteSubjectByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID subjectUuid
|
||||
) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
rbacSubjectRepository.deleteByUuid(subjectUuid);
|
||||
|
||||
@ -67,11 +68,10 @@ public class RbacSubjectController implements RbacSubjectsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.rbac.subjects.api.getSingleSubjectByUuid")
|
||||
public ResponseEntity<RbacSubjectResource> getSingleSubjectByUuid(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID subjectUuid) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = rbacSubjectRepository.findByUuid(subjectUuid);
|
||||
if (result == null) {
|
||||
@ -84,11 +84,10 @@ public class RbacSubjectController implements RbacSubjectsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.rbac.subjects.api.getListOfSubjects")
|
||||
public ResponseEntity<List<RbacSubjectResource>> getListOfSubjects(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final String userName
|
||||
) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
return ResponseEntity.ok(mapper.mapList(rbacSubjectRepository.findByOptionalNameLike(userName), RbacSubjectResource.class));
|
||||
}
|
||||
@ -97,11 +96,10 @@ public class RbacSubjectController implements RbacSubjectsApi {
|
||||
@Transactional(readOnly = true)
|
||||
@Timed("app.rbac.subjects.api.getListOfSubjectPermissions")
|
||||
public ResponseEntity<List<RbacSubjectPermissionResource>> getListOfSubjectPermissions(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID subjectUuid
|
||||
) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
return ResponseEntity.ok(mapper.mapList(
|
||||
rbacSubjectRepository.findPermissionsOfUserByUuid(subjectUuid),
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.hostsharing.hsadminng.rbac.test.cust;
|
||||
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
import net.hostsharing.hsadminng.test.generated.api.v1.api.TestCustomersApi;
|
||||
@ -15,6 +16,7 @@ import jakarta.persistence.PersistenceContext;
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class TestCustomerController implements TestCustomersApi {
|
||||
|
||||
@Autowired
|
||||
@ -32,11 +34,10 @@ public class TestCustomerController implements TestCustomersApi {
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<List<TestCustomerResource>> listCustomers(
|
||||
String currentSubject,
|
||||
String assumedRoles,
|
||||
String prefix
|
||||
) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = testCustomerRepository.findCustomerByOptionalPrefixLike(prefix);
|
||||
|
||||
@ -46,11 +47,10 @@ public class TestCustomerController implements TestCustomersApi {
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseEntity<TestCustomerResource> addCustomer(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final TestCustomerResource customer) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var saved = testCustomerRepository.save(mapper.map(customer, TestCustomerEntity.class));
|
||||
final var uri =
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.hostsharing.hsadminng.rbac.test.pac;
|
||||
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import net.hostsharing.hsadminng.mapper.StrictMapper;
|
||||
import net.hostsharing.hsadminng.mapper.OptionalFromJson;
|
||||
import net.hostsharing.hsadminng.context.Context;
|
||||
@ -15,6 +16,7 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@RestController
|
||||
@SecurityRequirement(name = "casTicket")
|
||||
public class TestPackageController implements TestPackagesApi {
|
||||
|
||||
@Autowired
|
||||
@ -29,11 +31,10 @@ public class TestPackageController implements TestPackagesApi {
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ResponseEntity<List<TestPackageResource>> listPackages(
|
||||
String currentSubject,
|
||||
String assumedRoles,
|
||||
String name
|
||||
) {
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var result = testPackageRepository.findAllByOptionalNameLike(name);
|
||||
return ResponseEntity.ok(mapper.mapList(result, TestPackageResource.class));
|
||||
@ -42,12 +43,11 @@ public class TestPackageController implements TestPackagesApi {
|
||||
@Override
|
||||
@Transactional
|
||||
public ResponseEntity<TestPackageResource> updatePackage(
|
||||
final String currentSubject,
|
||||
final String assumedRoles,
|
||||
final UUID packageUuid,
|
||||
final TestPackageUpdateResource body) {
|
||||
|
||||
context.define(currentSubject, assumedRoles);
|
||||
context.assumeRoles(assumedRoles);
|
||||
|
||||
final var current = testPackageRepository.findByUuid(packageUuid);
|
||||
OptionalFromJson.of(body.getDescription()).ifPresent(current::setDescription);
|
||||
|
@ -3,14 +3,6 @@ components:
|
||||
|
||||
parameters:
|
||||
|
||||
currentSubject:
|
||||
name: current-subject
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Identifying name of the current subject (e.g. user).
|
||||
|
||||
assumedRoles:
|
||||
name: assumed-roles
|
||||
in: header
|
||||
|
@ -1,20 +0,0 @@
|
||||
|
||||
components:
|
||||
|
||||
parameters:
|
||||
|
||||
currentSubject:
|
||||
name: current-subject
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Identifying name of the currently logged in subject.
|
||||
|
||||
assumedRoles:
|
||||
name: assumed-roles
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: Semicolon-separated list of roles to assume. The current subject needs to have the right to assume these roles.
|
1
src/main/resources/api-definition/hs-booking/auth.yaml
Symbolic link
1
src/main/resources/api-definition/hs-booking/auth.yaml
Symbolic link
@ -0,0 +1 @@
|
||||
../auth.yaml
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single booking item its uuid, if visible for the current subject.'
|
||||
operationId: getSingleBookingItemByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: bookingItemUuid
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single booking item identified by its uuid, if permitted for the current subject.'
|
||||
operationId: patchBookingItem
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: bookingItemUuid
|
||||
in: path
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single booking item identified by its uuid, if permitted for the current subject.'
|
||||
operationId: deleteBookingIemByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: bookingItemUuid
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-booking-items
|
||||
operationId: getListOfBookingItemsByProjectUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: projectUuid
|
||||
in: query
|
||||
@ -34,7 +33,6 @@ post:
|
||||
- hs-booking-items
|
||||
operationId: postNewBookingItem
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
description: A JSON object describing the new booking item.
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single booking project its uuid, if visible for the current subject.'
|
||||
operationId: getBookingProjectByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: bookingProjectUuid
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single booking project identified by its uuid, if permitted for the current subject.'
|
||||
operationId: patchBookingProject
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: bookingProjectUuid
|
||||
in: path
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single booking project identified by its uuid, if permitted for the current subject.'
|
||||
operationId: deleteBookingIemByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: bookingProjectUuid
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-booking-projects
|
||||
operationId: getListOfBookingProjectsByDebitorUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: debitorUuid
|
||||
in: query
|
||||
@ -34,7 +33,6 @@ post:
|
||||
- hs-booking-projects
|
||||
operationId: postNewBookingProject
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
description: A JSON object describing the new booking project.
|
||||
|
@ -1,20 +0,0 @@
|
||||
|
||||
components:
|
||||
|
||||
parameters:
|
||||
|
||||
currentSubject:
|
||||
name: current-subject
|
||||
in: header
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
description: Identifying name of the currently logged in subject.
|
||||
|
||||
assumedRoles:
|
||||
name: assumed-roles
|
||||
in: header
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
description: Semicolon-separated list of roles to assume. The current subject needs to have the right to assume these roles.
|
1
src/main/resources/api-definition/hs-hosting/auth.yaml
Symbolic link
1
src/main/resources/api-definition/hs-hosting/auth.yaml
Symbolic link
@ -0,0 +1 @@
|
||||
../auth.yaml
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single managed asset by its uuid, if visible for the current subject.'
|
||||
operationId: getSingleHostingAssetByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: assetUuid
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single hosting asset identified by its uuid, if permitted for the current subject.'
|
||||
operationId: patchHostingAsset
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: assetUuid
|
||||
in: path
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single hosting asset identified by its uuid, if permitted for the current subject.'
|
||||
operationId: deleteHostingAssetByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: assetUuid
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-hosting-assets
|
||||
operationId: getListOfHostingAssets
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: projectUuid
|
||||
in: query
|
||||
@ -47,7 +46,6 @@ post:
|
||||
- hs-hosting-assets
|
||||
operationId: postNewHostingAsset
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
description: A JSON object describing the new hosting asset.
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single bank account by its uuid, if visible for the current subject.'
|
||||
operationId: getSingleBankAccountByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: bankAccountUUID
|
||||
in: path
|
||||
@ -31,7 +30,6 @@ delete:
|
||||
description: 'Delete a single bank account by its uuid, if permitted for the current subject.'
|
||||
operationId: deleteBankAccountByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: bankAccountUUID
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-office-bank-accounts
|
||||
operationId: getListOfBankAccounts
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: holder
|
||||
in: query
|
||||
@ -33,7 +32,6 @@ post:
|
||||
- hs-office-bank-accounts
|
||||
operationId: postNewBankAccount
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
content:
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single business contact by its uuid, if visible for the current subject.'
|
||||
operationId: getSingleContactByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: contactUUID
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single contact by its uuid, if permitted for the current subject.'
|
||||
operationId: patchContact
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: contactUUID
|
||||
in: path
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single business contact by its uuid, if permitted for the current subject.'
|
||||
operationId: deleteContactByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: contactUUID
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-office-contacts
|
||||
operationId: getListOfContacts
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: caption
|
||||
in: query
|
||||
@ -40,7 +39,6 @@ post:
|
||||
- hs-office-contacts
|
||||
operationId: postNewContact
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
content:
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single asset transaction by its uuid, if visible for the current subject.'
|
||||
operationId: getSingleCoopAssetTransactionByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: assetTransactionUUID
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-office-coopAssets
|
||||
operationId: getListOfCoopAssets
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: membershipUuid
|
||||
in: query
|
||||
@ -48,7 +47,6 @@ post:
|
||||
- hs-office-coopAssets
|
||||
operationId: postNewCoopAssetTransaction
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
description: A JSON object describing the new cooperative assets transaction.
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single share transaction by its uuid, if visible for the current subject.'
|
||||
operationId: getSingleCoopShareTransactionByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: shareTransactionUUID
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-office-coopShares
|
||||
operationId: getListOfCoopShares
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: membershipUuid
|
||||
in: query
|
||||
@ -48,7 +47,6 @@ post:
|
||||
- hs-office-coopShares
|
||||
operationId: postNewCoopSharesTransaction
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
description: A JSON object describing the new cooperative shares transaction.
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single debitor by its debitorNumber, if visible for the current subject.'
|
||||
operationId: getSingleDebitorByDebitorNumber
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: debitorNumber
|
||||
in: path
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single debitor by its uuid, if visible for the current subject.'
|
||||
operationId: getSingleDebitorByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: debitorUUID
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single debitor by its uuid, if permitted for the current subject.'
|
||||
operationId: patchDebitor
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: debitorUUID
|
||||
in: path
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single debitor by its uuid, if permitted for the current subject.'
|
||||
operationId: deleteDebitorByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: debitorUUID
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-office-debitors
|
||||
operationId: getListOfDebitors
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: name
|
||||
in: query
|
||||
@ -47,7 +46,6 @@ post:
|
||||
- hs-office-debitors
|
||||
operationId: postNewDebitor
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
content:
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single membership by its membershipNumber, if visible for the current subject.'
|
||||
operationId: getSingleMembershipByMembershipNumber
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: membershipNumber
|
||||
in: path
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single membership by its uuid, if visible for the current subject.'
|
||||
operationId: getSingleMembershipByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: membershipUUID
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single membership by its uuid, if permitted for the current subject.'
|
||||
operationId: patchMembership
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: membershipUUID
|
||||
in: path
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single membership by its uuid, if permitted for the current subject.'
|
||||
operationId: deleteMembershipByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: membershipUUID
|
||||
in: path
|
||||
|
@ -6,7 +6,6 @@ get:
|
||||
- hs-office-memberships
|
||||
operationId: getListOfMemberships
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: partnerUuid
|
||||
in: query
|
||||
@ -42,7 +41,6 @@ post:
|
||||
- hs-office-memberships
|
||||
operationId: postNewMembership
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
description: A JSON object describing the new membership.
|
||||
|
@ -48,12 +48,11 @@ components:
|
||||
HsOfficePartnerPatch:
|
||||
type: object
|
||||
properties:
|
||||
partnerRel.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
partnerRel:
|
||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationPatch'
|
||||
details:
|
||||
$ref: '#/components/schemas/HsOfficePartnerDetailsPatch'
|
||||
additionalProperties: false
|
||||
|
||||
HsOfficePartnerDetailsPatch:
|
||||
type: object
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single business partner by its partner-number (prefixed with "P-"), if visible for the current subject.'
|
||||
operationId: getSinglePartnerByPartnerNumber
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: partnerNumber
|
||||
in: path
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single business partner by its uuid, if visible for the current subject.'
|
||||
operationId: getSinglePartnerByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: partnerUUID
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single business partner by its uuid, if permitted for the current subject.'
|
||||
operationId: patchPartner
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: partnerUUID
|
||||
in: path
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single business partner by its uuid, if permitted for the current subject.'
|
||||
operationId: deletePartnerByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: partnerUUID
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-office-partners
|
||||
operationId: getListOfPartners
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: name
|
||||
in: query
|
||||
@ -33,7 +32,6 @@ post:
|
||||
- hs-office-partners
|
||||
operationId: postNewPartner
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
content:
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single business person by its uuid, if visible for the current subject.'
|
||||
operationId: getSinglePersonByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: personUUID
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single person by its uuid, if permitted for the current subject.'
|
||||
operationId: patchPerson
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: personUUID
|
||||
in: path
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single business person by its uuid, if permitted for the current subject.'
|
||||
operationId: deletePersonByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: personUUID
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-office-persons
|
||||
operationId: getListOfPersons
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: name
|
||||
in: query
|
||||
@ -33,7 +32,6 @@ post:
|
||||
- hs-office-persons
|
||||
operationId: postNewPerson
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
content:
|
||||
|
@ -34,7 +34,7 @@ components:
|
||||
contact:
|
||||
$ref: 'hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContact'
|
||||
|
||||
HsOfficeRelationPatch:
|
||||
HsOfficeRelationContactPatch:
|
||||
type: object
|
||||
properties:
|
||||
contact.uuid:
|
||||
@ -42,6 +42,27 @@ components:
|
||||
format: uuid
|
||||
nullable: true
|
||||
|
||||
HsOfficeRelationPatch:
|
||||
type: object
|
||||
properties:
|
||||
anchor.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
holder.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
holder:
|
||||
$ref: 'hs-office-person-schemas.yaml#/components/schemas/HsOfficePersonInsert'
|
||||
contact.uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
nullable: true
|
||||
contact:
|
||||
$ref: 'hs-office-contact-schemas.yaml#/components/schemas/HsOfficeContactInsert'
|
||||
additionalProperties: false
|
||||
|
||||
# arbitrary relation with explicit type
|
||||
HsOfficeRelationInsert:
|
||||
type: object
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single person relation by its uuid, if visible for the current subject.'
|
||||
operationId: getSingleRelationByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: relationUUID
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single person relation by its uuid, if permitted for the current subject.'
|
||||
operationId: patchRelation
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: relationUUID
|
||||
in: path
|
||||
@ -44,7 +42,7 @@ patch:
|
||||
content:
|
||||
'application/json':
|
||||
schema:
|
||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationPatch'
|
||||
$ref: 'hs-office-relation-schemas.yaml#/components/schemas/HsOfficeRelationContactPatch'
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single person relation by its uuid, if permitted for the current subject.'
|
||||
operationId: deleteRelationByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: relationUUID
|
||||
in: path
|
||||
|
@ -7,7 +7,6 @@ get:
|
||||
- hs-office-relations
|
||||
operationId: getListOfRelations
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: personUuid
|
||||
in: query
|
||||
@ -60,7 +59,6 @@ post:
|
||||
- hs-office-relations
|
||||
operationId: postNewRelation
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
content:
|
||||
|
@ -4,7 +4,6 @@ get:
|
||||
description: 'Fetch a single SEPA Mandate by its uuid, if visible for the current subject.'
|
||||
operationId: getSingleSepaMandateByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: sepaMandateUUID
|
||||
in: path
|
||||
@ -32,7 +31,6 @@ patch:
|
||||
description: 'Updates a single SEPA Mandate by its uuid, if permitted for the current subject.'
|
||||
operationId: patchSepaMandate
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: sepaMandateUUID
|
||||
in: path
|
||||
@ -63,7 +61,6 @@ delete:
|
||||
description: 'Delete a single SEPA Mandate by its uuid, if permitted for the current subject.'
|
||||
operationId: deleteSepaMandateByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: sepaMandateUUID
|
||||
in: path
|
||||
|
@ -5,7 +5,6 @@ get:
|
||||
- hs-office-sepaMandates
|
||||
operationId: getListOfSepaMandates
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: iban
|
||||
in: query
|
||||
@ -33,7 +32,6 @@ post:
|
||||
- hs-office-sepaMandates
|
||||
operationId: postNewSepaMandate
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
description: A JSON object describing the new SEPA-Mandate.
|
||||
|
@ -3,7 +3,6 @@ get:
|
||||
- rbac-grants
|
||||
operationId: getListOfGrantsByUuid
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: grantedRoleUuid
|
||||
in: path
|
||||
@ -38,7 +37,6 @@ delete:
|
||||
- rbac-grants
|
||||
operationId: deleteRoleGrantFromSubject
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
- name: grantedRoleUuid
|
||||
in: path
|
||||
|
@ -3,7 +3,6 @@ get:
|
||||
- rbac-grants
|
||||
operationId: getListOfSubjectGrants
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
responses:
|
||||
"200":
|
||||
@ -20,7 +19,6 @@ post:
|
||||
- rbac-grants
|
||||
operationId: postNewRoleGrantToSubject
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
requestBody:
|
||||
required: true
|
||||
|
@ -3,7 +3,6 @@ get:
|
||||
- rbac-roles
|
||||
operationId: getListOfRoles
|
||||
parameters:
|
||||
- $ref: 'auth.yaml#/components/parameters/currentSubject'
|
||||
- $ref: 'auth.yaml#/components/parameters/assumedRoles'
|
||||
responses:
|
||||
"200":
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user