-
Notifications
You must be signed in to change notification settings - Fork 165
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
Update PolarisAuthorizerImpl to use PolarisGrantManager interface and add EntityCache based PolarisGrantManager impl #465
base: main
Are you sure you want to change the base?
Changes from all commits
0d161dc
4d0be15
2d5fa0c
9d04888
87ce0b0
8ef6313
2ec5473
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 |
---|---|---|
|
@@ -94,20 +94,22 @@ | |
import jakarta.annotation.Nonnull; | ||
import jakarta.annotation.Nullable; | ||
import jakarta.inject.Inject; | ||
import jakarta.inject.Provider; | ||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import org.apache.iceberg.exceptions.ForbiddenException; | ||
import org.apache.polaris.core.PolarisCallContext; | ||
import org.apache.polaris.core.PolarisConfiguration; | ||
import org.apache.polaris.core.PolarisConfigurationStore; | ||
import org.apache.polaris.core.context.CallContext; | ||
import org.apache.polaris.core.entity.PolarisBaseEntity; | ||
import org.apache.polaris.core.entity.PolarisEntity; | ||
import org.apache.polaris.core.entity.PolarisEntityConstants; | ||
import org.apache.polaris.core.entity.PolarisEntityCore; | ||
import org.apache.polaris.core.entity.PolarisGrantRecord; | ||
import org.apache.polaris.core.entity.PolarisPrivilege; | ||
import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper; | ||
import org.apache.polaris.core.persistence.ResolvedPolarisEntity; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
|
@@ -460,10 +462,13 @@ public class PolarisAuthorizerImpl implements PolarisAuthorizer { | |
} | ||
|
||
private final PolarisConfigurationStore featureConfig; | ||
private final Provider<PolarisGrantManager> grantManagerFactory; | ||
|
||
@Inject | ||
public PolarisAuthorizerImpl(PolarisConfigurationStore featureConfig) { | ||
public PolarisAuthorizerImpl( | ||
PolarisConfigurationStore featureConfig, Provider<PolarisGrantManager> grantManagerFactory) { | ||
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 are we injecting a 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. The 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.
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. @collado-mike : from my POV this is the last remaining concern on this PR :) |
||
this.featureConfig = featureConfig; | ||
this.grantManagerFactory = grantManagerFactory; | ||
} | ||
|
||
/** | ||
|
@@ -604,15 +609,23 @@ public boolean hasTransitivePrivilege( | |
Set<Long> activatedGranteeIds, | ||
PolarisPrivilege desiredPrivilege, | ||
PolarisResolvedPathWrapper resolvedPath) { | ||
PolarisGrantManager grantManager = grantManagerFactory.get(); | ||
PolarisCallContext callContext = CallContext.getCurrentContext().getPolarisCallContext(); | ||
|
||
// Iterate starting at the parent, since the most common case should be to manage grants as | ||
// high up in the resource hierarchy as possible, so we expect earlier termination. | ||
for (ResolvedPolarisEntity resolvedSecurableEntity : resolvedPath.getResolvedFullPath()) { | ||
Preconditions.checkState( | ||
resolvedSecurableEntity.getGrantRecordsAsSecurable() != null, | ||
"Got null grantRecordsAsSecurable for resolvedSecurableEntity %s", | ||
resolvedSecurableEntity); | ||
for (PolarisGrantRecord grantRecord : resolvedSecurableEntity.getGrantRecordsAsSecurable()) { | ||
for (PolarisEntity resolvedSecurableEntity : resolvedPath.getRawFullPath()) { | ||
PolarisGrantManager.LoadGrantsResult grantsResult = | ||
grantManager.loadGrantsOnSecurable( | ||
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'm generally fine with this change, but I'd like to clarify one point. My impression from current code on Is that a correct assumption? If I am mistaken, that is fine and this change is fine too. However, if my assumption is valid, how do we make sure the loaded grant records match the other data, which is loaded independently by API services? 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. If this is a reference to the same 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 thread is about the overall assumptions on the consistency of data in storage in the presence of concurrent changes. With this code, grants for different objects in the same logical API-level operation may be loaded from different states of the data (e.g. results of two of more RDBMS transactions). So my question is whether this is a concern. I'm not proposing any particular approach, just wondering for my own understanding. 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 code, like the existing code, assumes that all of the entities are in the cache once the Resolver runs. That process puts all of the entities and their grants into the cache so that by the time the Authorizer retrieves the grants, it fetches them from the cache. This approach is basically hiding that fact by wrapping the cache in a 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. Wait? Are you saying that cache eviction breaks requests? |
||
callContext, resolvedSecurableEntity.getCatalogId(), resolvedSecurableEntity.getId()); | ||
callContext | ||
.getDiagServices() | ||
.check( | ||
grantsResult.isSuccess(), | ||
"no_grants_for_securable", | ||
"unable to load grants for securable %s", | ||
resolvedSecurableEntity.getId()); | ||
for (PolarisGrantRecord grantRecord : grantsResult.getGrantRecords()) { | ||
if (matchesOrIsSubsumedBy( | ||
desiredPrivilege, PolarisPrivilege.fromCode(grantRecord.getPrivilegeCode()))) { | ||
// Found a potential candidate for satisfying our authz goal. | ||
|
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.
The name looks too generic. It does not convey the purpose of the beam. Also, the system could potentially have multiple delegates. How about
primaryGrantManager
,persistingGrantManager
?