-
Notifications
You must be signed in to change notification settings - Fork 218
Add support for federated principal and role with block for manual role assignment #1353
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?
Conversation
@@ -182,6 +182,12 @@ public static List<NameAndId> toNameAndIdList(List<EntityNameLookupRecord> entit | |||
.orElse(null); | |||
} | |||
|
|||
public static boolean isFederated(PolarisBaseEntity entity) { |
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 only a subset of entity types support this, I don't think it should be present in the base type PolarisEntity
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.
I tend to agree with @eric-maynard 's point here
return principalRole; | ||
return new PrincipalRole( | ||
getName(), | ||
PolarisEntity.isFederated(this), |
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 we have to add an argument, it's best to add it at the end
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.
Normally, I'd agree, but here I am trying to stick to the convention of keeping the entity-specific args at the front and the general, id/created/updated-at timestamps at the end. That's the convention that every entity type follows.
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.
Makes sense, it sounds like this folds into another comment above
@@ -65,6 +65,13 @@ public Builder(PrincipalRoleEntity original) { | |||
super(original); | |||
} | |||
|
|||
public Builder setFederated(Boolean isFederated) { | |||
if (isFederated != null && isFederated) { |
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.
This logic doesn't look right to me. This entity would come out federated:
new PolarisEntity.Builder()
. . .
.setFederated(true)
.setFederated(false)
.build()
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.
Yeah, the idea is to avoid adding any entry for non-federated identities. I don't think the logic snippet you posted is reasonable or should be supported. I'd be in favor of an IllegalStateException if someone typed that code. Happy to add that.
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.
Why should it not be supported? It sounds like you're saying that .setFederated(false)
is never a valid call, in which case setFederated
shouldn't take a boolean arg. Perhaps you are even looking for something like buildFederated
?
public void testCannotAddFederatedPrincipalToNonFederatedRole() { | ||
PolarisMetaStoreManager metaStoreManager = setupMetaStoreManager(); | ||
PolarisCallContext callContext = setupCallContext(metaStoreManager); | ||
PolarisAdminService polarisAdminService = | ||
setupPolarisAdminService(metaStoreManager, callContext); | ||
|
||
PrincipalEntity federatedPrincipal = | ||
createPrincipal(metaStoreManager, callContext, "federated_id", true); | ||
metaStoreManager.createPrincipal(callContext, federatedPrincipal); | ||
|
||
PrincipalRoleEntity nonFederatedRole = | ||
createRole(metaStoreManager, callContext, "non_federated_role", false); | ||
EntityResult result = | ||
metaStoreManager.createEntityIfNotExists(callContext, null, nonFederatedRole); | ||
assertThat(result.isSuccess()).isTrue(); | ||
|
||
assertThatThrownBy( | ||
() -> | ||
polarisAdminService.assignPrincipalRole( | ||
federatedPrincipal.getName(), nonFederatedRole.getName())) | ||
.isInstanceOf(ValidationException.class); | ||
} | ||
|
||
@Test | ||
public void testCannotAddNonFederatedPrincipalToFederatedRole() { |
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.
It could make sense to use a parameterized test here. IIUC there is basically a table like the following:
Principal Type | Principal Role Type | Can Attach |
---|---|---|
Non-Federated | Non-Federated | Yes |
Non-Federated | Federated | No |
Federated | Non-Federated | No |
Federated | Federated | No |
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 restrictions proposed in the table above make sense to me 👍
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.
I'll skip the first row because it's already covered exhaustively elsewhere. Personally, I tend to shy away from parameterized tests, as I think they're overused and oftentimes obfuscate the test code with a bunch of if/else statements, but I'm ok adding it here.
@@ -871,6 +873,42 @@ public void testCreatePrincipalAndRotateCredentials() { | |||
// rotation that makes the old secret fall off retention. | |||
} | |||
|
|||
@Test | |||
public void testCreateFederatedPrincipalFails() { |
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.
Given how different the semantics for federated principal (role)s appear to be, I wonder if we should consider a subtype? It looks like most APIs are valid for nonfederated principal (role)s are not valid for federated ones. This is reminiscent of EXTERNAL catalogs, but maybe more extreme
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.
External catalog has a separate type because it has an extra field. Federated identity types don't have any additional fields at this point, so I'd prefer not to add an extra type. If extra fields end up getting added, then it would make sense, but for now deserializing wouldn't even be able to distinguish what type it should deserialized to.
@@ -1089,6 +1089,10 @@ components: | |||
clientId: | |||
type: string | |||
description: The output-only OAuth clientId associated with this principal if applicable | |||
federated: |
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.
This part LGTM, but it should go to a ML vote
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.
+1 to ML discussion.
What's the use case for exposing federated principal in the Polaris Management API? Acting as a proxy in front of an IdP sounds questionable to me.
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 main case is to support adding federated roles so that privileges can be defined. Per the original design doc, federated identities are created on the fly when a user logs in, but if we don't allow creation of federated roles, we can't define any privileges for those users until they've logged in. That makes things hard for the admins, IMO.
For Principals, I don't think federated principals should be created via the API, for the same reasoning you suggest, but we should be able to return federated principals and report that they are federated. To make that clearer, maybe I can update the spec to return one of two types, but to only allow creation of one type.
@@ -771,6 +771,9 @@ public PrincipalWithCredentials createPrincipal(PolarisEntity entity) { | |||
PolarisAuthorizableOperation op = PolarisAuthorizableOperation.CREATE_PRINCIPAL; | |||
authorizeBasicRootOperationOrThrow(op); | |||
|
|||
if (PolarisEntity.isFederated(entity)) { |
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.
Why do we expose "federated" as a property settable via java API only to add validation that it cannot be set?
Also, REST API (which is the only caller of this method) does not allow setting the "federated" property, as far as I can tell, because it does not set internal properties on the PrincipalEntity
🤔
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.
to clarify: My thinking was that the fact that make those entities "not creatable" via the Admin API is not the presence of a specific property (i.e. "federated") but the fact that they are managed in a different way (via IdP integrations, I believe).
Can this be made more explicit in code? I do not have a solid suggestion, but I wonder if some sort of a "manager" class might be suitable here. That "manager" would then make the decision about allowing or not allowing changes via Admin API. WDYT?
@@ -1089,6 +1089,10 @@ components: | |||
clientId: | |||
type: string | |||
description: The output-only OAuth clientId associated with this principal if applicable | |||
federated: |
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.
+1 to ML discussion.
What's the use case for exposing federated principal in the Polaris Management API? Acting as a proxy in front of an IdP sounds questionable to me.
First PR to begin implementing the design outlined in https://docs.google.com/document/d/15_3ZiRB6Lhzw0nxij341QUdxEIyFGTrI9_18bFIyJVo/edit?tab=t.0#heading=h.cu1a1acu4lc5 .
Implements #441
Adds a
FEDERATED_ENTITY
property, which can be defined in Principals and PrincipalRoles.FEDERATED
Principals cannot be created via the API, butFEDERATED
roles can be. This allows for creating roles which can be assigned privileges, while actual principal creation is delegated to the JIT creation mechanism (not in this PR).