From f24b39bf7ba32725d7e6905a709c72a7d63297f0 Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Tue, 8 Apr 2025 14:40:19 +0200
Subject: [PATCH] Improved caching

Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at>
---
 .docker/docker-compose.yml                    |  23 +++++++++---------
 .../target/create-event-listener.jar          | Bin 10139 -> 10139 bytes
 dbrepo-dashboard-ui/Dockerfile                |   8 +++---
 .../src/test/resources/application.properties |   2 ++
 .../at/ifs/dbrepo/auth/AuthTokenFilter.java   |   8 ++++--
 .../gateway/impl/KeycloakGatewayImpl.java     |   2 +-
 .../service/impl/CredentialServiceImpl.java   |  14 ++++++++---
 dbrepo-gateway-service/dbrepo.conf            |   2 +-
 .../dbrepo/endpoints/AbstractEndpoint.java    |   1 +
 .../components/database/DatabaseToolbar.vue   |   4 +--
 dbrepo-ui/components/user/UserBadge.vue       |   2 +-
 dbrepo-ui/locales/en-US.json                  |   2 +-
 .../pages/database/[database_id]/settings.vue |   5 ++--
 docker-compose.yml                            |  10 +++-----
 helm/dbrepo/files/create-event-listener.jar   | Bin 10139 -> 10139 bytes
 .../dbrepo/core/mapper/MetadataMapper.java    |   3 ++-
 .../tuwien/ifs/dbrepo/core/test/BaseTest.java |   2 +-
 17 files changed, 50 insertions(+), 38 deletions(-)

diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml
index 32306b3ccd..835929d770 100644
--- a/.docker/docker-compose.yml
+++ b/.docker/docker-compose.yml
@@ -42,9 +42,9 @@ services:
     ports:
       - "3307:3306"
     environment:
-      MARIADB_PASSWORD: "${READONLY_PASSWORD:-user}"
+      MARIADB_PASSWORD: "${READONLY_PASSWORD:-readonly}"
       MARIADB_ROOT_PASSWORD: "${DATA_DB_PASSWORD:-dbrepo}"
-      MARIADB_USER: "${READONLY_USERNAME:-user}"
+      MARIADB_USER: "${READONLY_USERNAME:-readonly}"
     healthcheck:
       test: ./usr/local/bin/healthcheck.sh --connect --innodb_initialized
       interval: 10s
@@ -96,6 +96,7 @@ services:
       KEYCLOAK_DATABASE_NAME: "${AUTH_DB_NAME:-keycloak}"
       KEYCLOAK_DATABASE_USER: "${AUTH_DB_USERNAME:-keycloak}"
       KEYCLOAK_DATABASE_PASSWORD: "${AUTH_DB_PASSWORD:-dbrepo}"
+      KEYCLOAK_HOSTNAME: "${BASE_URL:-http://localhost}"
       METADATA_SERVICE_ENDPOINT: "${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}"
       SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
       SYSTEM_PASSWORD: "${SYSTEM_PASSWORD:-admin}"
@@ -122,7 +123,7 @@ services:
       METADATA_DB: "${METADATA_DB:-dbrepo}"
       METADATA_DB_PASSWORD: "${METADATA_DB_PASSWORD:-dbrepo}"
       METADATA_USERNAME: "root"
-      READONLY_USERNAME: "${READONLY_USERNAME:-user}"
+      READONLY_USERNAME: "${READONLY_USERNAME:-readonly}"
       SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
       SYSTEM_PASSWORD: ${SYSTEM_PASSWORD:-admin}
     depends_on:
@@ -301,6 +302,7 @@ services:
     environment:
       NUXT_PUBLIC_API_CLIENT: "${BASE_URL:-http://localhost}"
       NUXT_PUBLIC_API_SERVER: "${BASE_URL:-http://gateway-service}"
+      NUXT_PUBLIC_DASHBOARD_URL: "${BASE_URL:-http://localhost}/dashboard"
       NUXT_OIDC_PROVIDERS_KEYCLOAK_BASE_URL: "${BASE_URL:-http://localhost}/realms/dbrepo"
       NUXT_OIDC_PROVIDERS_KEYCLOAK_AUTHORIZATION_URL: "${BASE_URL:-http://localhost}/realms/dbrepo/protocol/openid-connect/auth"
       NUXT_OIDC_PROVIDERS_KEYCLOAK_CLIENT_ID: "${AUTH_SERVICE_CLIENT:-dbrepo-client}"
@@ -348,6 +350,8 @@ services:
         condition: service_healthy
       dbrepo-dashboard-service:
         condition: service_healthy
