Google-cloud-java: Can't attach custom role to service account or container

Created on 7 Dec 2017  路  12Comments  路  Source: googleapis/google-cloud-java

I created a custom role that consists of some permission but when I tried to attach it to service account or container programatically it always give error. Is it not supported yet? If it is supported how can I attach it to service account or container?

My code are as follow:
In Google Cloud Storage

Policy policy = storage.getIamPolicy(bucketName);

Policy updatedPolicy = storage.setIamPolicy(bucketName, policy.toBuilder().addIdentity(Role.of("roles/CustomRole341"), Identity.serviceAccount(emailServiceAccount)).build());
ServiceAccount user = findServiceAccount(iam, username, projectName);
List<String> member = new ArrayList<String>();
member.add(Identity.serviceAccount(user.getEmail()).toString());

Binding s = new Binding();
s.setRole("roles/CustomRole341");
s.setMembers(member);

List<Binding> bindings = new ArrayList<Binding>();
bindings.add(s);

Policy a = new Policy();
a.setBindings(bindings);

SetIamPolicyRequest req = new SetIamPolicyRequest();
req.setPolicy(a);

iam.projects().serviceAccounts().setIamPolicy("projects/" + projectName + "/serviceAccounts/" + username +  "@" + projectName + ".iam.gserviceaccount.com" , req).execute()
storage p2 question

Most helpful comment

Just updating here too- I posted in https://github.com/terraform-providers/terraform-provider-google/issues/993 that the reason this is happening is that the name of custom roles has to be in the form projects/{project-id}/roles/{role-id}- omitting the project will cause it to fail, just as it would fail if you typo-ed the name of the role.

All 12 comments

@ihsanhaikalz What is the error that you get?

@lesv Do you know off-hand the correct way to do this?

@vchudnov-g here is the error that I got

Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
  "code" : 400,
  "errors" : [ {
    "domain" : "global",
    "message" : "Role roles/CustomRole341 is not supported for this resource.",
    "reason" : "badRequest"
  } ],
  "message" : "Role roles/CustomRole341 is not supported for this resource.",
  "status" : "INVALID_ARGUMENT"
}
    at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
    at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
    at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1065)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
    at gcp.GoogleIAM.testSetRoleToServiceAccount(GoogleIAM.java:236)
    at gcp.GoogleIAM.main(GoogleIAM.java:73)

I am not sure whether the naming of the role is correct by using roles/CustomRole341 because I tried to follow other roles like roles/storage.admin.

@ihsanhaikalz Is CustomRole341 defined? Did you go through the procedure in https://cloud.google.com/iam/docs/creating-custom-roles#creating_a_custom_role to create it before trying to use it? Do you get the same error if you use one of the pre-defined roles?

@ihsanhaikalz Where are you running this?
@jabubake PTAL

In general, default service accounts are provided to GCP and GAE to run your code, you need to add whatever scope is needed beyond the https://www.googleapis.com/auth/cloud-platform scope to your default service account using Cloud Console IAM & admin > IAM

You can also provide a custom service account json file and use that instead of the default credential provider to do auth.

--

I have this exact problem too!


# Add the temporary K8 discovery project
resource "google_project" "k8-discovery" {
  name            = "at-k8-discovery"
  project_id      = "k8-discovery-185615"
  billing_account = "${local.billing_account_id}"
  folder_id       = "${google_folder.delivery-engineering.name}"
}

resource "google_project_iam_custom_role" "container-registry-viewer" {
  role_id     = "container.registry.viewer"
  title       = "Container Registry Viewer"
  description = "Enables someone to view/pull from the GCR"
  permissions = ["storage.objects.get", "storage.objects.list"]
}

resource "google_project_iam_policy" "project" {
  project     = "${google_project.k8-discovery.project_id}"
  policy_data = "${data.google_iam_policy.k8-policy.policy_data}"
}

