Skip to content

JCU/perf(api): stop asking the API questions an anonymous user cannot answer - #1437

Open
Kasinhou wants to merge 1 commit into
customer/jcufrom
jcu/skip-admin-api-calls-for-anonymous-users
Open

JCU/perf(api): stop asking the API questions an anonymous user cannot answer#1437
Kasinhou wants to merge 1 commit into
customer/jcufrom
jcu/skip-admin-api-calls-for-anonymous-users

Conversation

@Kasinhou

@Kasinhou Kasinhou commented Aug 6, 2026

Copy link
Copy Markdown

Fixes the frontend part of L3 from the dataquest-dev/dspace-customers#853 review.

Reproduced

Anonymous, cold cache, local Docker DSpace 9.3 with the JCU configuration. Every request the review
lists is real, plus one it does not (google.analytics.key):

Page Request Status
every page /api/system/scripts/metadata-export 401
every page /api/system/scripts/metadata-import 401
every page /api/config/properties/contentreport.enable 404
every page /api/config/properties/google.analytics.key 404
item page /api/config/correctiontypes/search/findByItem 401
item page /api/integration/qualityassurancesources/search/byTarget 401
item page /api/ldn/notifyrequests/<uuid> 401
/search /api/config/properties/bulkedit.export.max.items 404
/register /api/config/properties/authentication-password.domain.valid 404

That is 4 failed requests on the home page and 6 on an item page, for every visitor, on every
page view.

Two different causes, and the review's remedy only fits one of them

The review proposes adding the properties to rest.properties.exposed. That is right for exactly one
of the nine — and the split matters, because it decides which repository the fix belongs in.

The 401s are a frontend problem. Five call sites request something, then check whether the user
was allowed to ask. The permission they check is already known — in every case the corresponding
/authz/authorizations/search/object?feature=… request is fired anyway, for other reasons, before
these calls go out. So we already had the answer and asked anyway. ScriptDataService even documents
it: "user needs to be allowed to execute script for this to not throw a 401 Unauthorized".

The 404s are backend configuration, and not the way the review describes:

All four of contentreport.enable, bulkedit.export.max.items,
authentication-password.domain.valid and google.analytics.key are already listed in
rest.properties.exposed in modules/rest.cfg. ConfigurationRestRepository.findOne() throws
ResourceNotFoundException when the property is not exposed or when
!configurationService.hasProperty(property) — and DSpace ships all four commented out. Exposing
them changes nothing; they have to be set. (google.analytics.key is a fifth 404 the review
missed.)

Those live in local.cfg on the server, not in this repository, so they are not in this PR. The
exact lines are at the bottom, together with the one that needs a decision from JCU
(authentication-password.domain.valid — whether registration should be restricted to @jcu.cz).

What this PR changes

File Was Now
export.menu.ts combineLatest([isAdmin$, scriptExists$]) ask for the script only when AdministratorOf
import.menu.ts same same
create-report.menu.ts combineLatest([contentreportEnabled$, isAdmin$]), duplicated in both methods one isReportMenuAvailable(), config read only when AdministratorOf
withdrawn-reinstate-item.menu.ts findByItem() on every item page only when isAuthenticated() — you cannot request a withdrawal while logged out
qa-event-notification.component.ts getSourcesByTarget() on every item page only when CanSeeQA
notify-requests-status.component.ts getNotifyRequestsStatus() on every item page only when isCoarConfigEnabled() (CoarNotifyEnabled)

Nothing is hidden that was visible before: in each case the item was already visible: false for a
user without the permission — the fix only stops asking. And no request is added: every
authorization used as the gate was already being fetched.

Two incidental notes:

  • notify-requests-status.component.ts had its NotifyRequestsStatusDataService field named
    notifyInfoService, which is the name of a different service that I now inject next to it.
    Renamed the field to notifyRequestsStatusDataService.
  • qa-event-notification.component.ts had catchError(() => []). RxJS treats a bare array as a
    source that emits each element, so an empty array emits nothing — the intent was clearly
    of([]), which is what it says now. No visible difference (the template renders nothing either
    way), but it is what the code meant.

Verified

Same measurement, same stack, on this branch:

                      before   after
