Conversation
…for anonymous users ContentTypeAPIImpl.find() was calling doesUserHavePermission() without passing respectFrontendRoles, hardcoding it to false. This caused the CMS Anonymous frontend role to be ignored, throwing a DotSecurityException when anonymous GraphQL queries traversed relationship fields — even when the content type had CMS Anonymous VIEW permission explicitly set. Also fixed both overloads of RelationshipAPIImpl.getRelationshipFromField() to use getContentTypeAPI(user, true) since this method is called from frontend/GraphQL contexts where frontend roles must be respected. Fixes #35037 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Claude finished @fmontes's task in 3m 40s —— View job Code ReviewThe behavioral fix is sound: splitting the combined 1. Misleading Javadoc in test 1 (lines 102–107) The Javadoc says the pre-fix failure happened "even when CMS Anonymous had VIEW permission on the related content type." That framing is inaccurate based on the code.
The Javadoc comment should reflect what the fix actually does rather than the misdescribed root cause from the issue. 2. Test 1 is not a regression test for the original bug Since the 3-arg overload already hardcoded 3. Vacuous
// line 195 — this can never fail, assertFalse on isEmpty is the real guard
assertNotNull("Errors list should not be null", dataFetcherResult.getErrors());4. Missing The PR description states that the denied case returns assertNull("Data should be null when permission is denied", dataFetcherResult.getData());5. new FieldRelationshipDataGen()
.parent(parentType)
.child(childType)
.persist(null); // ← non-obvious; readers will wonder why null is passedA short comment clarifying that The production change in |
…ntend roles Adds regression tests for #35037: - ContentTypeAPIImplTest: verifies find() respects CMS Anonymous frontend role when respectFrontendRoles=true, and still denies access when permission is absent - RelationshipAPITest: verifies getRelationshipFromField() resolves correctly for anonymous users when the content type has CMS Anonymous READ permission Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes anonymous GraphQL relationship traversal failures by ensuring ContentType permission checks can include frontend roles (e.g., CMS Anonymous) when the calling API instance is configured to do so.
Changes:
- Update
ContentTypeAPIImpl.find()to pass the instance’srespectFrontendRolesflag into permission checks. - Update both
RelationshipAPIImpl.getRelationshipFromField()overloads to build aContentTypeAPIwithrespectFrontendRoles=true. - Add integration regression tests covering anonymous access scenarios for
ContentTypeAPI.find()and relationship resolution.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| dotCMS/src/main/java/com/dotcms/contenttype/business/ContentTypeAPIImpl.java | Makes find() permission enforcement depend on the API instance’s respectFrontendRoles setting. |
| dotCMS/src/main/java/com/dotmarketing/business/RelationshipAPIImpl.java | Forces relationship resolution-from-field to use a ContentTypeAPI configured to respect frontend roles. |
| dotcms-integration/src/test/java/com/dotcms/contenttype/test/ContentTypeAPIImplTest.java | Adds regression coverage for anonymous find() with/without CMS Anonymous READ permission. |
| dotcms-integration/src/test/java/com/dotcms/contenttype/business/RelationshipAPITest.java | Adds regression coverage for anonymous relationship resolution when CMS Anonymous has READ on the parent type. |
…tionship metadata lookup The relationship lookup in getRelationshipFromField() is a structural metadata operation (finding the Relationship record by field definition), not a content access check. Passing the request user (e.g. anonymous) caused a DotSecurityException when the user's ContentType permission check ran without frontend roles being considered. The fix uses systemUser() for this lookup — consistent with RelationshipFieldGenerator which already does the same — while content access is correctly enforced separately via pullRelatedField() with the original request user. Also reverts the overly broad changes to ContentTypeAPIImpl.find() (the 3-arg doesUserHavePermission(ContentType) overload already defaults to respectFrontendRoles=true) and RelationshipAPIImpl (hardcoding true would affect non-GraphQL call sites). Fixes #35037 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eld fetching Replaces the systemUser-based test (which provided no regression protection) with a proper regression test in RelationshipFieldDataFetcherTest that: - Creates two content types with a relationship field - Grants CMS Anonymous READ permission on the parent content type - Invokes RelationshipFieldDataFetcher.get() with an anonymous user context - Asserts no DotSecurityException is thrown This test would have failed on the unpatched codebase, directly covering the regression described in #35037. Also registers RelationshipFieldDataFetcherTest in MainSuite1b. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…herTest Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…dDataFetcherTest MANY_TO_MANY returns an empty List when no related content exists, not null. Asserting non-null was misleading and could fail for ONE_TO_ONE cardinality. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Pull Request Unsafe to Rollback!!!
|
3a6763d to
d9e7ccf
Compare
nollymar
left a comment
There was a problem hiding this comment.
I think the actual fix here is to handle the exception instead of bypassing it with the system user
… instead of bypassing with systemUser Reverts the systemUser workaround in RelationshipFieldDataFetcher. DotSecurityException from getRelationshipFromField is now caught and returned as a DataFetcherResult with a PermissionDeniedGraphQLException error, consistent with the FolderCollectionDataFetcher pattern. Adds regression test for the permission-denied path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Pull Request Unsafe to Rollback!!!
|
|
Pull Request Unsafe to Rollback!!!
|
… relationship fetcher test A new content type inherits permissions from the system host, which grants anonymous read by default. The test for the permission-denied path needs explicit individual permissions (admin-only) to break inheritance and prevent CMS Anonymous from passing the ContentType.find() check. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nymous in relationship test CMS Admin role has editPermissions=false and cannot be used in permission.save(). Instead, create a temporary custom role (editPermissions=true by default via RoleDataGen) and grant it READ on the restricted content type. This breaks permission inheritance so anonymous is excluded from individual permissions, triggering DotSecurityException in getRelationshipFromField() as the test expects. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…aFetcherTest - Remove conflicting Field import (would not compile alongside graphql.language.Field) - Use fully-qualified names for com.dotcms.contenttype.model.field.Field references - Use ContentTypeDataGen.remove() (sync) instead of async ContentTypeAPI.delete() - Add restrictedContentlet cleanup in finally block - Fix DataFetcherResult to typed builder (<Object>newResult()) - Correct error message: "relationship metadata" instead of "related content type" - Update first-test Javadoc to accurately describe the fix mechanism Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Pull Request Unsafe to Rollback!!!
|
Suite order matters — new test classes must be appended last. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Pull Request Unsafe to Rollback!!!
|
gql-relationship-field-permissions.mp4
Summary
Internal Server Erroreven when the content type had CMS Anonymous VIEW permission setDotSecurityExceptionfromgetRelationshipFromField()and returns aDataFetcherResultwith aPermissionDeniedGraphQLExceptionin the GraphQLerrorsarray — consistent with howFolderCollectionDataFetcherhandles denied subfoldersRoot Cause
RelationshipFieldDataFetcher.get()passes the request user togetRelationshipFromField(), which internally callsContentTypeAPI.find().ContentTypeAPIis instantiated withrespectFrontendRoles=falseby default, causingfilterUserRoles()inPermissionBitAPIImplto strip the CMS Anonymous role from the user's role set, making the permission check fail even when CMS Anonymous has READ explicitly set.Approach
Rather than bypassing the permission check with
systemUser(), the fetcher now handlesDotSecurityExceptionexplicitly and surfaces it as a GraphQL-level error. The anonymous user receivesnulldata for the relationship field plus aPERMISSION_DENIEDerror in theerrorsarray — the same patternFolderCollectionDataFetcheruses when a user can't access a subfolder.{ "data": { "Blog": { "relatedField": null } }, "errors": [{ "message": "You do not have permission to access the related content type for field 'relatedField'", "extensions": { "code": "PERMISSION_DENIED" } }] }Changes
RelationshipFieldDataFetcher.java— catchDotSecurityExceptionfromgetRelationshipFromField()and returnDataFetcherResultwithPermissionDeniedGraphQLExceptionRelationshipFieldDataFetcherTest.java— regression test for the permission-granted path + new test for the permission-denied path assertingDataFetcherResultwith the correct error typeFixes #35037
Test plan
nulldata withPERMISSION_DENIEDinerrors, noInternal Server Error🤖 Generated with Claude Code