-
Notifications
You must be signed in to change notification settings - Fork 506
Return 503 instead of 500 when the metastore fails during authentication #5247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9d70ac7
704b18c
eb6c4a7
4a3fba7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,7 +25,6 @@ | |
| import io.smallrye.common.annotation.Identifier; | ||
| import jakarta.enterprise.context.RequestScoped; | ||
| import jakarta.inject.Inject; | ||
| import jakarta.ws.rs.ServiceUnavailableException; | ||
| import java.util.Map; | ||
| import java.util.Objects; | ||
| import java.util.Set; | ||
|
|
@@ -40,6 +39,7 @@ | |
| import org.apache.polaris.core.entity.PolarisGrantRecord; | ||
| import org.apache.polaris.core.entity.PrincipalEntity; | ||
| import org.apache.polaris.core.entity.PrincipalRoleEntity; | ||
| import org.apache.polaris.core.exceptions.PolarisServiceUnavailableException; | ||
| import org.apache.polaris.core.persistence.PolarisMetaStoreManager; | ||
| import org.apache.polaris.core.persistence.dao.entity.LoadGrantsResult; | ||
| import org.eclipse.microprofile.jwt.JsonWebToken; | ||
|
|
@@ -144,12 +144,10 @@ protected PrincipalEntity resolvePrincipalEntity(PolarisCredential credentials) | |
| .orElse(null); | ||
| } | ||
| } catch (Exception e) { | ||
| LOGGER | ||
| .atError() | ||
| .addKeyValue(StructuredLogKeys.ERR_MSG, e.getMessage()) | ||
| .addKeyValue(StructuredLogKeys.STACK_TRACE, Throwables.getStackTraceAsString(e)) | ||
| .log("Unable to resolve principal entity from credentials"); | ||
| throw new ServiceUnavailableException("Unable to fetch principal entity"); | ||
| throw metaStoreUnavailable( | ||
| e, | ||
| "Unable to resolve principal entity from credentials", | ||
| "Unable to fetch principal entity"); | ||
| } | ||
|
|
||
| if (principal == null || principal.getType() != PolarisEntityType.PRINCIPAL) { | ||
|
|
@@ -258,8 +256,13 @@ protected PrincipalRoleSelection extractRequestedRoles(PolarisCredential credent | |
| */ | ||
| protected LoadGrantsResult loadPrincipalGrants(PrincipalEntity principal) { | ||
| PolarisCallContext polarisContext = callContext.getPolarisCallContext(); | ||
| LoadGrantsResult principalGrantResults = | ||
| metaStoreManager.loadGrantsToGrantee(polarisContext, principal); | ||
| LoadGrantsResult principalGrantResults; | ||
| try { | ||
| principalGrantResults = metaStoreManager.loadGrantsToGrantee(polarisContext, principal); | ||
| } catch (Exception e) { | ||
| throw metaStoreUnavailable( | ||
| e, "Unable to load grants for principal", "Unable to fetch principal grants"); | ||
| } | ||
| diagnostics.check( | ||
| principalGrantResults.isSuccess(), | ||
| "Failed to resolve principal roles for principal name={} id={}", | ||
|
|
@@ -285,13 +288,34 @@ protected LoadGrantsResult loadPrincipalGrants(PrincipalEntity principal) { | |
| if (entitiesById != null) { | ||
| return entitiesById.get(grant.getSecurableId()); | ||
| } | ||
| return metaStoreManager | ||
| .loadEntity( | ||
| callContext.getPolarisCallContext(), | ||
| grant.getSecurableCatalogId(), | ||
| grant.getSecurableId(), | ||
| PolarisEntityType.PRINCIPAL_ROLE) | ||
| .getEntity(); | ||
| PolarisCallContext polarisContext = callContext.getPolarisCallContext(); | ||
| try { | ||
| return metaStoreManager | ||
| .loadEntity( | ||
| polarisContext, | ||
| grant.getSecurableCatalogId(), | ||
| grant.getSecurableId(), | ||
| PolarisEntityType.PRINCIPAL_ROLE) | ||
| .getEntity(); | ||
| } catch (Exception e) { | ||
| throw metaStoreUnavailable( | ||
| e, "Unable to load securable entity for grant", "Unable to fetch securable entity"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Logs a metastore failure raised during authentication and returns the exception to throw, so | ||
| * that a failing backend is reported as a transient condition instead of an internal error. | ||
| */ | ||
| private static PolarisServiceUnavailableException metaStoreUnavailable( | ||
| Exception cause, String logMessage, String responseMessage) { | ||
| LOGGER | ||
| .atError() | ||
| .setCause(cause) | ||
| .addKeyValue(StructuredLogKeys.ERR_MSG, cause.getMessage()) | ||
| .addKeyValue(StructuredLogKeys.STACK_TRACE, Throwables.getStackTraceAsString(cause)) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not also pass
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done: |
||
| .log(logMessage); | ||
| return new PolarisServiceUnavailableException(0, "%s", responseMessage); | ||
| } | ||
|
|
||
| protected record PrincipalRoleSelection(Set<String> roles, boolean allRolesRequested) {} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're touching this code... I believe it is preferable not to disclose any error details to the client before it has been properly authorized (to reduce the risk of disclosing valuable information about the server to malicious clients).
In this case I believe we ought to log these messages (
Unable to fetch principal grants, etc.) with a UUID and pass the UUID back to the client with a generic "service unavailable" message.The admin user will be able to correlate the client-side error UUID to specific failures in Polaris logs if it comes to debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cf. #5011
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you agree, we probably need to rebase this PR on top of #5119 and redo those new messages too.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. On the UUID, #5011 points at something already in place: the response carries the request id as
X-Request-IDand the default log format prints the same id, so there is nothing new to mint. #4406 took that route on the 403 path, and I checked a 503 raised during authentication comes back with the header set.That leaves the message, and it should cover the existing
Unable to fetch principal entitytoo, otherwise two of the three lookups go generic and one keeps its current text. Doing that here rewrites a message that has already shipped, so I'd rather put all three in one follow-up together with the exception type from the other thread, since both come down to whatmetaStoreUnavailablethrows. I'll open it as soon as this is in.On the rebase: in
DefaultAuthenticatorthe only lines both PRs change are theresolvePrincipalEntitycatch block, so nothing here has to wait. If #5119 goes in first I'll rebase onto it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Follow-up SGTM 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Narrowing that follow-up to the three messages, since the exception type is now in this PR.