diff --git a/google-cloud-core/pom.xml b/google-cloud-core/pom.xml
index 342b3ce47e..6789692116 100644
--- a/google-cloud-core/pom.xml
+++ b/google-cloud-core/pom.xml
@@ -27,6 +27,10 @@
com.google.api
gax
+
+ com.google.auto.value
+ auto-value-annotations
+
com.google.protobuf
protobuf-java-util
@@ -83,6 +87,10 @@
objenesis
test
+
+ com.google.code.findbugs
+ jsr305
+
com.google.truth
truth
diff --git a/google-cloud-core/src/main/java/com/google/cloud/Binding.java b/google-cloud-core/src/main/java/com/google/cloud/Binding.java
new file mode 100644
index 0000000000..be16f87421
--- /dev/null
+++ b/google-cloud-core/src/main/java/com/google/cloud/Binding.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud;
+
+import static com.google.common.base.Predicates.in;
+import static com.google.common.base.Predicates.not;
+
+import com.google.api.core.BetaApi;
+import com.google.auto.value.AutoValue;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import javax.annotation.Nullable;
+
+/**
+ * Class for Identity and Access Management (IAM) policies. IAM policies are used to specify access
+ * settings for Cloud Platform resources. A policy is a list of bindings. A binding assigns a set of
+ * identities to a role, where the identities can be user accounts, Google groups, Google domains,
+ * and service accounts. A role is a named list of permissions defined by IAM.
+ *
+ * @see Policy
+ */
+@BetaApi("This is a Beta API is not stable yet and may change in the future.")
+@AutoValue
+public abstract class Binding {
+ /** Get IAM Policy Binding Role */
+ public abstract String getRole();
+
+ /** Get IAM Policy Binding Members */
+ public abstract ImmutableList getMembers();
+
+ /** Get IAM Policy Binding Condition */
+ @Nullable
+ public abstract Condition getCondition();
+
+ /** Create a Binding.Builder from an existing Binding */
+ public abstract Builder toBuilder();
+
+ /** Create a new Binding.Builder */
+ public static Builder newBuilder() {
+ List emptyMembers = ImmutableList.of();
+ return new AutoValue_Binding.Builder().setMembers(emptyMembers);
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ /**
+ * Set IAM Role for Policy Binding
+ *
+ * @throws NullPointerException if the role is null.
+ */
+ public abstract Builder setRole(String role);
+
+ /**
+ * Set IAM Members for Policy Binding
+ *
+ * @throws NullPointerException if a member is null.
+ */
+ public abstract Builder setMembers(Iterable members);
+
+ /** Set IAM Condition for Policy Binding */
+ public abstract Builder setCondition(Condition condition);
+
+ /** Internal use to getMembers() in addMembers() and removeMembers() */
+ abstract ImmutableList getMembers();
+
+ /**
+ * Add members to Policy Binding.
+ *
+ * @throws NullPointerException if a member is null.
+ */
+ public Builder addMembers(String member, String... moreMembers) {
+ ImmutableList.Builder membersBuilder = ImmutableList.builder();
+ membersBuilder.addAll(getMembers());
+ membersBuilder.addAll(Lists.asList(member, moreMembers));
+ setMembers(membersBuilder.build());
+ return this;
+ }
+
+ /**
+ * Remove members to Policy Binding.
+ *
+ * @throws NullPointerException if a member is null.
+ */
+ public Builder removeMembers(String... members) {
+ Predicate selectMembersNotInList = not(in(Arrays.asList(members)));
+ Collection filter = Collections2.filter(getMembers(), selectMembersNotInList);
+ setMembers(filter);
+ return this;
+ }
+
+ public abstract Binding build();
+ }
+}
diff --git a/google-cloud-core/src/main/java/com/google/cloud/Condition.java b/google-cloud-core/src/main/java/com/google/cloud/Condition.java
new file mode 100644
index 0000000000..3854883796
--- /dev/null
+++ b/google-cloud-core/src/main/java/com/google/cloud/Condition.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud;
+
+import com.google.api.core.BetaApi;
+import com.google.auto.value.AutoValue;
+
+/**
+ * Class for Identity and Access Management (IAM) policies. IAM policies are used to specify access
+ * settings for Cloud Platform resources. A policy is a list of bindings. A binding assigns a set of
+ * identities to a role, where the identities can be user accounts, Google groups, Google domains,
+ * and service accounts. A role is a named list of permissions defined by IAM.
+ *
+ * @see Policy
+ * @see IAM Conditions
+ */
+@BetaApi("This is a Beta API is not stable yet and may change in the future.")
+@AutoValue
+public abstract class Condition {
+ /** Get IAM Policy Binding Condition Title */
+ public abstract String getTitle();
+
+ /** Get IAM Policy Binding Condition Description */
+ public abstract String getDescription();
+
+ /** Get IAM Policy Binding Condition Expression */
+ public abstract String getExpression();
+
+ /** Create a new Condition.Builder from an existing Condition */
+ public abstract Builder toBuilder();
+
+ /** Create a new Condition.Builder */
+ public static Builder newBuilder() {
+ return new AutoValue_Condition.Builder();
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ /** Set IAM Policy Binding Condition Title */
+ public abstract Builder setTitle(String title);
+
+ /** Set IAM Policy Binding Condition Description */
+ public abstract Builder setDescription(String description);
+
+ /** Set IAM Policy Binding Condition Expression */
+ public abstract Builder setExpression(String expression);
+
+ /** Build Builder which creates a Condition instance */
+ public abstract Condition build();
+ }
+}
diff --git a/google-cloud-core/src/main/java/com/google/cloud/Policy.java b/google-cloud-core/src/main/java/com/google/cloud/Policy.java
index 84067ec87e..a6ecbae11e 100644
--- a/google-cloud-core/src/main/java/com/google/cloud/Policy.java
+++ b/google-cloud-core/src/main/java/com/google/cloud/Policy.java
@@ -21,19 +21,16 @@
import com.google.api.core.ApiFunction;
import com.google.api.core.InternalApi;
-import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
import com.google.common.io.BaseEncoding;
import com.google.protobuf.ByteString;
+import com.google.type.Expr;
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -42,20 +39,35 @@
/**
* Class for Identity and Access Management (IAM) policies. IAM policies are used to specify access
- * settings for Cloud Platform resources. A policy is a map of bindings. A binding assigns a set of
+ * settings for Cloud Platform resources. A policy is a list of bindings. A binding assigns a set of
* identities to a role, where the identities can be user accounts, Google groups, Google domains,
* and service accounts. A role is a named list of permissions defined by IAM.
*
- * @see Policy
+ * @see Policy
*/
public final class Policy implements Serializable {
private static final long serialVersionUID = -3348914530232544290L;
-
- private final Map> bindings;
+ private final ImmutableList bindingsList;
private final String etag;
private final int version;
+ /*
+ * Return true if Policy is version 3 OR bindings has a conditional binding.
+ * Return false if Policy is version 1 AND bindings does not have a conditional binding.
+ */
+ private static boolean isConditional(int version, List bindingsList) {
+ for (Binding binding : bindingsList) {
+ if (binding.getCondition() != null) {
+ return true;
+ }
+ }
+ if (version == 3) {
+ return true;
+ }
+ return false;
+ }
+
public abstract static class Marshaller {
@InternalApi("This class should only be extended within google-cloud-java")
@@ -85,22 +97,25 @@ public static class DefaultMarshaller extends Marshaller> bindings = new HashMap<>();
+ ImmutableList.Builder bindingsListBuilder = ImmutableList.builder();
for (com.google.iam.v1.Binding bindingPb : policyPb.getBindingsList()) {
- bindings.put(
- Role.of(bindingPb.getRole()),
- ImmutableSet.copyOf(
- Lists.transform(
- bindingPb.getMembersList(),
- new Function() {
- @Override
- public Identity apply(String s) {
- return IDENTITY_VALUE_OF_FUNCTION.apply(s);
- }
- })));
+ Binding.Builder convertedBinding =
+ Binding.newBuilder()
+ .setRole(bindingPb.getRole())
+ .setMembers(bindingPb.getMembersList());
+ if (bindingPb.hasCondition()) {
+ Expr expr = bindingPb.getCondition();
+ convertedBinding.setCondition(
+ Condition.newBuilder()
+ .setTitle(expr.getTitle())
+ .setDescription(expr.getDescription())
+ .setExpression(expr.getExpression())
+ .build());
+ }
+ bindingsListBuilder.add(convertedBinding.build());
}
return newBuilder()
- .setBindings(bindings)
+ .setBindings(bindingsListBuilder.build())
.setEtag(
policyPb.getEtag().isEmpty()
? null
@@ -113,18 +128,19 @@ public Identity apply(String s) {
protected com.google.iam.v1.Policy toPb(Policy policy) {
com.google.iam.v1.Policy.Builder policyBuilder = com.google.iam.v1.Policy.newBuilder();
List bindingPbList = new LinkedList<>();
- for (Map.Entry> binding : policy.getBindings().entrySet()) {
+ for (Binding binding : policy.getBindingsList()) {
com.google.iam.v1.Binding.Builder bindingBuilder = com.google.iam.v1.Binding.newBuilder();
- bindingBuilder.setRole(binding.getKey().getValue());
- bindingBuilder.addAllMembers(
- Lists.transform(
- new ArrayList<>(binding.getValue()),
- new Function() {
- @Override
- public String apply(Identity identity) {
- return IDENTITY_STR_VALUE_FUNCTION.apply(identity);
- }
- }));
+ bindingBuilder.setRole(binding.getRole());
+ bindingBuilder.addAllMembers(binding.getMembers());
+ if (binding.getCondition() != null) {
+ Condition condition = binding.getCondition();
+ bindingBuilder.setCondition(
+ Expr.newBuilder()
+ .setTitle(condition.getTitle())
+ .setDescription(condition.getDescription())
+ .setExpression(condition.getExpression())
+ .build());
+ }
bindingPbList.add(bindingBuilder.build());
}
policyBuilder.addAllBindings(bindingPbList);
@@ -138,8 +154,7 @@ public String apply(Identity identity) {
/** A builder for {@code Policy} objects. */
public static class Builder {
-
- private final Map> bindings = new HashMap<>();
+ private final List bindingsList = new ArrayList();
private String etag;
private int version;
@@ -148,7 +163,7 @@ protected Builder() {}
@InternalApi("This class should only be extended within google-cloud-java")
protected Builder(Policy policy) {
- setBindings(policy.bindings);
+ bindingsList.addAll(policy.bindingsList);
setEtag(policy.etag);
setVersion(policy.version);
}
@@ -157,26 +172,73 @@ protected Builder(Policy policy) {
* Replaces the builder's map of bindings with the given map of bindings.
*
* @throws NullPointerException if the given map is null or contains any null keys or values
- * @throws IllegalArgumentException if any identities in the given map are null
+ * @throws IllegalArgumentException if any identities in the given map are null or if policy
+ * version is equal to 3 or has conditional bindings because conditional policies are not
+ * supported
*/
public final Builder setBindings(Map> bindings) {
checkNotNull(bindings, "The provided map of bindings cannot be null.");
+ checkArgument(
+ !isConditional(this.version, this.bindingsList),
+ "setBindings() is only supported with version 1 policies and non-conditional policies");
for (Map.Entry> binding : bindings.entrySet()) {
checkNotNull(binding.getKey(), "The role cannot be null.");
Set identities = binding.getValue();
checkNotNull(identities, "A role cannot be assigned to a null set of identities.");
checkArgument(!identities.contains(null), "Null identities are not permitted.");
}
- this.bindings.clear();
+ // convert into List format
+ this.bindingsList.clear();
for (Map.Entry> binding : bindings.entrySet()) {
- this.bindings.put(binding.getKey(), new HashSet<>(binding.getValue()));
+ Binding.Builder bindingBuilder = Binding.newBuilder();
+ bindingBuilder.setRole(binding.getKey().getValue());
+ ImmutableList.Builder membersBuilder = ImmutableList.builder();
+ for (Identity identity : binding.getValue()) {
+ membersBuilder.add(identity.strValue());
+ }
+ bindingBuilder.setMembers(membersBuilder.build());
+ this.bindingsList.add(bindingBuilder.build());
}
return this;
}
- /** Removes the role (and all identities associated with that role) from the policy. */
+ /**
+ * Replaces the builder's List of bindings with the given List of Bindings.
+ *
+ * @throws NullPointerException if the given list is null, role is null, or contains any null
+ * members in bindings
+ */
+ public final Builder setBindings(List bindings) {
+ this.bindingsList.clear();
+ for (Binding binding : bindings) {
+ Binding.Builder bindingBuilder = Binding.newBuilder();
+ bindingBuilder.setMembers(ImmutableList.copyOf(binding.getMembers()));
+ bindingBuilder.setRole(binding.getRole());
+ bindingBuilder.setCondition(binding.getCondition());
+ this.bindingsList.add(bindingBuilder.build());
+ }
+ return this;
+ }
+
+ /**
+ * Removes the role (and all identities associated with that role) from the policy.
+ *
+ * @throws IllegalArgumentException if policy version is equal to 3 or has conditional bindings
+ * because conditional policies are not supported
+ */
public final Builder removeRole(Role role) {
- bindings.remove(role);
+ checkArgument(
+ !isConditional(this.version, this.bindingsList),
+ "removeRole() is only supported with version 1 policies and non-conditional policies");
+ Iterator iterator = bindingsList.iterator();
+
+ while (iterator.hasNext()) {
+ Binding binding = (Binding) iterator.next();
+ if (binding.getRole().equals(role.getValue())) {
+ iterator.remove();
+ return this;
+ }
+ }
return this;
}
@@ -184,39 +246,79 @@ public final Builder removeRole(Role role) {
* Adds one or more identities to the policy under the role specified.
*
* @throws NullPointerException if the role or any of the identities is null.
+ * @throws IllegalArgumentException if policy version is equal to 3 or has conditional bindings.
*/
public final Builder addIdentity(Role role, Identity first, Identity... others) {
+ checkArgument(
+ !isConditional(this.version, this.bindingsList),
+ "addIdentity() is only supported with version 1 policies and non-conditional policies");
String nullIdentityMessage = "Null identities are not permitted.";
checkNotNull(first, nullIdentityMessage);
checkNotNull(others, nullIdentityMessage);
- for (Identity identity : others) {
- checkNotNull(identity, nullIdentityMessage);
+ checkNotNull(role, "The role cannot be null.");
+ for (int i = 0; i < bindingsList.size(); i++) {
+ Binding binding = bindingsList.get(i);
+ if (binding.getRole().equals(role.getValue())) {
+ Binding.Builder bindingBuilder = binding.toBuilder();
+ ImmutableList.Builder membersBuilder = ImmutableList.builder();
+ membersBuilder.addAll(binding.getMembers());
+ membersBuilder.add(first.strValue());
+ for (Identity identity : others) {
+ membersBuilder.add(identity.strValue());
+ }
+ bindingBuilder.setMembers(membersBuilder.build());
+ bindingsList.set(i, bindingBuilder.build());
+ return this;
+ }
}
- Set toAdd = new LinkedHashSet<>();
- toAdd.add(first);
- toAdd.addAll(Arrays.asList(others));
- Set identities = bindings.get(checkNotNull(role, "The role cannot be null."));
- if (identities == null) {
- identities = new HashSet<>();
- bindings.put(role, identities);
+ // Binding does not yet exist.
+ Binding.Builder bindingBuilder = Binding.newBuilder().setRole(role.getValue());
+ ImmutableList.Builder membersBuilder = ImmutableList.builder();
+ membersBuilder.add(first.strValue());
+ for (Identity identity : others) {
+ membersBuilder.add(identity.strValue());
}
- identities.addAll(toAdd);
+ bindingBuilder.setMembers(membersBuilder.build());
+ bindingsList.add(bindingBuilder.build());
return this;
}
/**
* Removes one or more identities from an existing binding. Does nothing if the binding
* associated with the provided role doesn't exist.
+ *
+ * @throws IllegalArgumentException if policy version is equal to 3 or has conditional bindings
*/
public final Builder removeIdentity(Role role, Identity first, Identity... others) {
- Set identities = bindings.get(role);
- if (identities != null) {
- identities.remove(first);
- identities.removeAll(Arrays.asList(others));
+ checkArgument(
+ !isConditional(this.version, this.bindingsList),
+ "removeIdentity() is only supported with version 1 policies and non-conditional policies");
+ String nullIdentityMessage = "Null identities are not permitted.";
+ checkNotNull(first, nullIdentityMessage);
+ checkNotNull(others, nullIdentityMessage);
+ checkNotNull(role, "The role cannot be null.");
+ for (int i = 0; i < bindingsList.size(); i++) {
+ Binding binding = bindingsList.get(i);
+ if (binding.getRole().equals(role.getValue())) {
+ Binding.Builder bindingBuilder = binding.toBuilder().removeMembers(first.strValue());
+ for (Identity identity : others) {
+ bindingBuilder.removeMembers(identity.strValue());
+ }
+ Binding updatedBindings = bindingBuilder.build();
+ bindingsList.set(i, updatedBindings);
+ break;
+ }
}
- if (identities != null && identities.isEmpty()) {
- bindings.remove(role);
+
+ Iterator iterator = bindingsList.iterator();
+ while (iterator.hasNext()) {
+ Binding binding = (Binding) iterator.next();
+ if (binding.getRole().equals(role.getValue()) && binding.getMembers().size() == 0) {
+ iterator.remove();
+ break;
+ }
}
+
return this;
}
@@ -236,11 +338,8 @@ public final Builder setEtag(String etag) {
return this;
}
- /**
- * Sets the version of the policy. The default version is 0, meaning only the "owner", "editor",
- * and "viewer" roles are permitted. If the version is 1, you may also use other roles.
- */
- protected final Builder setVersion(int version) {
+ /** Sets the version of the policy. */
+ public final Builder setVersion(int version) {
this.version = version;
return this;
}
@@ -252,11 +351,7 @@ public final Policy build() {
}
private Policy(Builder builder) {
- ImmutableMap.Builder> bindingsBuilder = ImmutableMap.builder();
- for (Map.Entry> binding : builder.bindings.entrySet()) {
- bindingsBuilder.put(binding.getKey(), ImmutableSet.copyOf(binding.getValue()));
- }
- this.bindings = bindingsBuilder.build();
+ this.bindingsList = ImmutableList.copyOf(builder.bindingsList);
this.etag = builder.etag;
this.version = builder.version;
}
@@ -266,9 +361,29 @@ public Builder toBuilder() {
return new Builder(this);
}
- /** Returns the map of bindings that comprises the policy. */
+ /**
+ * Returns the map of bindings that comprises the policy.
+ *
+ * @throws IllegalArgumentException if policy version is equal to 3 or has conditional bindings
+ */
public Map> getBindings() {
- return bindings;
+ checkArgument(
+ !isConditional(this.version, this.bindingsList),
+ "getBindings() is only supported with version 1 policies and non-conditional policies");
+ ImmutableMap.Builder> bindingsV1Builder = ImmutableMap.builder();
+ for (Binding binding : bindingsList) {
+ ImmutableSet.Builder identities = ImmutableSet.builder();
+ for (String member : binding.getMembers()) {
+ identities.add(Identity.valueOf(member));
+ }
+ bindingsV1Builder.put(Role.of(binding.getRole()), identities.build());
+ }
+ return bindingsV1Builder.build();
+ }
+
+ /** Returns the list of bindings that comprises the policy for version 3. */
+ public ImmutableList getBindingsList() {
+ return bindingsList;
}
/**
@@ -297,7 +412,7 @@ public int getVersion() {
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
- .add("bindings", bindings)
+ .add("bindings", bindingsList)
.add("etag", etag)
.add("version", version)
.toString();
@@ -305,7 +420,7 @@ public String toString() {
@Override
public int hashCode() {
- return Objects.hash(getClass(), bindings, etag, version);
+ return Objects.hash(getClass(), bindingsList, etag, version);
}
@Override
@@ -317,9 +432,10 @@ public boolean equals(Object obj) {
return false;
}
Policy other = (Policy) obj;
- return Objects.equals(bindings, other.getBindings())
- && Objects.equals(etag, other.getEtag())
- && Objects.equals(version, other.getVersion());
+ if (!bindingsList.equals(other.getBindingsList())) {
+ return false;
+ }
+ return Objects.equals(etag, other.getEtag()) && version == other.getVersion();
}
/** Returns a builder for {@code Policy} objects. */
diff --git a/google-cloud-core/src/test/java/com/google/cloud/PolicyTest.java b/google-cloud-core/src/test/java/com/google/cloud/PolicyTest.java
index 61d99223e4..eb03e33726 100644
--- a/google-cloud-core/src/test/java/com/google/cloud/PolicyTest.java
+++ b/google-cloud-core/src/test/java/com/google/cloud/PolicyTest.java
@@ -125,7 +125,7 @@ public void testIllegalPolicies() {
assertEquals("Null identities are not permitted.", ex.getMessage());
}
try {
- Policy.newBuilder().setBindings(null);
+ Policy.newBuilder().setBindings((Map>) null);
fail("Null bindings map should cause exception.");
} catch (NullPointerException ex) {
assertEquals("The provided map of bindings cannot be null.", ex.getMessage());
diff --git a/google-cloud-core/src/test/java/com/google/cloud/PolicyV3Test.java b/google-cloud-core/src/test/java/com/google/cloud/PolicyV3Test.java
new file mode 100644
index 0000000000..13cab4332c
--- /dev/null
+++ b/google-cloud-core/src/test/java/com/google/cloud/PolicyV3Test.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.cloud.Policy.DefaultMarshaller;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+
+public class PolicyV3Test {
+
+ private static final String ALL_USERS = "allUsers";
+ private static final String ALL_AUTH_USERS = "allAuthenticatedUsers";
+ private static final String USER = "user:abc@gmail.com";
+ private static final String SERVICE_ACCOUNT = "serviceAccount:service-account@gmail.com";
+ private static final String GROUP = "group:group@gmail.com";
+ private static final String DOMAIN = "domain:google.com";
+ private static final String VIEWER = "roles/viewer";
+ private static final String EDITOR = "roles/editor";
+ private static final String OWNER = "roles/owner";
+ private static final List MEMBERS_LIST_1 =
+ ImmutableList.of(USER, SERVICE_ACCOUNT, ALL_USERS);
+ private static final List MEMBERS_LIST_2 =
+ ImmutableList.of(ALL_AUTH_USERS, GROUP, DOMAIN);
+ private static final List BINDINGS_NO_CONDITIONS =
+ ImmutableList.of(
+ Binding.newBuilder().setRole(VIEWER).setMembers(MEMBERS_LIST_1).build(),
+ Binding.newBuilder().setRole(EDITOR).setMembers(MEMBERS_LIST_2).build());
+ private static final List BINDINGS_WITH_CONDITIONS =
+ ImmutableList.copyOf(BINDINGS_NO_CONDITIONS)
+ .of(
+ Binding.newBuilder()
+ .setRole(VIEWER)
+ .setMembers(MEMBERS_LIST_1)
+ .setCondition(
+ Condition.newBuilder()
+ .setTitle("Condition")
+ .setDescription("Condition")
+ .setExpression("Expr")
+ .build())
+ .build(),
+ Binding.newBuilder().setRole(EDITOR).setMembers(MEMBERS_LIST_2).build());
+ private static final Policy FULL_POLICY_V1 =
+ Policy.newBuilder().setBindings(BINDINGS_NO_CONDITIONS).setEtag("etag").setVersion(1).build();
+
+ private static final Policy FULL_POLICY_V3 =
+ Policy.newBuilder()
+ .setBindings(BINDINGS_WITH_CONDITIONS)
+ .setEtag("etag")
+ .setVersion(3)
+ .build();
+
+ private static final Policy FULL_POLICY_V3_WITH_VERSION_1 =
+ Policy.newBuilder()
+ .setBindings(BINDINGS_WITH_CONDITIONS)
+ .setEtag("etag")
+ .setVersion(1)
+ .build();
+
+ @Test
+ public void testBuilderV1() {
+ assertEquals(BINDINGS_NO_CONDITIONS, FULL_POLICY_V1.getBindingsList());
+ assertEquals(1, FULL_POLICY_V1.getVersion());
+ assertEquals("etag", FULL_POLICY_V1.getEtag());
+ Policy policy = FULL_POLICY_V1.toBuilder().setBindings(BINDINGS_NO_CONDITIONS).build();
+ assertEquals(BINDINGS_NO_CONDITIONS, policy.getBindingsList());
+ assertEquals("etag", policy.getEtag());
+ assertEquals(1, policy.getVersion());
+ }
+
+ @Test
+ public void testBuilderV3WithConditions() {
+ assertEquals(BINDINGS_WITH_CONDITIONS, FULL_POLICY_V3.getBindingsList());
+ assertEquals(3, FULL_POLICY_V3.getVersion());
+ assertEquals("etag", FULL_POLICY_V3.getEtag());
+ Policy policy = FULL_POLICY_V3.toBuilder().setBindings(BINDINGS_WITH_CONDITIONS).build();
+ assertEquals(BINDINGS_WITH_CONDITIONS, policy.getBindingsList());
+ assertEquals("etag", policy.getEtag());
+ assertEquals(3, policy.getVersion());
+ }
+
+ @Test
+ public void testBuilderV1ToV3Compatability() {
+ assertEquals(BINDINGS_WITH_CONDITIONS, FULL_POLICY_V3_WITH_VERSION_1.getBindingsList());
+ assertEquals(1, FULL_POLICY_V3_WITH_VERSION_1.getVersion());
+ assertEquals("etag", FULL_POLICY_V3_WITH_VERSION_1.getEtag());
+ Policy policy =
+ FULL_POLICY_V3_WITH_VERSION_1
+ .toBuilder()
+ .setBindings(BINDINGS_WITH_CONDITIONS)
+ .setVersion(3)
+ .build();
+ assertEquals(BINDINGS_WITH_CONDITIONS, policy.getBindingsList());
+ assertEquals("etag", policy.getEtag());
+ assertEquals(3, policy.getVersion());
+ }
+
+ @Test
+ public void removeMemberFromPolicy() {
+ assertEquals(3, FULL_POLICY_V3.getBindingsList().get(0).getMembers().size());
+ List bindings = new ArrayList<>(FULL_POLICY_V3.getBindingsList());
+
+ for (int i = 0; i < bindings.size(); i++) {
+ Binding binding = bindings.get(i);
+ if (binding.getRole().equals(VIEWER)) {
+ bindings.set(i, binding.toBuilder().removeMembers(ALL_USERS).build());
+ break;
+ }
+ }
+
+ Policy updatedPolicy = FULL_POLICY_V3.toBuilder().setBindings(bindings).build();
+ assertEquals(2, updatedPolicy.getBindingsList().get(0).getMembers().size());
+ }
+
+ @Test
+ public void addMemberFromPolicy() {
+ assertEquals(3, FULL_POLICY_V3.getBindingsList().get(0).getMembers().size());
+ List bindings = new ArrayList<>(FULL_POLICY_V3.getBindingsList());
+
+ for (int i = 0; i < bindings.size(); i++) {
+ Binding binding = bindings.get(i);
+ if (binding.getRole().equals(VIEWER)) {
+ bindings.set(i, binding.toBuilder().addMembers("user:example@example.com").build());
+ }
+ }
+
+ Policy updatedPolicy = FULL_POLICY_V3.toBuilder().setBindings(bindings).build();
+ assertEquals(4, updatedPolicy.getBindingsList().get(0).getMembers().size());
+ }
+
+ @Test
+ public void removeBindingFromPolicy() {
+ assertEquals(2, FULL_POLICY_V3.getBindingsList().size());
+ List bindings = new ArrayList<>(FULL_POLICY_V3.getBindingsList());
+
+ Iterator iterator = bindings.iterator();
+ while (iterator.hasNext()) {
+ Binding binding = (Binding) iterator.next();
+ if (binding.getRole().equals(EDITOR) && binding.getCondition() == null) {
+ iterator.remove();
+ break;
+ }
+ }
+
+ Policy updatedPolicy = FULL_POLICY_V3.toBuilder().setBindings(bindings).build();
+ assertEquals(1, updatedPolicy.getBindingsList().size());
+ }
+
+ @Test
+ public void addBindingToPolicy() {
+ assertEquals(2, FULL_POLICY_V3.getBindingsList().size());
+ List bindings = new ArrayList<>(FULL_POLICY_V3.getBindingsList());
+ bindings.add(Binding.newBuilder().setRole(OWNER).setMembers(ImmutableList.of(USER)).build());
+ Policy updatedPolicy = FULL_POLICY_V3.toBuilder().setBindings(bindings).build();
+ assertEquals(3, updatedPolicy.getBindingsList().size());
+ }
+
+ @Test
+ public void testIllegalPolicies() {
+ try {
+ Binding.newBuilder().setRole(null).build();
+ fail("Null role should cause exception.");
+ } catch (NullPointerException ex) {
+ assertEquals("Null role", ex.getMessage());
+ }
+ try {
+ FULL_POLICY_V3
+ .toBuilder()
+ .setBindings(
+ Arrays.asList(
+ Binding.newBuilder()
+ .setRole("test")
+ .setMembers(Arrays.asList(null, "user"))
+ .build()))
+ .build();
+ fail("Null member should cause exception.");
+ } catch (NullPointerException ex) {
+ assertEquals("at index 0", ex.getMessage());
+ }
+ try {
+ FULL_POLICY_V3.getBindings();
+ fail("getBindings() should cause exception with Policy V3.");
+ } catch (IllegalArgumentException ex) {
+ assertEquals(
+ "getBindings() is only supported with version 1 policies and non-conditional policies",
+ ex.getMessage());
+ }
+ try {
+ FULL_POLICY_V3.toBuilder().addIdentity(Role.editor(), Identity.allUsers());
+ fail("getBindings() should cause exception with Policy V3.");
+ } catch (IllegalArgumentException ex) {
+ assertEquals(
+ "addIdentity() is only supported with version 1 policies and non-conditional policies",
+ ex.getMessage());
+ }
+ try {
+ FULL_POLICY_V3.toBuilder().removeIdentity(Role.editor(), Identity.allUsers());
+ fail("getBindings() should cause exception with Policy V3.");
+ } catch (IllegalArgumentException ex) {
+ assertEquals(
+ "removeIdentity() is only supported with version 1 policies and non-conditional policies",
+ ex.getMessage());
+ }
+ try {
+ FULL_POLICY_V3.toBuilder().setBindings(FULL_POLICY_V1.getBindings());
+ fail("getBindings() should cause exception with Policy V3.");
+ } catch (IllegalArgumentException ex) {
+ assertEquals(
+ "setBindings() is only supported with version 1 policies and non-conditional policies",
+ ex.getMessage());
+ }
+ }
+
+ @Test
+ public void testEqualsHashCode() {
+ assertNotNull(FULL_POLICY_V3);
+ Policy emptyPolicy = Policy.newBuilder().build();
+ Policy anotherPolicy = Policy.newBuilder().build();
+ assertEquals(emptyPolicy, anotherPolicy);
+ assertEquals(emptyPolicy.hashCode(), anotherPolicy.hashCode());
+ assertNotEquals(FULL_POLICY_V3, FULL_POLICY_V1);
+ assertNotEquals(FULL_POLICY_V3.hashCode(), FULL_POLICY_V1.hashCode());
+ Policy copy = FULL_POLICY_V1.toBuilder().build();
+ assertEquals(FULL_POLICY_V1, copy);
+ assertEquals(FULL_POLICY_V1.hashCode(), copy.hashCode());
+ }
+
+ @Test
+ public void testBindings() {
+ assertTrue(Policy.newBuilder().build().getBindingsList().isEmpty());
+ assertEquals(BINDINGS_WITH_CONDITIONS, FULL_POLICY_V3.getBindingsList());
+ }
+
+ @Test
+ public void testEtag() {
+ assertNotNull(FULL_POLICY_V3.getEtag());
+ assertEquals("etag", FULL_POLICY_V3.getEtag());
+ }
+
+ @Test
+ public void testVersion() {
+ assertEquals(1, FULL_POLICY_V1.getVersion());
+ assertEquals(3, FULL_POLICY_V3.getVersion());
+ assertEquals(1, FULL_POLICY_V3_WITH_VERSION_1.getVersion());
+ }
+
+ @Test
+ public void testDefaultMarshaller() {
+ DefaultMarshaller marshaller = new DefaultMarshaller();
+ Policy emptyPolicy = Policy.newBuilder().build();
+ assertEquals(emptyPolicy, marshaller.fromPb(marshaller.toPb(emptyPolicy)));
+ assertEquals(FULL_POLICY_V3, marshaller.fromPb(marshaller.toPb(FULL_POLICY_V3)));
+ assertEquals(FULL_POLICY_V1, marshaller.fromPb(marshaller.toPb(FULL_POLICY_V1)));
+ com.google.iam.v1.Policy policyPb = com.google.iam.v1.Policy.getDefaultInstance();
+ Policy policy = marshaller.fromPb(policyPb);
+ assertTrue(policy.getBindingsList().isEmpty());
+ assertNull(policy.getEtag());
+ assertEquals(0, policy.getVersion());
+ }
+}
diff --git a/pom.xml b/pom.xml
index edec8b77ec..c3ee892baa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -151,7 +151,8 @@
UTF-8
github
google-cloud-core-parent
-
+ 1.7
+ 1.7
1.53.1
1.8.1
1.17.0
@@ -231,7 +232,12 @@
pom
import
-
+
+ com.google.auto.value
+ auto-value-annotations
+ ${auto-value-annotations.version}
+ true
+
com.google.api
api-common
@@ -247,7 +253,6 @@
proto-google-iam-v1
${google.iam.version}
-
io.opencensus
@@ -324,7 +329,20 @@
org.objenesis:objenesis
+
+ maven-compiler-plugin
+
+
+
+ com.google.auto.value
+ auto-value
+ ${auto-value.version}
+
+
+
+
+