{"record":{"id":"afcf640d4bab1ca5","repo":"spring-projects/spring-security","slug":"authenticated-principal-required-to-operate-with-a","errorCode":null,"errorMessage":"Authenticated principal required to operate with ACLs","messagePattern":"Authenticated principal required to operate with ACLs","errorType":"exception","errorClass":"AccessDeniedException","httpStatus":403,"severity":"error","filePath":"acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java","lineNumber":97,"sourceCode":"\t\t\t\t\"One or three GrantedAuthority instances required\");\n\t\tif (auths.length == 3) {\n\t\t\tthis.gaTakeOwnership = auths[0];\n\t\t\tthis.gaModifyAuditing = auths[1];\n\t\t\tthis.gaGeneralChanges = auths[2];\n\t\t}\n\t\telse {\n\t\t\tthis.gaTakeOwnership = auths[0];\n\t\t\tthis.gaModifyAuditing = auths[0];\n\t\t\tthis.gaGeneralChanges = auths[0];\n\t\t}\n\t}\n\n\t@Override\n\tpublic void securityCheck(Acl acl, int changeType) {\n\t\tSecurityContext context = this.securityContextHolderStrategy.getContext();\n\t\tif ((context == null) || (context.getAuthentication() == null)\n\t\t\t\t|| !context.getAuthentication().isAuthenticated()) {\n\t\t\tthrow new AccessDeniedException(\"Authenticated principal required to operate with ACLs\");\n\t\t}\n\t\tAuthentication authentication = context.getAuthentication();\n\t\t// Check if authorized by virtue of ACL ownership\n\t\tSid currentUser = createCurrentUser(authentication);\n\t\tSid owner = acl.getOwner();\n\t\tif (owner != null && currentUser.equals(owner)\n\t\t\t\t&& ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Iterate this principal's authorities to determine right\n\t\tCollection<? extends GrantedAuthority> reachableGrantedAuthorities = this.roleHierarchy\n\t\t\t.getReachableGrantedAuthorities(authentication.getAuthorities());\n\t\tSet<String> authorities = AuthorityUtils.authorityListToSet(reachableGrantedAuthorities);\n\t\tif (owner instanceof GrantedAuthoritySid\n\t\t\t\t&& authorities.contains(((GrantedAuthoritySid) owner).getGrantedAuthority())) {\n\t\t\treturn;\n\t\t}","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java#L79-L115","documentation":"AclAuthorizationStrategyImpl.securityCheck() guards ACL modification operations (change ownership, auditing, general modifications). It throws AccessDeniedException when there is no authenticated principal in the SecurityContext at all — the operation cannot even be attributed to a user.","triggerScenarios":"Calling mutableAclService.createAcl/updateAcl/deleteAcl (or acl.setOwner/setEntriesInheriting etc.) while SecurityContextHolder holds null context, null Authentication, or an Authentication with isAuthenticated()==false (e.g. AnonymousAuthenticationToken).","commonSituations":"Performing ACL writes in background threads/async tasks/schedulers where no SecurityContext exists; calling ACL APIs before authentication is established; forgetting to propagate SecurityContext to @Async/executors.","solutions":["Ensure a fully authenticated Authentication is set in the SecurityContext before ACL mutation calls","In background jobs, set a system/principal authentication explicitly: SecurityContextHolder.getContext().setAuthentication(auth)","Propagate SecurityContext to async threads via DelegatingSecurityContextAsyncTaskExecutor","Verify anonymous authentication is not leaking into ACL administration code paths"],"exampleFix":"// before\n// background thread: no security context\nmutableAclService.createAcl(objectIdentity);\n\n// after\nAuthentication auth = new UsernamePasswordAuthenticationToken(\n    \"systemUser\", \"n/a\", List.of(new SimpleGrantedAuthority(\"ROLE_ADMIN\")));\nSecurityContextHolder.getContext().setAuthentication(auth);\nmutableAclService.createAcl(objectIdentity);","handlingStrategy":"validation","validationCode":"Authentication a = SecurityContextHolder.getContext().getAuthentication();\nif (a == null || !a.isAuthenticated() || a instanceof AnonymousAuthenticationToken) {\n    throw new AccessDeniedException(\"ACL mutation requires an authenticated principal\");\n}","typeGuard":"null","tryCatchPattern":"try {\n    mutableAclService.updateAcl(acl);\n} catch (AccessDeniedException e) {\n    log.error(\"No authenticated principal for ACL operation\");\n    throw e;\n}","preventionTips":["Never call ACL write APIs from unauthenticated threads without establishing an Authentication","Use DelegatingSecurityContext* executors for async ACL work","Assert a populated SecurityContext in integration tests for ACL paths"],"tags":["spring-security","acl","authentication","security-context","access-denied"],"backgroundTag":"authentication-required","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}