-
Notifications
You must be signed in to change notification settings - Fork 332
Add new PolaisAuthorizer.authorizeOrThrow for Fine Grained AuthorizableOperations #2767
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
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 |
|---|---|---|
|
|
@@ -41,4 +41,18 @@ void authorizeOrThrow( | |
| @Nonnull PolarisAuthorizableOperation authzOp, | ||
| @Nullable List<PolarisResolvedPathWrapper> targets, | ||
| @Nullable List<PolarisResolvedPathWrapper> secondaries); | ||
|
|
||
| void authorizeOrThrow( | ||
| @Nonnull PolarisPrincipal polarisPrincipal, | ||
| @Nonnull Set<PolarisBaseEntity> activatedEntities, | ||
| @Nonnull Set<PolarisAuthorizableOperation> authzOps, | ||
| @Nullable PolarisResolvedPathWrapper target, | ||
| @Nullable PolarisResolvedPathWrapper secondary); | ||
|
|
||
| void authorizeOrThrow( | ||
| @Nonnull PolarisPrincipal polarisPrincipal, | ||
| @Nonnull Set<PolarisBaseEntity> activatedEntities, | ||
| @Nonnull Set<PolarisAuthorizableOperation> authzOps, | ||
| @Nullable List<PolarisResolvedPathWrapper> targets, | ||
| @Nullable List<PolarisResolvedPathWrapper> secondaries); | ||
|
Comment on lines
+45
to
+57
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. since this is a public interface, do you wanna may be add some function docs for this ?
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. also do we wanna mark the existing API's deprecated ? since these APIs can superseed them
Contributor
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.
Sounds great!
I think we should keep the existing ones since they’re widely used across the codebase — they serve as polymorphic helpers.
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. I think this probably deserves a mailing list discussion, but I wonder if this API change is really enough to support batching everything together into one call. This API makes the assumption that all operations have the same primary and secondary entities. Is that always going to be the case? or are we fine to kick that can down the road?
Contributor
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. Sounds good, will start a discussion! IMHO, batch AuthorizableOperations should mainly be used for fine-grained cases like In the updateTable case, there’s a sort of internal inheritance — for example, a fine-grained operation like TABLE_ADD_SNAPSHOT conceptually extends a broader one such as TABLE_WRITE_PROPERTIES. All these fine-grained operations should share the same set of primary and secondary securable entities; it’s just that this “inheritance” relationship hasn’t been reflected in the code yet. If two operations involve different sets of securables, they should ideally be authorized through separate API calls, each likely corresponding to its own endpoint. If in the future the above assumption does not hold, I assume we will need large change anyway and thus a new set of apis will be introduced. But the above are just my thoughts, let's discuss in dev mailing list to collect thoughts on the new api and see what we want to support : ) |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -128,6 +128,7 @@ | |
| import jakarta.annotation.Nonnull; | ||
| import jakarta.annotation.Nullable; | ||
| import jakarta.inject.Inject; | ||
| import java.util.EnumSet; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
| import java.util.stream.Collectors; | ||
|
|
@@ -774,33 +775,63 @@ public void authorizeOrThrow( | |
| @Nonnull PolarisAuthorizableOperation authzOp, | ||
| @Nullable List<PolarisResolvedPathWrapper> targets, | ||
| @Nullable List<PolarisResolvedPathWrapper> secondaries) { | ||
| authorizeOrThrow( | ||
| polarisPrincipal, activatedEntities, EnumSet.of(authzOp), targets, secondaries); | ||
| } | ||
|
|
||
| @Override | ||
| public void authorizeOrThrow( | ||
| @Nonnull PolarisPrincipal polarisPrincipal, | ||
| @Nonnull Set<PolarisBaseEntity> activatedEntities, | ||
| @Nonnull Set<PolarisAuthorizableOperation> authzOps, | ||
| @Nullable PolarisResolvedPathWrapper target, | ||
| @Nullable PolarisResolvedPathWrapper secondary) { | ||
| authorizeOrThrow( | ||
|
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. Honestly this and the other authorizeOrThrow (with single op and single target / secondary) would probably be nice to have a default as well to default to calling the option with a list of targets and secondaries. The Polaris impls end up just delegating these other impls often do as well. |
||
| polarisPrincipal, | ||
| activatedEntities, | ||
| authzOps, | ||
| target == null ? null : List.of(target), | ||
| secondary == null ? null : List.of(secondary)); | ||
| } | ||
|
|
||
| @Override | ||
| public void authorizeOrThrow( | ||
| @Nonnull PolarisPrincipal polarisPrincipal, | ||
| @Nonnull Set<PolarisBaseEntity> activatedEntities, | ||
| @Nonnull Set<PolarisAuthorizableOperation> authzOps, | ||
| @Nullable List<PolarisResolvedPathWrapper> targets, | ||
| @Nullable List<PolarisResolvedPathWrapper> secondaries) { | ||
| boolean enforceCredentialRotationRequiredState = | ||
| realmConfig.getConfig( | ||
| FeatureConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING); | ||
| boolean isRoot = getRootPrincipalName().equals(polarisPrincipal.getName()); | ||
| if (enforceCredentialRotationRequiredState | ||
| && polarisPrincipal | ||
| .getProperties() | ||
| .containsKey(PolarisEntityConstants.PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE) | ||
| && authzOp != PolarisAuthorizableOperation.ROTATE_CREDENTIALS) { | ||
| throw new ForbiddenException( | ||
| "Principal '%s' is not authorized for op %s due to PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE", | ||
| polarisPrincipal.getName(), authzOp); | ||
| } else if (authzOp == PolarisAuthorizableOperation.RESET_CREDENTIALS) { | ||
| if (!isRoot) { | ||
| throw new ForbiddenException("Only Root principal(service-admin) can perform %s", authzOp); | ||
| for (PolarisAuthorizableOperation authzOp : authzOps) { | ||
|
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. This loop could be done in a |
||
| if (enforceCredentialRotationRequiredState | ||
| && polarisPrincipal | ||
| .getProperties() | ||
| .containsKey(PolarisEntityConstants.PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE) | ||
| && authzOp != PolarisAuthorizableOperation.ROTATE_CREDENTIALS) { | ||
| throw new ForbiddenException( | ||
| "Principal '%s' is not authorized for op %s due to PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE", | ||
| polarisPrincipal.getName(), authzOp); | ||
| } else if (authzOp == PolarisAuthorizableOperation.RESET_CREDENTIALS) { | ||
| if (!isRoot) { | ||
| throw new ForbiddenException( | ||
| "Only Root principal(service-admin) can perform %s", authzOp); | ||
| } | ||
| LOGGER | ||
| .atDebug() | ||
| .addKeyValue("principalName", polarisPrincipal.getName()) | ||
| .log("Root principal allowed to reset credentials"); | ||
| } else if (!isAuthorized( | ||
| polarisPrincipal, activatedEntities, authzOp, targets, secondaries)) { | ||
| throw new ForbiddenException( | ||
| "Principal '%s' with activated PrincipalRoles '%s' and activated grants via '%s' is not authorized for op %s", | ||
| polarisPrincipal.getName(), | ||
| polarisPrincipal.getRoles(), | ||
| activatedEntities.stream().map(PolarisEntityCore::getName).collect(Collectors.toSet()), | ||
| authzOp); | ||
| } | ||
| LOGGER | ||
| .atDebug() | ||
| .addKeyValue("principalName", polarisPrincipal.getName()) | ||
| .log("Root principal allowed to reset credentials"); | ||
| } else if (!isAuthorized(polarisPrincipal, activatedEntities, authzOp, targets, secondaries)) { | ||
| throw new ForbiddenException( | ||
| "Principal '%s' with activated PrincipalRoles '%s' and activated grants via '%s' is not authorized for op %s", | ||
| polarisPrincipal.getName(), | ||
| polarisPrincipal.getRoles(), | ||
| activatedEntities.stream().map(PolarisEntityCore::getName).collect(Collectors.toSet()), | ||
| authzOp); | ||
| } | ||
| } | ||
|
|
||
|
|
||
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.
Would it be possible to add
default(possibly inefficient) implementations to the new methods to simplify transition in downstream projects? This is just a matter of convenience, so optional.