data "google_iam_policy" "k8-policy" {
  binding {
    role = "roles/container.registry.viewer"

error is googleapi: Error 400: Role roles/container.registry.viewer is not supported for this resource., badRequest

@frankyn : Frank, can you take a look at the code snippet provided in the initial post?

FYI I worked around this by specifying a fully qualified role - see https://github.com/terraform-providers/terraform-provider-google/issues/993

Just updating here too- I posted in https://github.com/terraform-providers/terraform-provider-google/issues/993 that the reason this is happening is that the name of custom roles has to be in the form projects/{project-id}/roles/{role-id}- omitting the project will cause it to fail, just as it would fail if you typo-ed the name of the role.

Thanks @danawillow and @Stono, I tried using the correct Role resource form with the client and I'm still seeing the same issue. Reviewing implementation of class Role, the Rule.of() expects roles prefixed with roles/ which doesn't fit the pattern for Custom Roles.

Sorry for the late reply.

@lesv I run this using google-cloud-storage version 1.12.0 and google-api-services-iam v1-rev220-1.23.0

@vchudnov-g I created the custom role from the Console as well as from the IAM Java API using something like this

public static Role createCustomRole(Iam iam, String roleTitle, String roleId, List<String> permission, String description, String projectName) throws IOException{
        Role role = new Role();
        role.setTitle(roleTitle);
        role.setDescription(description);
        role.setIncludedPermissions(permission);

        CreateRoleRequest req = new CreateRoleRequest();
        req.setRoleId(roleId);
        req.setRole(role);

        return iam.projects().roles().create("projects/"+projectName, req).execute();
    }

private static Role testCreateCustomRole(Iam iam) throws IOException{
        Role role = new Role();
        role.setTitle("testTitle");
        List<String> permissions = new ArrayList<>();
        permissions.add("storage.buckets.create");
        permissions.add("storage.buckets.delete");
        role.setDescription("testing create role 1");
        role.setIncludedPermissions(permissions);

        CreateRoleRequest req = new CreateRoleRequest();
        req.setRoleId("testID");
        req.setRole(role);

        return iam.projects().roles().create("projects/"+PROJECT_NAME, req).execute();
    }

Assigning the service account with pre-defined roles gave no error but with custom role gave error. I am not sure if what I did was right or wrong, especially with the naming I just gave simple name like CustomRole341.

@danawillow @Stono @frankyn So what do you think is the best approach to assign custom role using Java API? I don't think I could specify roles using the format projects/{project-id}/roles/{role-id} as it only accepts Role.of(the role ID or stuff)

UPDATES

So I tried to assign the custom role for Google IAM and it works as per suggested by @danawillow @Stono @frankyn something like this:

setRoleToServiceAccount(iam, PROJECT_NAME, "cfb-employee-ihsan", "projects/testing-123-ok/roles/AdministratorCfB" );
....
    public static Policy setRoleToServiceAccount(Iam iam, String projectName, String username, String role) throws IOException{
        ServiceAccount user = findServiceAccount(iam, username, projectName);
        List<String> member = new ArrayList<String>();
        member.add(Identity.serviceAccount(user.getEmail()).toString());

        Binding binding = new Binding();
        binding.setRole(role);
        binding.setMembers(member);

        List<Binding> bindings = new ArrayList<Binding>();
        bindings.add(binding);

        Policy policy = new Policy();
        policy.setBindings(bindings);

        SetIamPolicyRequest req = new SetIamPolicyRequest();
        req.setPolicy(policy);

        return iam.projects().serviceAccounts().setIamPolicy("projects/" + projectName + "/serviceAccounts/" + username +  "@" + projectName + ".iam.gserviceaccount.com" , req).execute();
    }

But for the Storage to assign custom role into the Bucket still gives me error of:

Exception in thread "main" com.google.cloud.storage.StorageException: Invalid argument
    at com.google.cloud.storage.spi.v1.HttpStorageRpc.translate(HttpStorageRpc.java:189)
    at com.google.cloud.storage.spi.v1.HttpStorageRpc.setIamPolicy(HttpStorageRpc.java:888)
    at com.google.cloud.storage.StorageImpl$36.call(StorageImpl.java:924)
    at com.google.cloud.storage.StorageImpl$36.call(StorageImpl.java:921)
    at com.google.api.gax.retrying.DirectRetryingExecutor.submit(DirectRetryingExecutor.java:89)
    at com.google.cloud.RetryHelper.run(RetryHelper.java:74)
    at com.google.cloud.RetryHelper.runWithRetries(RetryHelper.java:51)
    at com.google.cloud.storage.StorageImpl.setIamPolicy(StorageImpl.java:921)
    at gcp.GoogleStorage.setBucketRoleAndServiceAccount(GoogleStorage.java:84)
    at gcp.GoogleStorage.main(GoogleStorage.java:41)
Caused by: com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
  "code" : 400,
  "errors" : [ {
    "domain" : "global",
    "message" : "Invalid argument",
    "reason" : "invalid"
  } ],
  "message" : "Invalid argument"
}
    at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
    at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
    at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
    at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1065)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
    at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
    at com.google.cloud.storage.spi.v1.HttpStorageRpc.setIamPolicy(HttpStorageRpc.java:886)
    ... 8 more

with the code like:

setBucketRoleAndServiceAccount(a,  "testagainoioi",  "[email protected]", Role.of("projects/testing-123-ok/roles/DeveloperCfB"));
...
public static void setBucketRoleAndServiceAccount(Storage storage, String bucketName, String emailServiceAccount, Role role){
        Policy policy = storage.getIamPolicy(bucketName);

        Policy updatedPolicy = storage.setIamPolicy(bucketName,
                policy.toBuilder().addIdentity(role, Identity.serviceAccount(emailServiceAccount)).build());

        if (updatedPolicy.getBindings().get(role).contains(Identity.serviceAccount(emailServiceAccount))) {
            System.out.printf("Added %s with role %s to %s\n", Identity.serviceAccount(emailServiceAccount), role.getValue(), bucketName);
        }
        else{
            System.out.println("Something is wrong!");
        }
    }

but assigning pre-defined roles is fine like this:
setBucketRoleAndServiceAccount(a, "testagainoioi", "[email protected]", StorageRoles.admin());

Any idea how to fix assigning Role to Bucket?

@pongad could you update to this issue once your commit has been pushed to master? hopefully your commit could solve my issue

Just updating here too- I posted in terraform-providers/terraform-provider-google#993 that the reason this is happening is that the name of custom roles has to be in the form projects/{project-id}/roles/{role-id}- omitting the project will cause it to fail, just as it would fail if you typo-ed the name of the role.

Actually, I get this error and forward slash is an invalid character:

ERROR: (gcloud.iam.roles.create) INVALID_ARGUMENT: The role_id "projects/project/roles/my_role" is invalid. It doesn't match pattern "[a-zA-Z0-9_\.]{3,64}". The role_id must be 3 to 64 characters long and can be a mix of uppercase and lowercase English letters, digits, underscores and periods.
Was this page helpful?
0 / 5 - 0 ratings