+      dbrepo-dashboard-ui:
+        condition: service_healthy
       dbrepo-ui:
         condition: service_healthy
     logging:
@@ -361,8 +365,8 @@ services:
     environment:
       LDAP_ADMIN_USERNAME: "${IDENTITY_SERVICE_ADMIN_USERNAME:-admin}"
       LDAP_ADMIN_PASSWORD: "${IDENTITY_SERVICE_ADMIN_PASSWORD:-admin}"
-      LDAP_USERS: "${SYSTEM_USERNAME:-admin},${READONLY_USERNAME:-user}"
-      LDAP_PASSWORDS: "${SYSTEM_PASSWORD:-admin},${READONLY_PASSWORD:-user}"
+      LDAP_USERS: "${SYSTEM_USERNAME:-admin},${READONLY_USERNAME:-readonly}"
+      LDAP_PASSWORDS: "${SYSTEM_PASSWORD:-admin},${READONLY_PASSWORD:-readonly}"
       LDAP_GROUP: "${ADMIN_GROUP:-system}"
       LDAP_ROOT: "${IDENTITY_SERVICE_ROOT:-dc=dbrepo,dc=at}"
       LDAP_ADMIN_DN: "${IDENTITY_SERVICE_ADMIN_DN:-cn=admin,dc=dbrepo,dc=at}"
@@ -525,8 +529,8 @@ services:
       - dashboard-ui-data:/opt/bitnami/grafana/data
     environment:
       BASE_URL: "${BASE_URL:-http://localhost}"
+      GF_SERVER_ROOT_URL: http://dashboard-ui:3000/dashboard/
       GF_INSTALL_PLUGINS: "yesoreyeram-infinity-datasource"
-      GF_SERVER_DOMAIN: "dashboard-service"
       GF_SECURITY_DISABLE_INITIAL_ADMIN_CREATION: "true"
       LDAP_ADMIN_USERNAME: "${IDENTITY_SERVICE_ADMIN_USERNAME:-admin}"
       LDAP_ADMIN_PASSWORD: "${IDENTITY_SERVICE_ADMIN_PASSWORD:-admin}"
@@ -536,9 +540,6 @@ services:
       interval: 10s
       timeout: 5s
       retries: 12
-    depends_on:
-      dbrepo-metric-db:
-        condition: service_started
     extra_hosts:
       - "localhost:host-gateway"
     logging:
@@ -563,7 +564,7 @@ services:
       timeout: 5s
       retries: 12
     depends_on:
-      dbrepo-metric-db:
-        condition: service_started
+      dbrepo-dashboard-ui:
+        condition: service_healthy
     logging:
       driver: json-file
