Skip to content

Core: include missing privilege and target entity in PolarisAuthorizer 403 messages - #4406

Merged
flyrain merged 1 commit into
apache:mainfrom
iprithv:improve/auth-failure-missing-privilege-details
Jul 10, 2026
Merged

flyrain merged 1 commit into
apache:mainfrom
iprithv:improve/auth-failure-missing-privilege-details

Conversation

@iprithv

@iprithv iprithv commented May 11, 2026 •

Copy link
Copy Markdown
Contributor

PolarisAuthorizerImpl.authorizeOrThrow today produces only:

Principal 'alice' with activated PrincipalRoles '[reader]' and activated grants via '[]' is not authorized for op CREATE_TABLE_DIRECT

the operator has no way to tell which privilege is missing or on which entity, and routinely has to grep the codebase to figure out which grant to add. The explicit TODO at the old short-circuit in isAuthorized ("Collect missing privileges to report all at the end and/or return to code that throws NotAuthorizedException for more useful messages.") calls this out.

and this is a diagnostic improvement, not a behavioural change, every operation that was authorized before is still authorized, every one denied is still denied. Only the text of the 403 changes.

Principal 'alice' with activated PrincipalRoles '[reader]' and activated grants via '[]' is not authorized for op CREATE_TABLE_DIRECT; missing TABLE_CREATE on NAMESPACE 'ns1'

secondary failures are labelled (secondary) so operators can tell which side of a RENAME_TABLE-style op needs grants. Multiple missing privileges are joined with , in one message.

new SPI's AuthorizationDecision.deny(e.getMessage()) in PolarisAuthorizerImpl.authorize() inherits the richer text automatically.

@dimas-b dimas-b left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution, @iprithv !

operator has no way to tell which privilege is missing or on which entity [...]

I believe a more robust solution would be to log these details and return a matching unique ID of that message to the client. This way the Polaris Admin can correlated the client-side error to AuthZ grants without exposing any extra (potentially sensitive) information to clients.

That said, please open a corresponding discussion on the dev ML. I think all AuthZ changes should be discussed on dev.

findMissingPrivileges(polarisPrincipal, activatedEntities, authzOp, targets, secondaries);
if (!missing.isEmpty()) {
throw new ForbiddenException(
"Principal '%s' with activated PrincipalRoles '%s' and activated grants via '%s' is not authorized for op %s; missing %s",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not think exposing authZ details to the client in case of denied responses is a good idea. This information can potentially be used by malicious clients to further attacks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with @dimas-b. Deny responses should just tell the caller that they're denied, not the shape of the wall they're hitting. Instead, logging the missing privileges should be good.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok, I get it about not exposing authZ details to clients. updated. thanks!

@github-actions

Copy link
Copy Markdown

This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

@github-actions github-actions Bot added the stale label Jun 12, 2026
@github-actions github-actions Bot closed this Jun 17, 2026
@github-project-automation github-project-automation Bot moved this from PRs In Progress to Done in Basic Kanban Board Jun 17, 2026
iprithv added a commit to iprithv/polaris that referenced this pull request Jul 7, 2026
Addresses security review feedback on PR apache#4406:
- Detailed missing privilege info (e.g. 'TABLE_CREATE on NAMESPACE ns1')
  is now logged at INFO level server-side instead of being included in
  the client-facing ForbiddenException message.
- The client-facing 403 response remains generic to avoid leaking
  authorization metadata to untrusted clients, per SECURITY-THREAT-MODEL.md.
- Operators can correlate client errors to server logs via the existing
  X-Request-ID header already stamped on every request.
- findMissingPrivileges() and MissingPrivilege record are retained for
  server-side logging and debugging.

Fixes apache#4406
@dimas-b dimas-b reopened this Jul 7, 2026
@github-project-automation github-project-automation Bot moved this from Done to PRs In Progress in Basic Kanban Board Jul 7, 2026
@iprithv
iprithv requested review from dimas-b and flyrain July 8, 2026 02:11
@github-actions github-actions Bot removed the stale label Jul 8, 2026

@dimas-b dimas-b left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional changes LGTM, but it looks like this PR accumulated some unrelated changes? 🤔

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file looks odd in current PR 🤔 Would you mind re-applying the functional commits to main from scratch?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — those were accidental gradle/server-test-runner/.gradle cache artifacts that should never have been committed (they're gitignored).

Re-applied the functional changes onto current main from scratch in a single clean commit (review changes). The PR now only touches:

  • PolarisAuthorizerImpl.java
  • PolarisAuthorizerImplTest.java
  • CHANGELOG.md (under Unreleased)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, done. thanks!

Comment on lines +942 to +948
LOGGER.info(
"Authorization denied for principal '{}' on operation '{}': missing {}",
polarisPrincipal.getName(),
authzOp,
missingDetails);
throw new ForbiddenException(
"Principal '%s' with activated PrincipalRoles '%s' and activated grants via '%s' is not authorized for op %s",

@dimas-b dimas-b Jul 10, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach looks reasonable to me. I think we can merge "as is".

However, as I commented earlier, as more admin-friendly approach might be to add a UUID to this message and also log it on line 942. Whoever needs to investigate the authZ denial, can use the UUID to find related log messages easily (hopefully).

I'll leave it up to your @iprithv to choose which way to go for this PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — going with the existing request-ID correlation path for this PR (as suggested by @sungwy on the ML).

Default logging already includes %X{requestId} via MDC (LoggingMDCFilter + RequestIdFilter), and the client already receives X-Request-ID. Operators can match a client-side 403 to the server-side INFO log that lists the missing privileges without putting authZ details (or a second synthetic UUID) into the response body.

Leaving the client message generic as-is; happy to follow up later if we want the request ID duplicated into the ForbiddenException text as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — using the existing request ID / X-Request-ID is enough here. Default log format already stamps requestId from MDC, so no extra UUID in this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah okay, went with existing request-ID correlation. default logs include MDC requestId.. client gets X-Request-ID...leaving client message generic...thanks!

@iprithv
iprithv force-pushed the improve/auth-failure-missing-privilege-details branch from a89e0b8 to 801b2af Compare July 10, 2026 05:47
@iprithv
iprithv requested a review from dimas-b July 10, 2026 06:08
Re-apply auth-failure missing-privilege logging onto main from scratch:
server-side INFO logs with missing privilege details, generic client 403,
and drop accidentally committed gradle/server-test-runner/.gradle cache files.
@iprithv
iprithv force-pushed the improve/auth-failure-missing-privilege-details branch from 801b2af to 89b5e39 Compare July 10, 2026 06:10
@github-project-automation github-project-automation Bot moved this from PRs In Progress to Ready to merge in Basic Kanban Board Jul 10, 2026

@flyrain flyrain left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 Thanks @iprithv

@flyrain
flyrain merged commit 6533382 into apache:main Jul 10, 2026
25 checks passed
@github-project-automation github-project-automation Bot moved this from Ready to merge to Done in Basic Kanban Board Jul 10, 2026
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.

3 participants