home page              4        1
item page              6        1

The one remaining is google.analytics.key, which is the backend config item.

Logged in as a site administrator the three admin requests fire again, exactly as before, and the
menus still build — sidebar → ExportMetadata, Batch Export (ZIP).

  • Touched specs: 24/24, with a new "does not call X when not authorized" test for each of the six
    call sites.
  • Wider suites src/app/shared/menu/** + src/app/item-page/**: 692 passing, 11 failing — all 11
    in edit-relationship-list.component.spec.ts, which fails identically on customer/jcu without
    this branch (verified by stashing).
  • npm run lint → 0 errors.

Still to do in local.cfg (server side, not this repo) — already verified

# L3 - all four are already in rest.properties.exposed; they 404 only because they are unset.
contentreport.enable = false
bulkedit.export.max.items = 500

# Analytics is not configured; an empty value returns 200 with no values, which is what
# GoogleAnalyticsService expects when analytics is off (it checks isEmpty(trackingId)).
google.analytics.key =

# JCU's decision, not mine. Empty = no restriction (today's behaviour);
# set to jcu.cz to only allow @jcu.cz self-registration.
authentication-password.domain.valid =

Applied to the local Docker stack, this turns all four 404s into 200s with nothing else regressing,
and with this PR an anonymous visitor then generates zero 4xx REST responses on /home, an item
page, /search and /register.

Do not add rest.properties.exposed to local.cfg. A value there replaces the whole array
from modules/rest.cfg rather than appending to it. I tried it while verifying and it silently
404'd registration.verification.enabled, matomo.enabled and themed.by.url. Nothing needs
adding anyway — every property involved is already exposed.

Full write-up, including the H3 name/e-mail change and the OAI cache purge it requires, is in
C:\Users\MatusKasak\jcu-853-H3-L3-local-cfg-patch.md.

Upstream and other customers

All six files are byte-for-byte identical to upstream/dspace-9_x, and upstream/main still has
the same eager combineLatest([permission$, request$]) shape — so this is upstream behaviour, not a
JCU regression, and every DSpace 7/8/9/10 instance does it. There is no upstream issue for it
(searched "401 anonymous requests", "unnecessary requests anonymous", "contentreport.enable"). The
change is small and generic and is worth offering upstream; it is a straight win for any repository
whose traffic is mostly anonymous, which is all of them.

None of our other customer branches has it either.

Evidence

Screenshots in C:\Users\MatusKasak\jcu-853-evidence\ (to be attached here):

  • L3-1-before-anonymous-401-404-requests.png
  • L3-2-after-anonymous-calls-gone.png
  • L3-3-after-local-item-page-unchanged.png
  • L3-4-after-local-admin-export-menu-still-works.png

… answer

An anonymous visitor produced 4 failed REST requests on every page and 6 on an
item page. The frontend fires them as feature detection, then discards the
error - nothing is broken, but every page view writes 401s into the backend log
and errors into the browser console, and each one is a round trip nobody needs.

Five call sites asked first and checked the permission afterwards; they now
check first:

  export.menu / import.menu        /api/system/scripts/metadata-{export,import}
                                   -> only when the user is a site administrator
  create-report.menu               /api/config/properties/contentreport.enable
                                   -> only when the user is a site administrator
  withdrawn-reinstate-item.menu    /api/config/correctiontypes/search/findByItem
                                   -> only when the user is logged in
  qa-event-notification            /api/integration/qualityassurancesources/search/byTarget
                                   -> only when the user may see QA events (canSeeQA)
  notify-requests-status           /api/ldn/notifyrequests/<uuid>
                                   -> only when COAR Notify is enabled for the user

The authorizations these now depend on are already fetched for other reasons, so
no request is added. Behaviour for a user who does have the permission is
unchanged.

The remaining 404s (google.analytics.key, bulkedit.export.max.items,
authentication-password.domain.valid) come from properties that are unset or not
exposed in the backend and belong in local.cfg, not here.

Fixes the frontend part of L3 from dataquest-dev/dspace-customers#853.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Kasinhou

Kasinhou commented Aug 6, 2026

Copy link
Copy Markdown
Author

will not do it for now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant