Skip to content

Commit a493457

Browse files
mziccardaozarov
authored andcommitted
Refactor IAM-related classes to be ready to support more services (#1224)
Refactor IAM-related classes to be ready to support more services - Remove resourcemanager's Policy class - Rename core's IamPolicy to Policy - Add Role class to hold string values for roles - Add Policy.Marshaller and Policy.DefaultMarshaller classes to convert Policy to/from gRPC protos - Add PolicyMarshaller to resourcemanager to convert Policy to/from resourcemanager's protos - Update READMEs, javadoc and examples - Add factory methods for primitive roles, other minor fixes
1 parent 8cf6a66 commit a493457

20 files changed

Lines changed: 575 additions & 456 deletions

File tree

google-cloud-core/pom.xml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,12 @@
111111
<dependency>
112112
<groupId>com.google.api.grpc</groupId>
113113
<artifactId>grpc-google-common-protos</artifactId>
114-
<version>0.0.7</version>
114+
<version>0.0.9</version>
115+
</dependency>
116+
<dependency>
117+
<groupId>com.google.api.grpc</groupId>
118+
<artifactId>grpc-google-iam-v1</artifactId>
119+
<version>0.0.9</version>
115120
</dependency>
116121
</dependencies>
117122
</project>

google-cloud-core/src/main/java/com/google/cloud/Identity.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424
import java.util.Objects;
2525

2626
/**
27-
* An identity in an {@link IamPolicy}. The following types of identities are permitted in IAM
28-
* policies:
27+
* An identity in a {@link Policy}. The following types of identities are permitted in IAM policies:
2928
* <ul>
3029
* <li>Google account
3130
* <li>Service account
@@ -161,6 +160,11 @@ public static Identity domain(String domain) {
161160
return new Identity(Type.DOMAIN, checkNotNull(domain));
162161
}
163162

163+
@Override
164+
public String toString() {
165+
return strValue();
166+
}
167+
164168
@Override
165169
public int hashCode() {
166170
return Objects.hash(value, type);

google-cloud-core/src/main/java/com/google/cloud/IamPolicy.java renamed to google-cloud-core/src/main/java/com/google/cloud/Policy.java

Lines changed: 139 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -19,87 +19,154 @@
1919
import static com.google.common.base.Preconditions.checkArgument;
2020
import static com.google.common.base.Preconditions.checkNotNull;
2121

22+
import com.google.common.base.Function;
23+
import com.google.common.base.MoreObjects;
2224
import com.google.common.collect.ImmutableMap;
2325
import com.google.common.collect.ImmutableSet;
26+
import com.google.common.collect.Lists;
27+
import com.google.common.io.BaseEncoding;
28+
import com.google.protobuf.ByteString;
2429

2530
import java.io.Serializable;
31+
import java.util.ArrayList;
2632
import java.util.Arrays;
2733
import java.util.HashMap;
2834
import java.util.HashSet;
2935
import java.util.LinkedHashSet;
36+
import java.util.LinkedList;
37+
import java.util.List;
3038
import java.util.Map;
3139
import java.util.Objects;
3240
import java.util.Set;
3341

3442
/**
35-
* Base class for Identity and Access Management (IAM) policies. IAM policies are used to specify
36-
* access settings for Cloud Platform resources. A policy is a map of bindings. A binding assigns
37-
* a set of identities to a role, where the identities can be user accounts, Google groups, Google
38-
* domains, and service accounts. A role is a named list of permissions defined by IAM.
43+
* Class for Identity and Access Management (IAM) policies. IAM policies are used to specify access
44+
* settings for Cloud Platform resources. A policy is a map of bindings. A binding assigns a set of
45+
* identities to a role, where the identities can be user accounts, Google groups, Google domains,
46+
* and service accounts. A role is a named list of permissions defined by IAM.
3947
*
40-
* @param <R> the data type of roles (should be serializable)
4148
* @see <a href="https://cloud.google.com/iam/reference/rest/v1/Policy">Policy</a>
4249
*/
43-
public abstract class IamPolicy<R> implements Serializable {
50+
public final class Policy implements Serializable {
4451

45-
private static final long serialVersionUID = 1114489978726897720L;
52+
private static final long serialVersionUID = -3348914530232544290L;
4653

47-
private final Map<R, Set<Identity>> bindings;
54+
private final Map<Role, Set<Identity>> bindings;
4855
private final String etag;
49-
private final Integer version;
56+
private final int version;
57+
58+
public abstract static class Marshaller<T> {
59+
60+
protected static final Function<String, Identity> IDENTITY_VALUE_OF_FUNCTION =
61+
new Function<String, Identity>() {
62+
@Override
63+
public Identity apply(String identityPb) {
64+
return Identity.valueOf(identityPb);
65+
}
66+
};
67+
protected static final Function<Identity, String> IDENTITY_STR_VALUE_FUNCTION =
68+
new Function<Identity, String>() {
69+
@Override
70+
public String apply(Identity identity) {
71+
return identity.strValue();
72+
}
73+
};
74+
75+
protected abstract Policy fromPb(T policyPb);
76+
77+
protected abstract T toPb(Policy policy);
78+
}
79+
80+
public static class DefaultMarshaller extends Marshaller<com.google.iam.v1.Policy> {
81+
82+
@Override
83+
protected Policy fromPb(com.google.iam.v1.Policy policyPb) {
84+
Map<Role, Set<Identity>> bindings = new HashMap<>();
85+
for (com.google.iam.v1.Binding bindingPb : policyPb.getBindingsList()) {
86+
bindings.put(Role.of(bindingPb.getRole()),
87+
ImmutableSet.copyOf(
88+
Lists.transform(bindingPb.getMembersList(), IDENTITY_VALUE_OF_FUNCTION)));
89+
}
90+
return builder()
91+
.bindings(bindings)
92+
.etag(policyPb.getEtag().isEmpty() ? null
93+
: BaseEncoding.base64().encode(policyPb.getEtag().toByteArray()))
94+
.version(policyPb.getVersion())
95+
.build();
96+
}
97+
98+
@Override
99+
protected com.google.iam.v1.Policy toPb(Policy policy) {
100+
com.google.iam.v1.Policy.Builder policyBuilder = com.google.iam.v1.Policy.newBuilder();
101+
List<com.google.iam.v1.Binding> bindingPbList = new LinkedList<>();
102+
for (Map.Entry<Role, Set<Identity>> binding : policy.bindings().entrySet()) {
103+
com.google.iam.v1.Binding.Builder bindingBuilder = com.google.iam.v1.Binding.newBuilder();
104+
bindingBuilder.setRole(binding.getKey().value());
105+
bindingBuilder.addAllMembers(
106+
Lists.transform(new ArrayList<>(binding.getValue()), IDENTITY_STR_VALUE_FUNCTION));
107+
bindingPbList.add(bindingBuilder.build());
108+
}
109+
policyBuilder.addAllBindings(bindingPbList);
110+
if (policy.etag != null) {
111+
policyBuilder.setEtag(ByteString.copyFrom(BaseEncoding.base64().decode(policy.etag)));
112+
}
113+
policyBuilder.setVersion(policy.version);
114+
return policyBuilder.build();
115+
}
116+
}
50117

51118
/**
52-
* Builder for an IAM Policy.
53-
*
54-
* @param <R> the data type of roles
55-
* @param <B> the subclass extending this abstract builder
119+
* A builder for {@code Policy} objects.
56120
*/
57-
public abstract static class Builder<R, B extends Builder<R, B>> {
121+
public static class Builder {
58122

59-
private final Map<R, Set<Identity>> bindings = new HashMap<>();
123+
private final Map<Role, Set<Identity>> bindings = new HashMap<>();
60124
private String etag;
61-
private Integer version;
125+
private int version;
62126

63-
/**
64-
* Constructor for IAM Policy builder.
65-
*/
66127
protected Builder() {}
67128

129+
protected Builder(Policy policy) {
130+
bindings(policy.bindings);
131+
etag(policy.etag);
132+
version(policy.version);
133+
}
134+
68135
/**
69136
* Replaces the builder's map of bindings with the given map of bindings.
70137
*
71138
* @throws NullPointerException if the given map is null or contains any null keys or values
72139
* @throws IllegalArgumentException if any identities in the given map are null
73140
*/
74-
public final B bindings(Map<R, Set<Identity>> bindings) {
141+
public final Builder bindings(Map<Role, Set<Identity>> bindings) {
75142
checkNotNull(bindings, "The provided map of bindings cannot be null.");
76-
for (Map.Entry<R, Set<Identity>> binding : bindings.entrySet()) {
143+
for (Map.Entry<Role, Set<Identity>> binding : bindings.entrySet()) {
77144
checkNotNull(binding.getKey(), "The role cannot be null.");
78145
Set<Identity> identities = binding.getValue();
79146
checkNotNull(identities, "A role cannot be assigned to a null set of identities.");
80147
checkArgument(!identities.contains(null), "Null identities are not permitted.");
81148
}
82149
this.bindings.clear();
83-
for (Map.Entry<R, Set<Identity>> binding : bindings.entrySet()) {
84-
this.bindings.put(binding.getKey(), new HashSet<Identity>(binding.getValue()));
150+
for (Map.Entry<Role, Set<Identity>> binding : bindings.entrySet()) {
151+
this.bindings.put(binding.getKey(), new HashSet<>(binding.getValue()));
85152
}
86-
return self();
153+
return this;
87154
}
88155

89156
/**
90157
* Removes the role (and all identities associated with that role) from the policy.
91158
*/
92-
public final B removeRole(R role) {
159+
public final Builder removeRole(Role role) {
93160
bindings.remove(role);
94-
return self();
161+
return this;
95162
}
96163

97164
/**
98165
* Adds one or more identities to the policy under the role specified.
99166
*
100167
* @throws NullPointerException if the role or any of the identities is null.
101168
*/
102-
public final B addIdentity(R role, Identity first, Identity... others) {
169+
public final Builder addIdentity(Role role, Identity first, Identity... others) {
103170
String nullIdentityMessage = "Null identities are not permitted.";
104171
checkNotNull(first, nullIdentityMessage);
105172
checkNotNull(others, nullIdentityMessage);
@@ -111,18 +178,18 @@ public final B addIdentity(R role, Identity first, Identity... others) {
111178
toAdd.addAll(Arrays.asList(others));
112179
Set<Identity> identities = bindings.get(checkNotNull(role, "The role cannot be null."));
113180
if (identities == null) {
114-
identities = new HashSet<Identity>();
181+
identities = new HashSet<>();
115182
bindings.put(role, identities);
116183
}
117184
identities.addAll(toAdd);
118-
return self();
185+
return this;
119186
}
120187

121188
/**
122189
* Removes one or more identities from an existing binding. Does nothing if the binding
123190
* associated with the provided role doesn't exist.
124191
*/
125-
public final B removeIdentity(R role, Identity first, Identity... others) {
192+
public final Builder removeIdentity(Role role, Identity first, Identity... others) {
126193
Set<Identity> identities = bindings.get(role);
127194
if (identities != null) {
128195
identities.remove(first);
@@ -131,7 +198,7 @@ public final B removeIdentity(R role, Identity first, Identity... others) {
131198
if (identities != null && identities.isEmpty()) {
132199
bindings.remove(role);
133200
}
134-
return self();
201+
return this;
135202
}
136203

137204
/**
@@ -145,31 +212,31 @@ public final B removeIdentity(R role, Identity first, Identity... others) {
145212
* applied to the same version of the policy. If no etag is provided in the call to
146213
* setIamPolicy, then the existing policy is overwritten blindly.
147214
*/
148-
protected final B etag(String etag) {
215+
protected final Builder etag(String etag) {
149216
this.etag = etag;
150-
return self();
217+
return this;
151218
}
152219

153220
/**
154221
* Sets the version of the policy. The default version is 0, meaning only the "owner", "editor",
155222
* and "viewer" roles are permitted. If the version is 1, you may also use other roles.
156223
*/
157-
protected final B version(Integer version) {
224+
protected final Builder version(int version) {
158225
this.version = version;
159-
return self();
226+
return this;
160227
}
161228

162-
@SuppressWarnings("unchecked")
163-
private B self() {
164-
return (B) this;
229+
/**
230+
* Creates a {@code Policy} object.
231+
*/
232+
public final Policy build() {
233+
return new Policy(this);
165234
}
166-
167-
public abstract IamPolicy<R> build();
168235
}
169236

170-
protected IamPolicy(Builder<R, ? extends Builder<R, ?>> builder) {
171-
ImmutableMap.Builder<R, Set<Identity>> bindingsBuilder = ImmutableMap.builder();
172-
for (Map.Entry<R, Set<Identity>> binding : builder.bindings.entrySet()) {
237+
private Policy(Builder builder) {
238+
ImmutableMap.Builder<Role, Set<Identity>> bindingsBuilder = ImmutableMap.builder();
239+
for (Map.Entry<Role, Set<Identity>> binding : builder.bindings.entrySet()) {
173240
bindingsBuilder.put(binding.getKey(), ImmutableSet.copyOf(binding.getValue()));
174241
}
175242
this.bindings = bindingsBuilder.build();
@@ -180,17 +247,19 @@ protected IamPolicy(Builder<R, ? extends Builder<R, ?>> builder) {
180247
/**
181248
* Returns a builder containing the properties of this IAM Policy.
182249
*/
183-
public abstract Builder<R, ? extends Builder<R, ?>> toBuilder();
250+
public Builder toBuilder() {
251+
return new Builder(this);
252+
}
184253

185254
/**
186-
* The map of bindings that comprises the policy.
255+
* Returns the map of bindings that comprises the policy.
187256
*/
188-
public Map<R, Set<Identity>> bindings() {
257+
public Map<Role, Set<Identity>> bindings() {
189258
return bindings;
190259
}
191260

192261
/**
193-
* The policy's etag.
262+
* Returns the policy's etag.
194263
*
195264
* <p>Etags are used for optimistic concurrency control as a way to help prevent simultaneous
196265
* updates of a policy from overwriting each other. It is strongly suggested that systems make
@@ -205,30 +274,45 @@ public String etag() {
205274
}
206275

207276
/**
208-
* Sets the version of the policy. The default version is 0, meaning only the "owner", "editor",
209-
* and "viewer" roles are permitted. If the version is 1, you may also use other roles.
277+
* Returns the version of the policy. The default version is 0, meaning only the "owner",
278+
* "editor", and "viewer" roles are permitted. If the version is 1, you may also use other roles.
210279
*/
211-
public Integer version() {
280+
public int version() {
212281
return version;
213282
}
214283

215284
@Override
216-
public final int hashCode() {
285+
public String toString() {
286+
return MoreObjects.toStringHelper(this)
287+
.add("bindings", bindings)
288+
.add("etag", etag)
289+
.add("version", version)
290+
.toString();
291+
}
292+
293+
@Override
294+
public int hashCode() {
217295
return Objects.hash(getClass(), bindings, etag, version);
218296
}
219297

220298
@Override
221-
public final boolean equals(Object obj) {
299+
public boolean equals(Object obj) {
222300
if (obj == this) {
223301
return true;
224302
}
225-
if (obj == null || !obj.getClass().equals(getClass())) {
303+
if (!(obj instanceof Policy)) {
226304
return false;
227305
}
228-
@SuppressWarnings("rawtypes")
229-
IamPolicy other = (IamPolicy) obj;
306+
Policy other = (Policy) obj;
230307
return Objects.equals(bindings, other.bindings())
231308
&& Objects.equals(etag, other.etag())
232309
&& Objects.equals(version, other.version());
233310
}
311+
312+
/**
313+
* Returns a builder for {@code Policy} objects.
314+
*/
315+
public static Builder builder() {
316+
return new Builder();
317+
}
234318
}

0 commit comments

Comments
 (0)