diff --git a/.aliases b/.aliases
index 96a0b970..570398a0 100644
--- a/.aliases
+++ b/.aliases
@@ -75,13 +75,37 @@ alias gw-importHostingAssets='importLegacyData importHostingAssets'
function gradlewBootRun() {
local serverPort=${1:-8080}; shift
- local managementPort=${2:-$((serverPort + 1))}; shift
- local additional_args="$@"
+ local managementPort
+ if [[ -n "${1:-}" && "${1}" != -* ]]; then
+ managementPort=$1
+ shift
+ else
+ managementPort=$((serverPort + 1))
+ fi
+
+ # everything before `--` is treated as application args (go into --args="...")
+ # everything after `--` is treated as Gradle/task args (e.g. --debug-jvm, --stacktrace, --info, ...)
+ local -a app_args=()
+ while [[ $# -gt 0 && "${1}" != "--" ]]; do
+ app_args+=("$1")
+ shift
+ done
+ if [[ "${1:-}" == "--" ]]; then
+ shift
+ fi
+ local -a gradle_args=("$@")
+
+ # preserve previous behavior: app args are appended as plain space-separated tokens
+ local additional_args=""
+ if [[ ${#app_args[@]} -gt 0 ]]; then
+ additional_args="$(printf ' %s' "${app_args[@]}")"
+ fi
+
unset HSADMINNG_JWT_ISSUER
unset HSADMINNG_JWT_JWKS_URL
unset HSADMINNG_JWT_TOKEN_URL
set -x
- ./gradlew bootRun --args="--spring.profiles.active=dev,fake-jwt,complete,test-data --server.port=${serverPort} --management.server.port=${managementPort} ${additional_args}"
+ ./gradlew bootRun "${gradle_args[@]}" --args="--spring.profiles.active=dev,fake-jwt,complete,test-data --server.port=${serverPort} --management.server.port=${managementPort}${additional_args}"
set +x
}
alias gw-bootRun=gradlewBootRun
@@ -91,7 +115,7 @@ alias podman-stop='systemctl --user disable --now podman.socket && systemctl --u
alias podman-use='export DOCKER_HOST="unix:///run/user/$UID/podman/podman.sock"; export TESTCONTAINERS_RYUK_DISABLED=true'
alias gw=gradleWrapper
-alias pg-sql-run='docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:15.5-bookworm'
+alias pg-sql-run='docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17.7-trixie'
alias pg-sql-stop='docker stop hsadmin-ng-postgres'
alias pg-sql-start='docker container start hsadmin-ng-postgres'
alias pg-sql-remove='docker rm hsadmin-ng-postgres'
diff --git a/README.md b/README.md
index a31e3a46..e9ffe2b7 100644
--- a/README.md
+++ b/README.md
@@ -87,20 +87,27 @@ If you have at least Docker and the Java JDK installed in appropriate versions a
# if the container has been built already and you want to keep the data, run this:
pg-sql-start
-Next, compile and run the application with in dev-mode with all modules, test-data and fake-JWT-authentication::
+Next, compile and run the application with in dev-mode with all modules, test-data and fake-JWT-authentication usind the `gw-bootRun` alias:
# on `localhost:8080` and the management server on `localhost:8081`:
gw-bootRun
- # there is also an alias which takes an optional port as an argument:
- gw-bootRun 8888
+ # you can also pass optional arguments:
+ gw-bootRun 8888 # will set the management port to 8888+1 = 8889
+ gw-bootRun 8888 9999 # with explicit management port 9999
-The meaning of these profiles is:
+At the beginning of the output, you'll see the full `./gradlew`-call like this:
+
+```
++ ./gradlew bootRun '--args=--spring.profiles.active=dev,fake-jwt,complete,test-data --server.port=8080 --management.server.port=8081'
+```
+
+The meaning of the listed profiles is:
- **dev**: the PostgreSQL users are created via Liquibase
- **fake-jwt**: the app starts with a build-in fake OAuth2/JWT server
-- **complete**: all modules are started
-- **test-data**: some test data inserted
+- **complete**: all modules (rbac, office, account, hosting) are started
+- **test-data**: some test data gets inserted at startup
Now we can access the REST API, e.g. using curl. But you need to use JWT authentication.
To make this a bit easier to handle, we use `bin/jwt-curl` (or `jwt-curl` alias).
@@ -130,7 +137,7 @@ Make sure you replace `8080` with the port you used to run the application.`
# the following command should return a JSON array with just all customers:
jwt-curl GET http://localhost:8080/api/test/customers \
- | jq # just if `jq` is installed, to prettyprint the output
+ | 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:
jwt-curl ASSUME 'rbactest.customer#yyy:ADMIN'
@@ -138,7 +145,7 @@ Make sure you replace `8080` with the port you used to run the application.`
| jq
jwt-curl UNASSUME
- # add a new customer
+ # add a new customer (this is a test-are, not to confuse with a Hostsharing partner)
jwt-curl POST \
-d '{ "prefix":"ttt", "reference":80001, "adminUserName":"admin@ttt.example.com" }' \
http://localhost:8080/api/test/customers \
@@ -177,14 +184,14 @@ But the easiest way to run PostgreSQL is via Docker.
Initially, pull an image compatible to the current PostgreSQL version of Hostsharing:
- docker pull postgres:15.5-bookworm
+ docker pull postgres:17.7-trixie
**⚠**
If we switch the version, please also amend the documentation as well as the aliases file. Thanks!
Create and run a container with the given PostgreSQL version:
- docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:15.5-bookworm
+ docker run --name hsadmin-ng-postgres -e POSTGRES_PASSWORD=password -p 5432:5432 -d postgres:17.7-trixie
# or via alias:
pg-sql-run
@@ -705,14 +712,15 @@ These profiles mean:
### How to Run the Application in a Debugger
-Add `' --debug-jvm` to the command line:
-
+Add `'-- --debug-jvm` to the command line ('...' stands any other args).
+The `--debug-jvm` is a so-called *Gradle side knob, which goes outside of the `--args="..."` application arguments,
+thus we separate it by `--`; this is treated by the `gw-bootRun` alias.
```sh
-gw bootRun ... --debug-jvm
+gw-bootRun ... -- --debug-jvm
```
-At the very beginning, the application is going to wait for a debugger with a message like this:
+In 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
diff --git a/bin/jwt-curl b/bin/jwt-curl
index 86f268a0..0489e231 100755
--- a/bin/jwt-curl
+++ b/bin/jwt-curl
@@ -14,11 +14,11 @@ if [ "$1" == "--trace" ]; then
function doCurl() {
set -x
if [ -z "$HSADMINNG_JWT_ASSUME" ]; then
- curl --fail-with-body \
+ curl --no-progress-meter --show-error --fail-with-body \
--header "Authorization: Bearer $HSADMINNG_JWT_TOKEN" \
"$@"
else
- curl --fail-with-body \
+ curl --no-progress-meter --show-error --fail-with-body \
--header "Authorization: Bearer $HSADMINNG_JWT_TOKEN" \
--header "assumed-roles: $HSADMINNG_JWT_ASSUME" \
"$@"
diff --git a/build.gradle.kts b/build.gradle.kts
index c9f0802a..97ac4120 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -29,6 +29,7 @@ plugins {
java
id("org.springframework.boot") version "3.5.5"
id("io.spring.dependency-management") version "1.1.7" // manages implicit dependencies
+ id("com.gorylenko.gradle-git-properties") version "2.5.0" // exposes git commit info via Actuator info endpoint
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
id("org.owasp.dependencycheck") version "12.1.1" // checks dependencies for known vulnerabilities
@@ -39,6 +40,10 @@ plugins {
id("com.github.ben-manes.versions") version "0.52.0" // determines which dependencies have updates
}
+springBoot {
+ buildInfo()
+}
+
// HOWTO: find out which dependency versions are managed by Spring Boot:
// https://docs.spring.io/spring-boot/appendix/dependency-versions/coordinates.html
@@ -702,3 +707,16 @@ tasks.named("bootRun") {
// Or always enable debug (remove the if condition)
// jvmArgs = listOf("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005")
}
+
+// Generate git.properties for Spring Boot Actuator /actuator/info
+gitProperties {
+ // Keep output small but useful for build identification
+ keys = listOf(
+ "git.commit.id",
+ "git.commit.id.abbrev",
+ "git.branch",
+ "git.commit.time"
+ )
+ // Allow builds from exported sources without .git
+ failOnNoGitDirectory = false
+}
diff --git a/doc/ideas/account-profiles-data-model.mermaid b/doc/ideas/account-profiles-data-model.mermaid
deleted file mode 100644
index 22154c44..00000000
--- a/doc/ideas/account-profiles-data-model.mermaid
+++ /dev/null
@@ -1,49 +0,0 @@
-classDiagram
- direction LR
-
-OfficePerson "1" o.. "*" Profile
-Profile "1" o-- "1" RbacSubject
-
-Scope "1..n" --o "1" ScopeMapping
-Profile "1..n" --o "1" ScopeMapping
-
-class Profile{
- +emailAdress: text
- +smsNumber: text
- +password: text
- +totpSecrets: text
- +phonePassword: text
- -active: bool [r/w]
- -globalUid: int [w/o]
- -globalGid: int [w/o]
-}
-
-class Scope{
- -type: Enum [SSH, Matrix, Mastodon, ...]
- -qualifier: text
-}
-
-class ScopeMapping{
- note for ScopeMapping "Assigns Profile to Scopes"
-}
-
-class RbacSubject{
- +uuid: uuid
- +name: text # == nickname
-}
-
-class OfficePerson{
- +type: enum
- +tradename: text
- +title: text
- +familyName: text
- +givenName: text
- +salutation: text
-}
-
-style Scope fill:#00f,color:#fff
-style ScopeMapping fill:#00f,color:#fff
-style Profile fill:#00f,color:#fff
-
-style RbacSubject fill:#f96,color:#fff
-style OfficePerson fill:#f66,color:#000
diff --git a/doc/ideas/accounts-data-model.mermaid b/doc/ideas/accounts-data-model.mermaid
new file mode 100644
index 00000000..c041bfa1
--- /dev/null
+++ b/doc/ideas/accounts-data-model.mermaid
@@ -0,0 +1,29 @@
+classDiagram
+ direction LR
+
+OfficePerson "1" o.. "*" Account
+Account "1" o-- "1" RbacSubject
+
+class Account{
+ -globalUid: int [w/o]
+ -globalGid: int [w/o]
+}
+
+class RbacSubject{
+ +uuid: uuid
+ +name: text # == subjectName
+}
+
+class OfficePerson{
+ +type: enum
+ +tradename: text
+ +title: text
+ +familyName: text
+ +givenName: text
+ +salutation: text
+}
+
+style Account fill:#00f,color:#fff
+
+style RbacSubject fill:#f96,color:#fff
+style OfficePerson fill:#f66,color:#000
diff --git a/gradle.properties b/gradle.properties
index 433cede1..0953e576 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,4 +1,5 @@
# Gradle Java Toolchain-support
+org.gradle.toolchain.auto-download=true
org.gradle.java.installations.auto-detect=true
org.gradle.java.installations.auto-download=true
# org.gradle.jvm.toolchain.install.adoptopenjdk.baseUri
diff --git a/src/main/java/net/hostsharing/hsadminng/HsadminNgApplication.java b/src/main/java/net/hostsharing/hsadminng/HsadminNgApplication.java
index 59812797..2816aeb7 100644
--- a/src/main/java/net/hostsharing/hsadminng/HsadminNgApplication.java
+++ b/src/main/java/net/hostsharing/hsadminng/HsadminNgApplication.java
@@ -21,14 +21,14 @@ public class HsadminNgApplication {
@Override
public void addCorsMappings(CorsRegistry registry) {
- // TODO: to enable testing, we should use Spring config
+ // TODO: to enable testing, we should use Spring config
String allowedOrigins = System.getenv("ALLOWED_ORIGINS");
if (allowedOrigins == null || allowedOrigins.length() <= 1) {
allowedOrigins = "/**";
}
registry.addMapping("/api/**")
- .allowedOrigins(allowedOrigins)
- .allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE");
+ .allowedOrigins(allowedOrigins)
+ .allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE");
}
};
}
diff --git a/src/main/java/net/hostsharing/hsadminng/config/FakeJwtController.java b/src/main/java/net/hostsharing/hsadminng/config/FakeJwtController.java
index e58776ad..d037b131 100644
--- a/src/main/java/net/hostsharing/hsadminng/config/FakeJwtController.java
+++ b/src/main/java/net/hostsharing/hsadminng/config/FakeJwtController.java
@@ -9,6 +9,7 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
+import jakarta.servlet.http.HttpServletRequest;
import java.util.Map;
@@ -21,8 +22,9 @@ public class FakeJwtController {
@PostMapping(value = "/fake-jwt/token", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
@Timed("app.config.jwt.token")
public ResponseEntity