dotCMS / dotCMS/core

Apps portlet list shows 0 configurations when the secrets store is unreadable, instead of reporting the error

Open
#37,061 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

dotCMS : Security Team : Maintenance Type : Defect
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Description

The Apps portlet list reports every app as 0 configurations when the secrets store cannot be read, while opening an app's detail raises an error. An administrator sees "nothing is configured", clicks in, and gets a failure — two surfaces telling them different things about the same store.

This is a layering problem, not a missing throw. The portlet is answering an administrative question with a runtime API:

Caller Question being asked Right answer when the store is unreadable
EMAWebInterceptor, SAML, Velocity $secrets, analytics, telemetry "Is this app configured for this site?" Degrade. Treat as not configured and keep serving the request.
Apps portlet list "What is the state of App configuration?" Say so. "I cannot read the store" is the single most important thing to report.

Both go through AppsAPIImpl.filterSitesForAppKey, which AppsHelper:124 calls to compute configurationsCount. That method deliberately catches Exception and returns "no secrets for this site", because it runs on every /api/* request via EMAWebInterceptor — making it raise returns a 500 for the entire API, which is a regression that was already found and fixed once in PR #37053 (94ea3579e6).

So the fix is not to make filterSitesForAppKey throw. It is to give the admin surface an explicit store-health signal instead of inferring state from a count that is designed to degrade.

Current behaviour

With a store this node cannot decrypt (cluster-salt rotation, or a per-node SECRETS_KEYSTORE_PASSWORD_KEY):

GET /api/v1/apps                                   -> 200, every app "configurationsCount": 0
GET /api/v1/apps/{key}/{siteId}                    -> 500

Observed on a container built from PR #37053.

Impact

Misinformation, not data loss. The admin cannot destroy anything from this state — saving also goes through getSecretsStore(), which raises, so a "re-enter the credentials" reaction fails loudly rather than overwriting an intact store (verified: the store's checksum was unchanged after a save attempt). The cost is that the list actively points away from the real problem, which is a mis-set salt or password, at exactly the moment someone is trying to diagnose it.

Not a regression: before #37053 the store was silently wiped and recreated empty, so the list also showed 0 — having just destroyed the credentials.

Acceptance Criteria
  • The Apps portlet list surfaces "the App secrets store cannot be read" as an explicit condition, rather than rendering it as 0 configurations.
  • The signal is carried on its own channel — getAvailableDescriptorViewsWithErrors already returns a List<AppDescriptorLoadError> alongside the views, which is the natural place, or an equivalent field.
  • AppsAPIImpl.filterSitesForAppKey still does not raise. Its runtime callers (EMAWebInterceptor on every /api/* request, InitRunner.isUveEnabled(), SAML, telemetry) must keep degrading. A regression here returns a 500 for the whole API.
  • The UI distinguishes "0 configurations" from "state unknown" — an administrator must not read an unreadable store as an empty one.
  • The message points at the actual cause: the store could not be decrypted, usually a changed cluster salt or a per-node SECRETS_KEYSTORE_PASSWORD_KEY. The wording in SecretsKeyStoreHelper.handleUnrecoverableLoad is a good starting point.
  • The detail view's existing hard error is preserved.
  • Test coverage for the list against an unreadable store.
Additional Context

Raised in review of PR #37053 (issue #36724), which stopped an unreadable secrets store from being silently wiped. That PR documents the split at the decision point in AppsAPIImpl.filterSitesForAppKey and deliberately left closing it to a follow-up, because surfacing the condition in the list is a UI-facing change with its own signal path.

Frontend work is likely required alongside the backend signal.

Severity

Low - Cosmetic or minor issue

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with AppsAPIImpl.filterSitesForAppKey and AppsHelper:124, then trace getAvailableDescriptorViewsWithErrors and the Apps portlet list UI. Compare the existing error wording in SecretsKeyStoreHelper.handleUnrecoverableLoad. Done means the list carries and displays an unreadable-store condition separately from 0 configurations, while runtime callers still degrade and the detail error remains.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend, frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
54/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.