diff --git a/dbrepo-auth-service/listeners/target/create-event-listener.jar b/dbrepo-auth-service/listeners/target/create-event-listener.jar
index d72418146ece32374ade889a7c9972b8e1bd9eb1..4c541f2eb5bd4606250425f72aa7971a40164662 100644
GIT binary patch
delta 645
zcmbR3Kigj@z?+#xgn@yBgW+0HN0hzY)5M8FO7*Ax4jTyUaX)4MgFWXvqvvetR^||q
zCDV%T-;mBSE!3)>d};r2Uqw+-CY_%<AJ6@`Mm)i{ao!JYtNg6l8y89(xwzI@;Yv-}
zi5pB&TizU7*_(NB<Hj9UN@=~Crt@Fx#=i|{&CZmO4PR!!rIGSTzs>8+jB~Q@viNv&
zcer1dYMp+feqQyJrTciIVt4nPP2c)m$9{uqRvEvDJO9;nN4RwVo-Jqm>!z`;zT^Mo
z;=MIzZnR}QH`e_Ad#^#O_?0)jQH)*;p2^(o^I{gSD17nMb6(Hlgc6};mRl}-zMGhL
z;9GmVDCaf1>bf-!#}-^V8M?i`Nz3N@#T(D|-v0i>rA6w~#|icIN#Uy=ZQr_e#ft9g
z(A504U3#e!=DvC%u_txqLLv`ui|-Javg$Xt%<To1Hgl#Q6OJlbe#ATHY-iZg%SlFU
zDOP@;61%SdR0!SW;hrUMi{(qnq^z}*U%tF|Yij!Qy2}bD6;EkZbi7-$R9J|mHjC5k
z*hO|wG;lF6a4>+QVe&>sW#%hI9h0v!>M~y~>X^*VBnPH7m{ggs6?IGwVA2B9wM-^p
zdJB^mSUn^2SulN(xf`T@b3Dr&CNSe48#gDI!6Q&32-ecAI15Z`C{5;oMg8Q%N>(7j
zNy;W*W6YIpL7FC)E9-&j70RX{>B$e2!OAAfs<?ppDJoH5^#@gg!L+z)E?9lLYAl$3
VuIdP;wbgck)xS{70Mo(h9sqx=A%p+`

delta 645
zcmbR3Kigj@z?+#xgn@yBgCTTzdz99cR_=*HO7#bW4jBmSEnoJ3g1kr20*AN09~SVu
z;OY3Tde^JXEOk=u#Qo+XI?9h%w5!a1b9&$O?U4-^Ev+8qXUtvabE-;-t9hlX!kHgW
zW^7_L$+)pMH2Us>RA#~X0+XY}CFiGF@4L15)AeJC1zjcUL?^625^pLxV^dCYVSM3=
z%+s>B+_d+&)|cJ+yX{NvrIe|@H*<n4kDIw~JJMsz)Os*@r~U>P=laig9rg=$J>>sc
zU$k!e=P9|J-}I(_DzM;~Ywh^eak1b6mI+tXwm&#=tt+eBZ{E|B1|Qt|{}?s1S?y!L
zlVNc8VYvIAt@?fQttJ?0Y!1BhZvMhA=jUbz*KhgFXD_s`z2?H>dhs`2Q){9mo4ao9
z2~PdBZl!F{ugd8BmQ`Lm4=>izx>4Hr!E@!U{RcLFob~r`<wDtoj@5d5j=0UweC)IG
zV60J}QqN?KmrpH^?F}^XOW+Z7ivK!UMuhMD_iNjBSbd+(<|!69?Vj_NaNV^6`Y#q8
zm6l|i4~hmZ1_llWa5PNb$f(R5vb=rrbw*w0(B<uu*_q_Pv<8zZm=0jl0@JljCSZCC
zlNVS$BlB4>eUZ5vB)&PGWeyXV@sEw06U^Wds1XD)CU+~&0@E5wlR02fKl!kd6-aQB
zvI*E2b7fl)cXGM19++OCYzn3yD1((vmQ`^9^HWr!!0HdG1cPaD)m*T6yJ{?$ey-{W
SrnS{}f$0}&8DKhC-2(uml^g8<

diff --git a/dbrepo-dashboard-ui/Dockerfile b/dbrepo-dashboard-ui/Dockerfile
index 07a30f99ab..099089718d 100644
--- a/dbrepo-dashboard-ui/Dockerfile
+++ b/dbrepo-dashboard-ui/Dockerfile
@@ -3,7 +3,7 @@ LABEL org.opencontainers.image.authors="martin.weise@tuwien.ac.at"
 
 WORKDIR /app
 
-COPY --chown=grafana:grafana  ./dashboards /app/dashboards
-COPY --chown=grafana:grafana  ./provisioning /etc/grafana/provisioning
-COPY --chown=grafana:grafana  ./grafana.ini /etc/grafana/grafana.ini
-COPY --chown=grafana:grafana  ./ldap.toml /etc/grafana/ldap.toml
+COPY --chown=grafana:grafana ./grafana.ini /opt/bitnami/grafana/conf/grafana.ini
+COPY --chown=grafana:grafana ./ldap.toml /opt/bitnami/grafana/conf/ldap.toml
+COPY --chown=grafana:grafana ./dashboards /app/dashboards
+COPY --chown=grafana:grafana ./provisioning /opt/bitnami/grafana/conf/
diff --git a/dbrepo-data-service/rest-service/src/test/resources/application.properties b/dbrepo-data-service/rest-service/src/test/resources/application.properties
index 1150414f22..8f658cab55 100644
--- a/dbrepo-data-service/rest-service/src/test/resources/application.properties
+++ b/dbrepo-data-service/rest-service/src/test/resources/application.properties
@@ -11,6 +11,8 @@ spring.cloud.config.enabled=false
 # very short for testing
 dbrepo.credentialCacheTimeout=3
 
+dbrepo.jwt.public_key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQAB
+
 # internal datasource
 spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE;INIT=CREATE SCHEMA IF NOT EXISTS DBREPO;NON_KEYWORDS=value
 spring.datasource.driverClassName=org.h2.Driver
diff --git a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/auth/AuthTokenFilter.java b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/auth/AuthTokenFilter.java
index 1dd94ec5ab..2073c384e8 100644
--- a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/auth/AuthTokenFilter.java
+++ b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/auth/AuthTokenFilter.java
@@ -53,7 +53,7 @@ public class AuthTokenFilter extends OncePerRequestFilter {
         filterChain.doFilter(request, response);
     }
 
-    public UserDetails verifyJwt(String token) throws BadCredentialsException {
+    public DecodedJWT decodeJwt(String token) {
         final KeyFactory kf;
         try {
             kf = KeyFactory.getInstance("RSA");
@@ -72,7 +72,11 @@ public class AuthTokenFilter extends OncePerRequestFilter {
         final Algorithm algorithm = Algorithm.RSA256(pubKey, null);
         final Verification verification = JWT.require(algorithm);
         final JWTVerifier verifier = verification.build();
-        final DecodedJWT jwt = verifier.verify(token);
+        return verifier.verify(token);
+    }
+
+    public UserDetails verifyJwt(String token) throws BadCredentialsException {
+        final DecodedJWT jwt = decodeJwt(token);
         final RealmAccessDto realmAccess = jwt.getClaim("realm_access").as(RealmAccessDto.class);
         return UserDetailsDto.builder()
                 .id(jwt.getClaim("uid").asString())
diff --git a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/gateway/impl/KeycloakGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/gateway/impl/KeycloakGatewayImpl.java
index 5ca5f61048..e3a439a376 100644
--- a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/gateway/impl/KeycloakGatewayImpl.java
+++ b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/gateway/impl/KeycloakGatewayImpl.java
@@ -1,9 +1,9 @@
 package at.ac.tuwien.ac.at.ifs.dbrepo.gateway.impl;
 
-import at.ac.tuwien.ifs.dbrepo.core.api.keycloak.TokenDto;
 import at.ac.tuwien.ac.at.ifs.dbrepo.config.KeycloakConfig;
 import at.ac.tuwien.ac.at.ifs.dbrepo.gateway.KeycloakGateway;
 import at.ac.tuwien.ac.at.ifs.dbrepo.mapper.MetadataMapper;
+import at.ac.tuwien.ifs.dbrepo.core.api.keycloak.TokenDto;
 import jakarta.ws.rs.NotAuthorizedException;
 import lombok.extern.log4j.Log4j2;
 import org.keycloak.OAuth2Constants;
diff --git a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/service/impl/CredentialServiceImpl.java b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/service/impl/CredentialServiceImpl.java
index 54db56f635..ed9420a984 100644
--- a/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/service/impl/CredentialServiceImpl.java
+++ b/dbrepo-data-service/services/src/main/java/at/ac/tuwien/ac/at/ifs/dbrepo/service/impl/CredentialServiceImpl.java
@@ -1,5 +1,6 @@
 package at.ac.tuwien.ac.at.ifs.dbrepo.service.impl;
 
+import at.ac.tuwien.ac.at.ifs.dbrepo.auth.AuthTokenFilter;
 import at.ac.tuwien.ifs.dbrepo.core.api.keycloak.TokenDto;
 import at.ac.tuwien.ac.at.ifs.dbrepo.gateway.KeycloakGateway;
 import at.ac.tuwien.ac.at.ifs.dbrepo.service.CredentialService;
@@ -14,25 +15,30 @@ import java.time.Instant;
 @Service
 public class CredentialServiceImpl implements CredentialService {
 
+    private final AuthTokenFilter authTokenFilter;
     private final KeycloakGateway keycloakGateway;
     private final Cache<String, TokenDto> tokenCache;
 
     @Autowired
-    public CredentialServiceImpl(KeycloakGateway keycloakGateway, Cache<String, TokenDto> tokenCache) {
-        this.tokenCache = tokenCache;
+    public CredentialServiceImpl(AuthTokenFilter authTokenFilter, KeycloakGateway keycloakGateway,
+                                 Cache<String, TokenDto> tokenCache) {
+        this.authTokenFilter = authTokenFilter;
         this.keycloakGateway = keycloakGateway;
+        this.tokenCache = tokenCache;
     }
 
     @Override
     public TokenDto getAccessToken(String username, String password) {
         final TokenDto cacheAccessToken = tokenCache.getIfPresent(username);
         if (cacheAccessToken != null) {
-            final Instant expiry = Instant.ofEpochSecond(cacheAccessToken.getExpiresIn());
+            final Instant expiry = authTokenFilter.decodeJwt(cacheAccessToken.getAccessToken())
+                    .getExpiresAtAsInstant();
             if (!expiry.isBefore(Instant.now())) {
                 log.trace("found access token for user with username {} in cache", username);
                 return cacheAccessToken;
             } else {
-                log.debug("access token for user with username {} expired in cache: request new", username);
+                log.debug("access token for user with username {} expired on {} in cache: request new", username,
+                        expiry);
             }
         } else {
             log.debug("access token for user with username {} not it cache (anymore): request new", username);
diff --git a/dbrepo-gateway-service/dbrepo.conf b/dbrepo-gateway-service/dbrepo.conf
index 9374a079a1..d77507fa09 100644
--- a/dbrepo-gateway-service/dbrepo.conf
+++ b/dbrepo-gateway-service/dbrepo.conf
@@ -4,7 +4,7 @@ map $http_upgrade $connection_upgrade {
   '' close;
 }
 
-client_max_body_size 20G;
+client_max_body_size     20G;
 
 proxy_buffer_size        128k;
 proxy_buffers       4    256k;
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/AbstractEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/AbstractEndpoint.java
index b6f5f65cf9..d11baa387b 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/AbstractEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/ac/tuwien/ifs/dbrepo/endpoints/AbstractEndpoint.java
@@ -1,6 +1,7 @@
 package at.ac.tuwien.ifs.dbrepo.endpoints;
 
 import at.ac.tuwien.ifs.dbrepo.core.api.container.ContainerDto;
+import at.ac.tuwien.ifs.dbrepo.core.api.database.DatabaseDto;
 import at.ac.tuwien.ifs.dbrepo.core.api.user.UserDetailsDto;
 import at.ac.tuwien.ifs.dbrepo.core.entity.database.Database;
 import at.ac.tuwien.ifs.dbrepo.core.entity.database.DatabaseAccess;
diff --git a/dbrepo-ui/components/database/DatabaseToolbar.vue b/dbrepo-ui/components/database/DatabaseToolbar.vue
index 0e69b9a91b..09d40abf8e 100644
--- a/dbrepo-ui/components/database/DatabaseToolbar.vue
+++ b/dbrepo-ui/components/database/DatabaseToolbar.vue
@@ -120,7 +120,7 @@ export default {
       return this.isContrastTheme ? '' : (this.isDarkTheme ? 'tertiary' : 'secondary')
     },
     canCreateIdentifier () {
-      if (!this.roles) {
+      if (!this.roles || !this.access) {
         return false
       }
       if (this.roles.includes('create-foreign-identifier')) {
@@ -150,7 +150,7 @@ export default {
       return this.hasReadAccess
     },
     canCreateView () {
-      if (!this.cacheUser || !this.isOwner || !this.roles) {
+      if (!this.cacheUser || !this.isOwner || !this.roles || !this.access) {
         return false
       }
       return this.roles.includes('create-database-view')
diff --git a/dbrepo-ui/components/user/UserBadge.vue b/dbrepo-ui/components/user/UserBadge.vue
index 9eb679de3c..d8cfaa6e7c 100644
--- a/dbrepo-ui/components/user/UserBadge.vue
+++ b/dbrepo-ui/components/user/UserBadge.vue
@@ -9,7 +9,7 @@
       <v-chip
         size="x-small"
         inline>
-        {{ $t('navigation.you') }}
+        {{ $t('navigation.me') }}
         <v-icon
           icon="mdi-account-outline"
           end />
diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json
index 242f48a5db..3b005cc759 100644
--- a/dbrepo-ui/locales/en-US.json
+++ b/dbrepo-ui/locales/en-US.json
@@ -38,7 +38,7 @@
     "help": "Help",
     "visibility": "Visibility",
     "update": "Update",
-    "you": "You",
+    "me": "Me",
     "enabled": "Enabled",
     "disabled": "Disabled"
   },
diff --git a/dbrepo-ui/pages/database/[database_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/settings.vue
index cefd76e639..3b8c104d14 100644
--- a/dbrepo-ui/pages/database/[database_id]/settings.vue
+++ b/dbrepo-ui/pages/database/[database_id]/settings.vue
@@ -428,11 +428,10 @@ export default {
       return this.roles.includes('modify-database-image')
     },
     canViewSettings () {
-      if (!this.database || !this.cacheUser || !this.access) {
+      if (!this.database || !this.cacheUser) {
         return false
       }
-      const userService = useUserService()
-      return userService.hasReadAccess(this.access) && this.database.owner.id === this.cacheUser.uid
+      return this.database.owner.id === this.cacheUser.uid
     },
     previewImage () {
       if (this.file) {
diff --git a/docker-compose.yml b/docker-compose.yml
index 061d1d5a70..3d92b85c66 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -79,7 +79,7 @@ services:
     restart: "no"
     container_name: dbrepo-auth-service
     hostname: auth-service
-    image: bitnami/keycloak:26.0.4-debian-12-r0
+    image: docker.io/bitnami/keycloak:26.0.4-debian-12-r0
     volumes:
       - ./dbrepo-auth-service/import-realms.sh:/docker-entrypoint-initdb.d/import-realms.sh
       - ./dbrepo-auth-service/master-realm.json:/opt/keycloak/data/import/master-realm.json
@@ -99,6 +99,7 @@ services:
       KEYCLOAK_DATABASE_USER: "${AUTH_DB_USERNAME:-keycloak}"
       KEYCLOAK_DATABASE_PASSWORD: "${AUTH_DB_PASSWORD:-dbrepo}"
       KEYCLOAK_HOSTNAME: "${BASE_URL:-http://localhost}"
+      KEYCLOAK_HOSTNAME_ADMIN: "http://localhost:8080"
       METADATA_SERVICE_ENDPOINT: "${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}"
       SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
       SYSTEM_PASSWORD: "${SYSTEM_PASSWORD:-admin}"
@@ -588,9 +589,6 @@ services:
       interval: 10s
       timeout: 5s
       retries: 12
-    depends_on:
-      dbrepo-data-db:
-        condition: service_healthy
     extra_hosts:
       - "localhost:host-gateway"
     logging:
@@ -618,7 +616,7 @@ services:
       timeout: 5s
       retries: 12
     depends_on:
-      dbrepo-metric-db:
-        condition: service_started
+      dbrepo-dashboard-ui:
+        condition: service_healthy
     logging:
       driver: json-file
diff --git a/helm/dbrepo/files/create-event-listener.jar b/helm/dbrepo/files/create-event-listener.jar
index d72418146ece32374ade889a7c9972b8e1bd9eb1..4c541f2eb5bd4606250425f72aa7971a40164662 100644
GIT binary patch
delta 645
zcmbR3Kigj@z?+#xgn@yBgW+0HN0hzY)5M8FO7*Ax4jTyUaX)4MgFWXvqvvetR^||q
zCDV%T-;mBSE!3)>d};r2Uqw+-CY_%<AJ6@`Mm)i{ao!JYtNg6l8y89(xwzI@;Yv-}
zi5pB&TizU7*_(NB<Hj9UN@=~Crt@Fx#=i|{&CZmO4PR!!rIGSTzs>8+jB~Q@viNv&
zcer1dYMp+feqQyJrTciIVt4nPP2c)m$9{uqRvEvDJO9;nN4RwVo-Jqm>!z`;zT^Mo
z;=MIzZnR}QH`e_Ad#^#O_?0)jQH)*;p2^(o^I{gSD17nMb6(Hlgc6};mRl}-zMGhL
z;9GmVDCaf1>bf-!#}-^V8M?i`Nz3N@#T(D|-v0i>rA6w~#|icIN#Uy=ZQr_e#ft9g
z(A504U3#e!=DvC%u_txqLLv`ui|-Javg$Xt%<To1Hgl#Q6OJlbe#ATHY-iZg%SlFU
zDOP@;61%SdR0!SW;hrUMi{(qnq^z}*U%tF|Yij!Qy2}bD6;EkZbi7-$R9J|mHjC5k
z*hO|wG;lF6a4>+QVe&>sW#%hI9h0v!>M~y~>X^*VBnPH7m{ggs6?IGwVA2B9wM-^p
zdJB^mSUn^2SulN(xf`T@b3Dr&CNSe48#gDI!6Q&32-ecAI15Z`C{5;oMg8Q%N>(7j
zNy;W*W6YIpL7FC)E9-&j70RX{>B$e2!OAAfs<?ppDJoH5^#@gg!L+z)E?9lLYAl$3
VuIdP;wbgck)xS{70Mo(h9sqx=A%p+`

delta 645
zcmbR3Kigj@z?+#xgn@yBgCTTzdz99cR_=*HO7#bW4jBmSEnoJ3g1kr20*AN09~SVu
z;OY3Tde^JXEOk=u#Qo+XI?9h%w5!a1b9&$O?U4-^Ev+8qXUtvabE-;-t9hlX!kHgW
zW^7_L$+)pMH2Us>RA#~X0+XY}CFiGF@4L15)AeJC1zjcUL?^625^pLxV^dCYVSM3=
z%+s>B+_d+&)|cJ+yX{NvrIe|@H*<n4kDIw~JJMsz)Os*@r~U>P=laig9rg=$J>>sc
zU$k!e=P9|J-}I(_DzM;~Ywh^eak1b6mI+tXwm&#=tt+eBZ{E|B1|Qt|{}?s1S?y!L
zlVNc8VYvIAt@?fQttJ?0Y!1BhZvMhA=jUbz*KhgFXD_s`z2?H>dhs`2Q){9mo4ao9
z2~PdBZl!F{ugd8BmQ`Lm4=>izx>4Hr!E@!U{RcLFob~r`<wDtoj@5d5j=0UweC)IG
zV60J}QqN?KmrpH^?F}^XOW+Z7ivK!UMuhMD_iNjBSbd+(<|!69?Vj_NaNV^6`Y#q8
zm6l|i4~hmZ1_llWa5PNb$f(R5vb=rrbw*w0(B<uu*_q_Pv<8zZm=0jl0@JljCSZCC
zlNVS$BlB4>eUZ5vB)&PGWeyXV@sEw06U^Wds1XD)CU+~&0@E5wlR02fKl!kd6-aQB
zvI*E2b7fl)cXGM19++OCYzn3yD1((vmQ`^9^HWr!!0HdG1cPaD)m*T6yJ{?$ey-{W
SrnS{}f$0}&8DKhC-2(uml^g8<

diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/mapper/MetadataMapper.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/mapper/MetadataMapper.java
index e6c354e35e..86daa015af 100644
--- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/mapper/MetadataMapper.java
+++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/mapper/MetadataMapper.java
@@ -155,7 +155,8 @@ public interface MetadataMapper {
     ContainerBriefDto containerToContainerBriefDto(Container data);
 
     @Mappings({
-            @Mapping(target = "previewImage", expression = "java(database.getImage() != null ? \"/api/database/\" + database.getId() + \"/image\" : null)")
+            @Mapping(target = "previewImage", expression = "java(database.getImage() != null ? \"/api/database/\" + database.getId() + \"/image\" : null)"),
+            @Mapping(target = "accesses", expression = "java(database.getAccesses().stream().filter(a -> !a.getUser().getIsInternal()).map(a -> databaseAccessToDatabaseAccessDto(a)).toList())")
     })
     DatabaseDto databaseToDatabaseDto(Database database);
 
diff --git a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/test/BaseTest.java b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/test/BaseTest.java
index 5209b40ce4..3a714d0fa8 100644
--- a/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/test/BaseTest.java
+++ b/lib/java/dbrepo-core/src/main/java/at/ac/tuwien/ifs/dbrepo/core/test/BaseTest.java
@@ -262,7 +262,7 @@ public class BaseTest {
             .type(AccessTypeDto.WRITE_ALL)
             .build();
 
-    public final static String TOKEN_ACCESS_TOKEN = "ey.yee.skrr";
+    public final static String TOKEN_ACCESS_TOKEN = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ0Mk9DZUNoZUo5dXdvQmJOUWpHX25ONldLaUxjY2VUSUFabWlUYkdPREZNIn0.eyJleHAiOjE3NDQxMTI1MzksImlhdCI6MTc0NDExMTYzOSwiYXV0aF90aW1lIjoxNzQ0MDkzNTMwLCJqdGkiOiI2MWNlODZjNi1kOTYzLTQxOTUtODE2NS00MTdiNDBkZjNhMmUiLCJpc3MiOiJodHRwczovL2RicmVwbzEuZWMudHV3aWVuLmFjLmF0L3JlYWxtcy9kYnJlcG8iLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiN2JkMWE2MDEtZjYyOS00MjA3LWEyMDEtNTY3MDRiYzI5ZTVlIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiZGJyZXBvLWNsaWVudCIsInNpZCI6ImQ5Y2FjN2NiLTc2OTctNGM3OS1iODRhLWViN2ViYzgzNDFhZCIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWxldGUtZGF0YWJhc2UtdmlldyIsImV4cG9ydC1xdWVyeS1kYXRhIiwiZXhlY3V0ZS1xdWVyeSIsImRlZmF1bHQtdXNlci1oYW5kbGluZyIsImRlbGV0ZS10YWJsZS1kYXRhIiwiZmluZC1xdWVyeSIsImxpc3QtZGF0YWJhc2Utdmlld3MiLCJwZXJzaXN0LXF1ZXJ5IiwiZGVsZXRlLWRhdGFiYXNlLWFjY2VzcyIsInZpZXctdGFibGUtaGlzdG9yeSIsIm1vZGlmeS11c2VyLXRoZW1lIiwibW9kaWZ5LXZpZXctdmlzaWJpbGl0eSIsImNyZWF0ZS1zZW1hbnRpYy1jb25jZXB0IiwiZGVmYXVsdC1jb250YWluZXItaGFuZGxpbmciLCJjcmVhdGUtdGFibGUiLCJkZWZhdWx0LWJyb2tlci1oYW5kbGluZyIsImV4ZWN1dGUtc2VtYW50aWMtcXVlcnkiLCJ0YWJsZS1zZW1hbnRpYy1hbmFseXNlIiwiY2hlY2stZGF0YWJhc2UtYWNjZXNzIiwiZGVmYXVsdC12aWV3LWhhbmRsaW5nIiwiZGVsZXRlLWlkZW50aWZpZXIiLCJtb2RpZnktZGF0YWJhc2Utb3duZXIiLCJsaXN0LXRhYmxlcyIsImV4cG9ydC10YWJsZS1kYXRhIiwiY3JlYXRlLWRhdGFiYXNlLWFjY2VzcyIsInJlLWV4ZWN1dGUtcXVlcnkiLCJjcmVhdGUtc2VtYW50aWMtdW5pdCIsInVwZGF0ZS10YWJsZS1zdGF0aXN0aWMiLCJkZWZhdWx0LWRhdGFiYXNlLWhhbmRsaW5nIiwiZmluZC1kYXRhYmFzZSIsImZpbmQtZGF0YWJhc2UtdmlldyIsImltcG9ydC1kYXRhYmFzZS1kYXRhIiwicHVibGlzaC1pZGVudGlmaWVyIiwidXBkYXRlLWRhdGFiYXNlLXZpZXciLCJkZWZhdWx0LXJvbGVzLWRicmVwbyIsImNyZWF0ZS1kYXRhYmFzZSIsImRlZmF1bHQtcmVzZWFyY2hlci1yb2xlcyIsImRlZmF1bHQtaWRlbnRpZmllci1oYW5kbGluZyIsIm1vZGlmeS11c2VyLWluZm9ybWF0aW9uIiwiY3JlYXRlLWRhdGFiYXNlLXZpZXciLCJmaW5kLWNvbnRhaW5lciIsImluc2VydC10YWJsZS1kYXRhIiwidXBkYXRlLXRhYmxlIiwibW9kaWZ5LWRhdGFiYXNlLWltYWdlIiwibW9kaWZ5LXRhYmxlLWNvbHVtbi1zZW1hbnRpY3MiLCJkZWZhdWx0LXNlbWFudGljcy1oYW5kbGluZyIsInVwZGF0ZS1kYXRhYmFzZS1hY2Nlc3MiLCJkZWZhdWx0LXF1ZXJ5LWhhbmRsaW5nIiwiZmluZC10YWJsZSIsImxpc3QtcXVlcmllcyIsImNyZWF0ZS1pZGVudGlmaWVyIiwiZmluZC1pZGVudGlmaWVyIiwidmlldy10YWJsZS1kYXRhIiwiZGVmYXVsdC1zdG9yYWdlLXJvbGVzIiwiZGVmYXVsdC10YWJsZS1oYW5kbGluZyIsImxpc3QtaWRlbnRpZmllcnMiLCJsaXN0LWRhdGFiYXNlcyIsIm1vZGlmeS1kYXRhYmFzZS12aXNpYmlsaXR5IiwidXBsb2FkLWZpbGUiLCJkZWxldGUtdGFibGUiXX0sInNjb3BlIjoib3BlbmlkIiwidWlkIjoiNmY1YTc0MzQtYTQwOS0xMDNmLTk2NmItMTFiNjU4OGRkOTEzIiwiaWRlbnRpdHlfcHJvdmlkZXIiOiJzYW1sIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiMjg3NzIyIiwiZ2l2ZW5fbmFtZSI6Ik1hcnRpbiIsImZhbWlseV9uYW1lIjoiV2Vpc2UifQ.Nfp0MIKuqjrEZqQXjPNRU2MuYyIJXhQVjdg7XY5_oqkYIngCoQ0y3ioBhMGT2XHd8kufk7FEP6Kme9Ihvm1Qx6rAejcSLaA6xnhQDrX6SGGQ9Kfm_9Ewv6IHoX--Yt3aKLu_YQ4eiDdxxEP4jbl-H6hM4_vwaJYe7vcfSE1lkewno_yYrhW6btPRfrbLy4_57vBK6MLN1h8A-ePx1037KnIXIRDOu0hZwidz4mVZjQ6x3arBYT9iFQmIkgucLMriuRPF_PoEHkUajJ06Y9xQuSa9MNtr_ALkUEbGnzBCAeNwIChavxdz7Be_x1qRTeOsdVD0mHJf_ePeXjmDUtV45w";
     public final static String TOKEN_ACCESS_SCOPE = "openid";
 
     public final TokenDto TOKEN_DTO = TokenDto.builder()
-- 
GitLab