spring-projects/spring-security · error · AccessDeniedException

Authenticated principal required to operate with ACLs

Error message

Authenticated principal required to operate with ACLs

What it means

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.

Source

Thrown at acl/src/main/java/org/springframework/security/acls/domain/AclAuthorizationStrategyImpl.java:97

				"One or three GrantedAuthority instances required");
		if (auths.length == 3) {
			this.gaTakeOwnership = auths[0];
			this.gaModifyAuditing = auths[1];
			this.gaGeneralChanges = auths[2];
		}
		else {
			this.gaTakeOwnership = auths[0];
			this.gaModifyAuditing = auths[0];
			this.gaGeneralChanges = auths[0];
		}
	}

	@Override
	public void securityCheck(Acl acl, int changeType) {
		SecurityContext context = this.securityContextHolderStrategy.getContext();
		if ((context == null) || (context.getAuthentication() == null)
				|| !context.getAuthentication().isAuthenticated()) {
			throw new AccessDeniedException("Authenticated principal required to operate with ACLs");
		}
		Authentication authentication = context.getAuthentication();
		// Check if authorized by virtue of ACL ownership
		Sid currentUser = createCurrentUser(authentication);
		Sid owner = acl.getOwner();
		if (owner != null && currentUser.equals(owner)
				&& ((changeType == CHANGE_GENERAL) || (changeType == CHANGE_OWNERSHIP))) {
			return;
		}

		// Iterate this principal's authorities to determine right
		Collection<? extends GrantedAuthority> reachableGrantedAuthorities = this.roleHierarchy
			.getReachableGrantedAuthorities(authentication.getAuthorities());
		Set<String> authorities = AuthorityUtils.authorityListToSet(reachableGrantedAuthorities);
		if (owner instanceof GrantedAuthoritySid
				&& authorities.contains(((GrantedAuthoritySid) owner).getGrantedAuthority())) {
			return;
		}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Ensure a fully authenticated Authentication is set in the SecurityContext before ACL mutation calls
  2. In background jobs, set a system/principal authentication explicitly: SecurityContextHolder.getContext().setAuthentication(auth)
  3. Propagate SecurityContext to async threads via DelegatingSecurityContextAsyncTaskExecutor
  4. Verify anonymous authentication is not leaking into ACL administration code paths

Example fix

// before
// background thread: no security context
mutableAclService.createAcl(objectIdentity);

// after
Authentication auth = new UsernamePasswordAuthenticationToken(
    "systemUser", "n/a", List.of(new SimpleGrantedAuthority("ROLE_ADMIN")));
SecurityContextHolder.getContext().setAuthentication(auth);
mutableAclService.createAcl(objectIdentity);
Defensive patterns

Strategy: validation

Validate before calling

Authentication a = SecurityContextHolder.getContext().getAuthentication();
if (a == null || !a.isAuthenticated() || a instanceof AnonymousAuthenticationToken) {
    throw new AccessDeniedException("ACL mutation requires an authenticated principal");
}

Type guard

null

Try / catch

try {
    mutableAclService.updateAcl(acl);
} catch (AccessDeniedException e) {
    log.error("No authenticated principal for ACL operation");
    throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/afcf640d4bab1ca5. Report an issue: GitHub.