dotCMS / dotCMS/core

Add granular per-API-family control over anonymous REST/GraphQL access (independent of custom /api/vtl endpoints)

Open
#36,882 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Description

Reported via Freshdesk #38556 (customer on dotCMS 25.07.10 LTS, self-hosted).

A customer's security team needs to disable anonymous access to the built-in content/query/GraphQL REST APIs (e.g. /api/content, /api/v1/content, /api/es, /api/v1/graphql, /api/widget) while keeping their custom /api/vtl/* scripting endpoints publicly (anonymously) accessible. dotCMS provides no way to make that split today.

Current behavior

CONTENT_APIS_ALLOW_ANONYMOUS is a single global switch (NONE / READ / WRITE, default READ). Both the built-in content/GraphQL APIs and custom VTL endpoints resolve their anonymous access through this same setting:

  • AnonymousAccess.systemSetting() reads CONTENT_APIS_ALLOW_ANONYMOUS (default READ).
  • WebResource.init(...) sets access = rejectWhenNoUser ? NONE : systemSetting() and checkAnonymousPermissions(...) throws 401 when systemSetting() == NONE.
  • GraphQL: DotGraphQLContextBuilder authenticates via WebResource with requiredAnonAccess(AnonymousAccess.systemSetting()).
  • Custom VTL: VTLResource.processRequest(...) calls webResource.init(null, request, response, false, null) → also resolves to systemSetting().

Net effect: setting CONTENT_APIS_ALLOW_ANONYMOUS=none to lock down the built-in APIs also disables the customer's custom /api/vtl/* endpoints, because they share the one switch. There is no per-endpoint / per-API-family granularity, and no GraphQL-specific toggle.

Why existing options don't suffice
  • Rules engine is not a viable REST auth boundary. CMSFilter fires RulesEngine.fireRules(EVERY_REQUEST), but the GraphQL endpoint is a standalone servlet (DotGraphQLHttpServlet, mapped to /api/v1/graphql in web.xml) that performs its own auth and returns its own response, so URL/redirect rules do not reliably intercept it. Relying on Rules to gate REST endpoints is fragile and unsupported.
  • Removing the CMS Anonymous role from contentlets breaks legitimate anonymous VTL usage — the customer's Velocity $dotcontent.pull() search hubs run in an anonymous request context and stop resolving.
  • Velocity Secrets require an authenticated user, so they can't back anonymous VTL either.
Desired capability

A supported, granular way to disable anonymous access to the built-in content/query/GraphQL API families independently of custom /api/vtl/* scripting endpoints — without requiring customers to duplicate gating at an edge/reverse proxy.

Acceptance Criteria

  • An administrator can disable anonymous access to the built-in content/query/GraphQL API families (/api/content, /api/v1/content, /api/es, /api/v1/graphql, /api/widget) without disabling anonymous access to custom /api/vtl/* endpoints.
  • With the new control active, an anonymous request to /api/v1/graphql returns 401/403.
  • With the new control active, an anonymous request to /api/content (and /api/v1/content, /api/es) returns 401/403.
  • With the new control active, an anonymous request to a custom /api/vtl/* endpoint still succeeds, and $dotcontent.pull() inside that VTL executes with the expected anonymous results.
  • Existing CONTENT_APIS_ALLOW_ANONYMOUS behavior is preserved (default unchanged) when the new control is not configured — no breaking change on upgrade.
  • The control's behavior and its interaction with CONTENT_APIS_ALLOW_ANONYMOUS and REST_API_REJECT_WITH_NO_USER are documented.
  • Integration/Postman coverage: each built-in API family blocked-when-anonymous, and VTL allowed-when-anonymous.

Open Design Questions (for eng/product triage)

  • Mechanism shape: per-API-family config keys? an include/exclude path list? or role-scoping that distinguishes the VTL execution context from direct content-API calls?
  • Should GraphQL be independently toggleable from the classic content APIs, or is one "built-in content APIs" group sufficient?
  • Precedence: how does the new control interact with CONTENT_APIS_ALLOW_ANONYMOUS (per-family override of the global default?) and with REST_API_REJECT_WITH_NO_USER?

Additional Context

Source references (main @ 0d85453):

  • dotCMS/.../com/dotcms/rest/AnonymousAccess.javaCONTENT_APIS_ALLOW_ANONYMOUS, default READ.
  • dotCMS/.../com/dotcms/rest/WebResource.java:204access = rejectWhenNoUser ? NONE : systemSetting(); :366-382checkAnonymousPermissions.
  • dotCMS/.../com/dotcms/graphql/DotGraphQLContextBuilder.javarequiredAnonAccess(AnonymousAccess.systemSetting()).
  • dotCMS/.../com/dotcms/rest/api/v1/vtl/VTLResource.javawebResource.init(null, request, response, false, null).
  • dotCMS/.../com/dotmarketing/filters/CMSFilter.java:93RulesEngine.fireRules(EVERY_REQUEST).
  • dotCMS/src/main/webapp/WEB-INF/web.xmlDotGraphQLHttpServlet/api/v1/graphql.

Current workaround provided to the customer: gate by path at the edge/reverse proxy — deny anonymous traffic to the built-in API families and allow-list /api/vtl/*.

Related (already fixed, different problem): #35037 / PR #35038 — anonymous GraphQL permission check.

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 AnonymousAccess.java, WebResource.java, DotGraphQLContextBuilder.java, VTLResource.java, and web.xml to trace how anonymous access is resolved for each API family. Review the existing integration/Postman coverage and compare REST_API_REJECT_WITH_NO_USER with CONTENT_APIS_ALLOW_ANONYMOUS. Done means the approved control blocks anonymous built-in APIs while preserving anonymous VTL behavior, keeps existing defaults, and documents the interaction.

Written by the indexing model from the issue text.

Assessment

Tech stack
graphql, java
Domain
api, authentication, backend-api-design, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.