fix: scope admin infra pages to current tenant's tables and indices
All checks were successful
CI / build (push) Successful in 1m14s
CI / cleanup-branch (push) Has been skipped
CI / docker (push) Successful in 44s
CI / deploy (push) Has been skipped
CI / deploy-feature (push) Successful in 35s

Database tables filtered to current_schema(), active queries to
current_database(), OpenSearch indices to configured index-prefix.
Delete endpoint rejects indices outside application scope.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-18 09:29:06 +01:00
parent f01487ccb4
commit 5a0a915cc6
2 changed files with 13 additions and 3 deletions

View File

@@ -72,13 +72,14 @@ public class DatabaseAdminController {
@Operation(summary = "Get table sizes and row counts")
public ResponseEntity<List<TableSizeResponse>> getTables() {
var tables = jdbc.query("""
SELECT schemaname || '.' || relname AS table_name,
SELECT relname AS table_name,
n_live_tup AS row_count,
pg_size_pretty(pg_total_relation_size(relid)) AS data_size,
pg_total_relation_size(relid) AS data_size_bytes,
pg_size_pretty(pg_indexes_size(relid)) AS index_size,
pg_indexes_size(relid) AS index_size_bytes
FROM pg_stat_user_tables
WHERE schemaname = current_schema()
ORDER BY pg_total_relation_size(relid) DESC
""", (rs, row) -> new TableSizeResponse(
rs.getString("table_name"), rs.getLong("row_count"),
@@ -94,7 +95,7 @@ public class DatabaseAdminController {
SELECT pid, EXTRACT(EPOCH FROM (now() - query_start)) AS duration_seconds,
state, query
FROM pg_stat_activity
WHERE state != 'idle' AND pid != pg_backend_pid()
WHERE state != 'idle' AND pid != pg_backend_pid() AND datname = current_database()
ORDER BY query_start ASC
""", (rs, row) -> new ActiveQueryResponse(
rs.getInt("pid"), rs.getDouble("duration_seconds"),

View File

@@ -48,17 +48,20 @@ public class OpenSearchAdminController {
private final AuditService auditService;
private final ObjectMapper objectMapper;
private final String opensearchUrl;
private final String indexPrefix;
public OpenSearchAdminController(OpenSearchClient client, RestClient restClient,
SearchIndexerStats indexerStats, AuditService auditService,
ObjectMapper objectMapper,
@Value("${opensearch.url:http://localhost:9200}") String opensearchUrl) {
@Value("${opensearch.url:http://localhost:9200}") String opensearchUrl,
@Value("${opensearch.index-prefix:executions-}") String indexPrefix) {
this.client = client;
this.restClient = restClient;
this.indexerStats = indexerStats;
this.auditService = auditService;
this.objectMapper = objectMapper;
this.opensearchUrl = opensearchUrl;
this.indexPrefix = indexPrefix;
}
@GetMapping("/status")
@@ -109,6 +112,9 @@ public class OpenSearchAdminController {
List<IndexInfoResponse> allIndices = new ArrayList<>();
for (JsonNode idx : indices) {
String name = idx.path("index").asText("");
if (!name.startsWith(indexPrefix)) {
continue;
}
if (!search.isEmpty() && !name.contains(search)) {
continue;
}
@@ -146,6 +152,9 @@ public class OpenSearchAdminController {
@Operation(summary = "Delete an OpenSearch index")
public ResponseEntity<Void> deleteIndex(@PathVariable String name, HttpServletRequest request) {
try {
if (!name.startsWith(indexPrefix)) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Cannot delete index outside application scope");
}
boolean exists = client.indices().exists(r -> r.index(name)).value();
if (!exists) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Index not found: " + name);