Skip to content
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

Refactor IAM-related classes to be ready to support more services #1224

Merged
merged 3 commits into from
Sep 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion google-cloud-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,12 @@
<dependency>
<groupId>com.google.api.grpc</groupId>
<artifactId>grpc-google-common-protos</artifactId>
<version>0.0.7</version>
<version>0.0.9</version>
</dependency>
<dependency>
<groupId>com.google.api.grpc</groupId>
<artifactId>grpc-google-iam-v1</artifactId>
<version>0.0.9</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
import java.util.Objects;

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

@Override
public String toString() {
return strValue();
}

@Override
public int hashCode() {
return Objects.hash(value, type);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,87 +19,154 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
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 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.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
* Base 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 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.
* 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
* 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.
*
* @param <R> the data type of roles (should be serializable)
* @see <a href="https://cloud.google.com/iam/reference/rest/v1/Policy">Policy</a>
*/
public abstract class IamPolicy<R> implements Serializable {
public final class Policy implements Serializable {

private static final long serialVersionUID = 1114489978726897720L;
private static final long serialVersionUID = -3348914530232544290L;

private final Map<R, Set<Identity>> bindings;
private final Map<Role, Set<Identity>> bindings;
private final String etag;
private final Integer version;
private final int version;

public abstract static class Marshaller<T> {

protected static final Function<String, Identity> IDENTITY_VALUE_OF_FUNCTION =
new Function<String, Identity>() {
@Override
public Identity apply(String identityPb) {
return Identity.valueOf(identityPb);
}
};
protected static final Function<Identity, String> IDENTITY_STR_VALUE_FUNCTION =
new Function<Identity, String>() {
@Override
public String apply(Identity identity) {
return identity.strValue();
}
};

protected abstract Policy fromPb(T policyPb);

protected abstract T toPb(Policy policy);
}

public static class DefaultMarshaller extends Marshaller<com.google.iam.v1.Policy> {

@Override
protected Policy fromPb(com.google.iam.v1.Policy policyPb) {
Map<Role, Set<Identity>> bindings = new HashMap<>();
for (com.google.iam.v1.Binding bindingPb : policyPb.getBindingsList()) {
bindings.put(Role.of(bindingPb.getRole()),
ImmutableSet.copyOf(
Lists.transform(bindingPb.getMembersList(), IDENTITY_VALUE_OF_FUNCTION)));
}
return builder()
.bindings(bindings)
.etag(policyPb.getEtag().isEmpty() ? null
: BaseEncoding.base64().encode(policyPb.getEtag().toByteArray()))
.version(policyPb.getVersion())
.build();
}

@Override
protected com.google.iam.v1.Policy toPb(Policy policy) {
com.google.iam.v1.Policy.Builder policyBuilder = com.google.iam.v1.Policy.newBuilder();
List<com.google.iam.v1.Binding> bindingPbList = new LinkedList<>();
for (Map.Entry<Role, Set<Identity>> binding : policy.bindings().entrySet()) {
com.google.iam.v1.Binding.Builder bindingBuilder = com.google.iam.v1.Binding.newBuilder();
bindingBuilder.setRole(binding.getKey().value());
bindingBuilder.addAllMembers(
Lists.transform(new ArrayList<>(binding.getValue()), IDENTITY_STR_VALUE_FUNCTION));
bindingPbList.add(bindingBuilder.build());
}
policyBuilder.addAllBindings(bindingPbList);
if (policy.etag != null) {
policyBuilder.setEtag(ByteString.copyFrom(BaseEncoding.base64().decode(policy.etag)));
}
policyBuilder.setVersion(policy.version);
return policyBuilder.build();
}
}

/**
* Builder for an IAM Policy.
*
* @param <R> the data type of roles
* @param <B> the subclass extending this abstract builder
* A builder for {@code Policy} objects.
*/
public abstract static class Builder<R, B extends Builder<R, B>> {
public static class Builder {

private final Map<R, Set<Identity>> bindings = new HashMap<>();
private final Map<Role, Set<Identity>> bindings = new HashMap<>();
private String etag;
private Integer version;
private int version;

/**
* Constructor for IAM Policy builder.
*/
protected Builder() {}

protected Builder(Policy policy) {
bindings(policy.bindings);
etag(policy.etag);
version(policy.version);
}

/**
* 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
*/
public final B bindings(Map<R, Set<Identity>> bindings) {
public final Builder bindings(Map<Role, Set<Identity>> bindings) {
checkNotNull(bindings, "The provided map of bindings cannot be null.");
for (Map.Entry<R, Set<Identity>> binding : bindings.entrySet()) {
for (Map.Entry<Role, Set<Identity>> binding : bindings.entrySet()) {
checkNotNull(binding.getKey(), "The role cannot be null.");
Set<Identity> 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();
for (Map.Entry<R, Set<Identity>> binding : bindings.entrySet()) {
this.bindings.put(binding.getKey(), new HashSet<Identity>(binding.getValue()));
for (Map.Entry<Role, Set<Identity>> binding : bindings.entrySet()) {
this.bindings.put(binding.getKey(), new HashSet<>(binding.getValue()));
}
return self();
return this;
}

/**
* Removes the role (and all identities associated with that role) from the policy.
*/
public final B removeRole(R role) {
public final Builder removeRole(Role role) {
bindings.remove(role);
return self();
return this;
}

/**
* Adds one or more identities to the policy under the role specified.
*
* @throws NullPointerException if the role or any of the identities is null.
*/
public final B addIdentity(R role, Identity first, Identity... others) {
public final Builder addIdentity(Role role, Identity first, Identity... others) {
String nullIdentityMessage = "Null identities are not permitted.";
checkNotNull(first, nullIdentityMessage);
checkNotNull(others, nullIdentityMessage);
Expand All @@ -111,18 +178,18 @@ public final B addIdentity(R role, Identity first, Identity... others) {
toAdd.addAll(Arrays.asList(others));
Set<Identity> identities = bindings.get(checkNotNull(role, "The role cannot be null."));
if (identities == null) {
identities = new HashSet<Identity>();
identities = new HashSet<>();
bindings.put(role, identities);
}
identities.addAll(toAdd);
return self();
return this;
}

/**
* Removes one or more identities from an existing binding. Does nothing if the binding
* associated with the provided role doesn't exist.
*/
public final B removeIdentity(R role, Identity first, Identity... others) {
public final Builder removeIdentity(Role role, Identity first, Identity... others) {
Set<Identity> identities = bindings.get(role);
if (identities != null) {
identities.remove(first);
Expand All @@ -131,7 +198,7 @@ public final B removeIdentity(R role, Identity first, Identity... others) {
if (identities != null && identities.isEmpty()) {
bindings.remove(role);
}
return self();
return this;
}

/**
Expand All @@ -145,31 +212,31 @@ public final B removeIdentity(R role, Identity first, Identity... others) {
* applied to the same version of the policy. If no etag is provided in the call to
* setIamPolicy, then the existing policy is overwritten blindly.
*/
protected final B etag(String etag) {
protected final Builder etag(String etag) {
this.etag = etag;
return self();
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 B version(Integer version) {
protected final Builder version(int version) {
this.version = version;
return self();
return this;
}

@SuppressWarnings("unchecked")
private B self() {
return (B) this;
/**
* Creates a {@code Policy} object.
*/
public final Policy build() {
return new Policy(this);
}

public abstract IamPolicy<R> build();
}

protected IamPolicy(Builder<R, ? extends Builder<R, ?>> builder) {
ImmutableMap.Builder<R, Set<Identity>> bindingsBuilder = ImmutableMap.builder();
for (Map.Entry<R, Set<Identity>> binding : builder.bindings.entrySet()) {
private Policy(Builder builder) {
ImmutableMap.Builder<Role, Set<Identity>> bindingsBuilder = ImmutableMap.builder();
for (Map.Entry<Role, Set<Identity>> binding : builder.bindings.entrySet()) {
bindingsBuilder.put(binding.getKey(), ImmutableSet.copyOf(binding.getValue()));
}
this.bindings = bindingsBuilder.build();
Expand All @@ -180,17 +247,19 @@ protected IamPolicy(Builder<R, ? extends Builder<R, ?>> builder) {
/**
* Returns a builder containing the properties of this IAM Policy.
*/
public abstract Builder<R, ? extends Builder<R, ?>> toBuilder();
public Builder toBuilder() {
return new Builder(this);
}

/**
* The map of bindings that comprises the policy.
* Returns the map of bindings that comprises the policy.
*/
public Map<R, Set<Identity>> bindings() {
public Map<Role, Set<Identity>> bindings() {
return bindings;
}

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

/**
* 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.
* Returns 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.
*/
public Integer version() {
public int version() {
return version;
}

@Override
public final int hashCode() {
public String toString() {
return MoreObjects.toStringHelper(this)
.add("bindings", bindings)
.add("etag", etag)
.add("version", version)
.toString();
}

@Override
public int hashCode() {
return Objects.hash(getClass(), bindings, etag, version);
}

@Override
public final boolean equals(Object obj) {
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || !obj.getClass().equals(getClass())) {
if (!(obj instanceof Policy)) {
return false;
}
@SuppressWarnings("rawtypes")
IamPolicy other = (IamPolicy) obj;
Policy other = (Policy) obj;
return Objects.equals(bindings, other.bindings())
&& Objects.equals(etag, other.etag())
&& Objects.equals(version, other.version());
}

/**
* Returns a builder for {@code Policy} objects.
*/
public static Builder builder() {
return new Builder();
}
}
Loading