-
Notifications
You must be signed in to change notification settings - Fork 35
feat: support conditional policies #110
Conversation
Codecov Report
@@ Coverage Diff @@
## master #110 +/- ##
=========================================
Coverage ? 67.98%
Complexity ? 377
=========================================
Files ? 36
Lines ? 1968
Branches ? 258
=========================================
Hits ? 1338
Misses ? 526
Partials ? 104
Continue to review full report at Codecov.
|
|
Hi @chingor13 @JesseLovelace @jkwlui @broady, I resolved presubmit checks, please review when you have a moment. Thank you! |
jkwlui
left a comment
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.
a few question, but looks good in general!
| private Condition condition; | ||
|
|
||
| public static class Builder { | ||
| private List<String> members = new ArrayList(); |
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.
Did we end up deciding to use a List instead of a Set for the list of members?
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 updated it to List<> instead of Set<> to get the conversation moving. I can revert back to Set<>.
| @Override | ||
| protected Policy fromPb(com.google.iam.v1.Policy policyPb) { | ||
| Map<Role, Set<Identity>> bindings = new HashMap<>(); | ||
| List<Binding> bindingsList = new ArrayList<Binding>(); |
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.
Do we want to allow users to modify the binding in-place?
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'd need to allow mutations outside of builders. That's okay but it does allow for mutation directly of the policy when it wasn't allowed beforehand.
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 could use ImmutableList.Builder if we want to keep it immutable
| setCondition(binding.condition); | ||
| } | ||
|
|
||
| public final Binding.Builder setRole(String role) { |
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.
is there a reason to use String instead of Role?
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'm trying to remove helpers from the new usage patterns. I could add them back in though.
| private List<String> members; | ||
| private Condition condition; | ||
|
|
||
| public static class Builder { |
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.
Should we be using auto-value for these classes and builders if they are meant to be value classes?
It might be tricky if we're trying to enforce that the list of members is immutable on the actual value, but it's probably worthwhile to have these classes fully immutable.
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.
good call I didn't think about using auto-value and I think you're right in that it can be helpful to be fully immutable reducing the number of builders.
Please correct me but I don't think I should update the Policy class but only Binding and Condition to use Auto-Value.
|
I notice there are no documentation on the fields/getter/setter for auto-value classes. @chingor13 how do we usually document these? |
|
I can add documentation to autovalue implementation after I get a review of the surface. |
| import javax.annotation.Nullable; | ||
|
|
||
| @AutoValue | ||
| abstract class Condition { |
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 should probably be public?
| import javax.annotation.Nullable; | ||
|
|
||
| @AutoValue | ||
| abstract class Binding { |
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 should probably be public?
| abstract class Binding { | ||
| abstract String getRole(); | ||
|
|
||
| abstract ImmutableList<String> getMembers(); |
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.
Assuming this class is public, we want to keep guava types off the surface of the classes. This should be a List<String> and we'd document that the implementation is immutable so they shouldn't try to modify 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.
I missed this comment a while back. The reason I'm using ImmutableList<> is based on recommendation from AutoValue docs https://github.com/google/auto/blob/master/value/userguide/builders-howto.md#-use-a-collection-valued-property. I want getMembers() to support a builder helper as well in case it's easier for users.
WDYT?
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.
Removed ImmutableList and now using List in the surface while still supporting helper methods addMembers and removeMembers.
| @Override | ||
| protected Policy fromPb(com.google.iam.v1.Policy policyPb) { | ||
| Map<Role, Set<Identity>> bindings = new HashMap<>(); | ||
| List<Binding> bindingsList = new ArrayList<Binding>(); |
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 could use ImmutableList.Builder if we want to keep it immutable
|
|
||
| package com.google.cloud; | ||
|
|
||
| import static org.junit.Assert.*; |
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.
nit: avoid * imports
| * Return false if Policy is version 3 OR bindings has a conditional binding. | ||
| * Return true if Policy is version 1 AND bindings does not have a conditional binding. | ||
| */ | ||
| private static boolean checkVersion(int version, List<Binding> bindingsList) { |
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 might call this isVersion. The check* methods in guava throw exceptions when conditions are not met, whereas this method is returning a boolean
jkwlui
left a comment
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.
LGTM
Codecov Report
@@ Coverage Diff @@
## master #110 +/- ##
=========================================
Coverage ? 67.98%
Complexity ? 377
=========================================
Files ? 36
Lines ? 1968
Branches ? 258
=========================================
Hits ? 1338
Misses ? 526
Partials ? 104
Continue to review full report at Codecov.
|
Codecov Report
@@ Coverage Diff @@
## master #110 +/- ##
=========================================
Coverage 67.94% 67.94%
Complexity 377 377
=========================================
Files 36 36
Lines 1959 1959
Branches 254 254
=========================================
Hits 1331 1331
Misses 524 524
Partials 104 104
Continue to review full report at Codecov.
|
|
Updated based on discussion with @chingor13, @kolea2, @elharo, @jkwlui, and David (not sure of their Github handle). Please take a look when you have a moment. Thank you for your help. |
|
cc: @netdpb (David) |
| return false; | ||
| } | ||
| for (int i = 0; i < bindingsList.size(); i++) { | ||
| bindingsList.get(i).equals(other.getBindingsList().get(i)); |
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.
sorry, missed this. Should this be returning something?
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.
oh, I missed this as well. Will fix.
netdpb
left a comment
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 haven't finished reviewing the test code yet, but I have to run.
Feel free not to address all (or any!) of these issues in this PR if they require further discussion.
Some are pure style nits, which you should feel free to ignore anyway (like deleting the final period at the end of a Javadoc @throws clause).
|
|
||
| public abstract ImmutableList<String> getMembers(); | ||
|
|
||
| @Nullable |
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 @Nullable instead of a non-nullable Optional<Condition>?
Either is okay, but it's interesting that you're using both patterns in the same class.
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 picked Nullable because it made more sense to me in this case. I'm not well versed in Optional's. Both patterns? Not sure I understand.
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.
Sorry, I thought for some reason that you were also using Optional. Don't know why I thought that.
| public abstract static class Builder { | ||
| public abstract Builder setRole(String role); | ||
|
|
||
| public abstract Builder setMembers(List<String> members); |
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 could even take Iterable<String> if you want to be most general.
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.
Thanks, addressing.
|
|
||
| public abstract String getRole(); | ||
|
|
||
| public abstract ImmutableList<String> getMembers(); |
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.
Do you expect this to be called other than from addMembers(String...) and removeMembers(String...)? If not, make this have default access.
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.
No, good point.
|
|
||
| public abstract Builder setCondition(Condition condition); | ||
|
|
||
| public abstract String getRole(); |
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.
Is this used from anywhere? Do you need it? Generally, builders don't need getters.
Same for getCondition().
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.
removed to reduce surface size. At the moment they were convenience but I'd like to get more developer input before adding unnecessary methods to the public surface.
| @BetaApi("This is a Beta API is not stable yet and may change in the future.") | ||
| @AutoValue | ||
| public abstract class Condition { | ||
| @Nullable |
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 are these nullable?
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.
Good point, no reason other than my first attempt with AutoValue. removing.
| checkArgument( | ||
| !isConditional(this.version, this.bindingsList), | ||
| "getBindings() is only supported with version 1 policies and non-conditional policies"); | ||
| ImmutableMap.Builder<Role, Set<Identity>> bindingsV1Builder = ImmutableMap.builder(); |
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.
You can use an ImmutableSetMultimap instead, regardless of whether you change the method return type. You can always get the map out with Multimaps.asMap().
| } | ||
|
|
||
| /** Returns the list of bindings that comprises the policy for version 3. */ | ||
| public List<Binding> getBindingsList() { |
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.
| public List<Binding> getBindingsList() { | |
| public ImmutableList<Binding> getBindingsList() { |
| return Objects.equals(bindings, other.getBindings()) | ||
| && Objects.equals(etag, other.getEtag()) | ||
| && Objects.equals(version, other.getVersion()); | ||
| if (bindingsList.size() != other.getBindingsList().size()) { |
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 whole block can be just
if (!bindingsList.equals(other.getBindingsList())) {
return false;
}
| for (int i = 0; i < bindingsList.size(); i++) { | ||
| bindingsList.get(i).equals(other.getBindingsList().get(i)); | ||
| } | ||
| return Objects.equals(etag, other.getEtag()) && Objects.equals(version, other.getVersion()); |
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.
Can etag ever be null? If not, just call etag.equals(other.getEtag()).
version is an int, so you can just do version == other.getVersion(). Otherwise you're boxing the int.
| Binding.newBuilder().setRole(EDITOR).setMembers(MEMBERS_LIST_2).build()); | ||
| private static final List<Binding> BINDINGS_WITH_CONDITIONS = | ||
| ImmutableList.copyOf(BINDINGS_NO_CONDITIONS) | ||
| .of( |
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.
You're calling a static method on an instance, and throwing the instance away. That's not what you want to do. (If the project used Error-Prone, this would have generated a warning.)
It looks like you want a variant of BINDINGS_NO_CONDITIONS where the viewer role has a condition. What about this?
ImmutableList.of(
BINDINGS_NO_CONDITIONS.get(0).toBuilder().setCondition(...).build(),
BINDINGS_NO_CONDITIONS.get(1))
Although it might be nice to have a constant for each element of BINDINGS_NO_CONDITIONS (VIEWER and EDITOR)?
chingor13
left a comment
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.
We should still clean up a bunch of the review comments, but they are minor and can be done separately. We can merge, but let's not lose the work items to clean up.
|
I'll make a separate list and file an issue to track it. Thanks @chingor13! |
|
|
||
| public abstract ImmutableList<String> getMembers(); | ||
|
|
||
| @Nullable |
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.
Sorry, I thought for some reason that you were also using Optional. Don't know why I thought that.
| /** Create a new Binding.Builder */ | ||
| public static Builder newBuilder() { | ||
| return new AutoValue_Binding.Builder(); | ||
| List<String> emptyMembers = ImmutableList.of(); |
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.
You could just inline emptyMembers into setMembers(ImmutableList.of()).
| /** | ||
| * Set IAM Members for Policy Binding | ||
| * | ||
| * @throws NullPointerException if a member is null. |
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.
You don't really need to repeat this everywhere. It's better to use @Nullable for the hopefully few cases where null is accepted.
| * because conditional policies are not supported | ||
| */ | ||
| public final Builder removeRole(Role role) { | ||
| checkArgument( |
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 still think all of these should use checkState instead of checkArgument so they throw IllegalStateException.
Throw IllegalArgumentException when the caller should have known that the arguments to this method are invalid.
Throw IllegalStateException when the caller should have known that this method cannot be called given the state of the object.
🤖 I have created a release \*beep\* \*boop\* --- ## [1.93.0](https://www.github.com/googleapis/java-core/compare/v1.92.6...v1.93.0) (2020-02-27) ### Features * support conditional policies ([#110](https://www.github.com/googleapis/java-core/issues/110)) ([61e2d19](https://www.github.com/googleapis/java-core/commit/61e2d19bb4400978681aa018a8dc200214203830)) ### Bug Fixes * fix conversion for pre-epoch timestamps ([#160](https://www.github.com/googleapis/java-core/issues/160)) ([1f8b6b4](https://www.github.com/googleapis/java-core/commit/1f8b6b4835aaa702ec94bbbde89ed90f519c935a)) ### Dependencies * update dependency com.google.api:gax-bom to v1.54.0 ([#168](https://www.github.com/googleapis/java-core/issues/168)) ([5b52f9e](https://www.github.com/googleapis/java-core/commit/5b52f9e8d8cdc82b56114d3d1e857d137ae7ca98)) * update dependency io.grpc:grpc-bom to v1.27.2 ([#166](https://www.github.com/googleapis/java-core/issues/166)) ([28c9859](https://www.github.com/googleapis/java-core/commit/28c98595c9ee96760a063085bd85024177bd6dd2)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please).
fixes: #118
Add support for conditional policies based on design document.
PTAL: @broady @jkwlui @JesseLovelace @chingor13